aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/undici/lib
diff options
context:
space:
mode:
Diffstat (limited to 'vanilla/node_modules/undici/lib')
-rw-r--r--vanilla/node_modules/undici/lib/api/abort-signal.js59
-rw-r--r--vanilla/node_modules/undici/lib/api/api-connect.js110
-rw-r--r--vanilla/node_modules/undici/lib/api/api-pipeline.js252
-rw-r--r--vanilla/node_modules/undici/lib/api/api-request.js214
-rw-r--r--vanilla/node_modules/undici/lib/api/api-stream.js209
-rw-r--r--vanilla/node_modules/undici/lib/api/api-upgrade.js111
-rw-r--r--vanilla/node_modules/undici/lib/api/index.js7
-rw-r--r--vanilla/node_modules/undici/lib/api/readable.js580
-rw-r--r--vanilla/node_modules/undici/lib/cache/memory-cache-store.js234
-rw-r--r--vanilla/node_modules/undici/lib/cache/sqlite-cache-store.js461
-rw-r--r--vanilla/node_modules/undici/lib/core/connect.js137
-rw-r--r--vanilla/node_modules/undici/lib/core/constants.js143
-rw-r--r--vanilla/node_modules/undici/lib/core/diagnostics.js225
-rw-r--r--vanilla/node_modules/undici/lib/core/errors.js448
-rw-r--r--vanilla/node_modules/undici/lib/core/request.js412
-rw-r--r--vanilla/node_modules/undici/lib/core/symbols.js74
-rw-r--r--vanilla/node_modules/undici/lib/core/tree.js160
-rw-r--r--vanilla/node_modules/undici/lib/core/util.js957
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/agent.js158
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/balanced-pool.js216
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/client-h1.js1606
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/client-h2.js990
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/client.js647
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/dispatcher-base.js165
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/dispatcher.js48
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js146
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/fixed-queue.js135
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/h2c-client.js51
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/pool-base.js214
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/pool.js118
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/proxy-agent.js287
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/retry-agent.js35
-rw-r--r--vanilla/node_modules/undici/lib/dispatcher/round-robin-pool.js137
-rw-r--r--vanilla/node_modules/undici/lib/encoding/index.js33
-rw-r--r--vanilla/node_modules/undici/lib/global.js50
-rw-r--r--vanilla/node_modules/undici/lib/handler/cache-handler.js561
-rw-r--r--vanilla/node_modules/undici/lib/handler/cache-revalidation-handler.js124
-rw-r--r--vanilla/node_modules/undici/lib/handler/decorator-handler.js67
-rw-r--r--vanilla/node_modules/undici/lib/handler/deduplication-handler.js216
-rw-r--r--vanilla/node_modules/undici/lib/handler/redirect-handler.js237
-rw-r--r--vanilla/node_modules/undici/lib/handler/retry-handler.js394
-rw-r--r--vanilla/node_modules/undici/lib/handler/unwrap-handler.js96
-rw-r--r--vanilla/node_modules/undici/lib/handler/wrap-handler.js95
-rw-r--r--vanilla/node_modules/undici/lib/interceptor/cache.js495
-rw-r--r--vanilla/node_modules/undici/lib/interceptor/decompress.js259
-rw-r--r--vanilla/node_modules/undici/lib/interceptor/deduplicate.js107
-rw-r--r--vanilla/node_modules/undici/lib/interceptor/dns.js474
-rw-r--r--vanilla/node_modules/undici/lib/interceptor/dump.js112
-rw-r--r--vanilla/node_modules/undici/lib/interceptor/redirect.js21
-rw-r--r--vanilla/node_modules/undici/lib/interceptor/response-error.js95
-rw-r--r--vanilla/node_modules/undici/lib/interceptor/retry.js19
-rw-r--r--vanilla/node_modules/undici/lib/llhttp/.gitkeep0
-rw-r--r--vanilla/node_modules/undici/lib/llhttp/constants.d.ts195
-rw-r--r--vanilla/node_modules/undici/lib/llhttp/constants.js531
-rw-r--r--vanilla/node_modules/undici/lib/llhttp/llhttp-wasm.js15
-rw-r--r--vanilla/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js15
-rw-r--r--vanilla/node_modules/undici/lib/llhttp/utils.d.ts2
-rw-r--r--vanilla/node_modules/undici/lib/llhttp/utils.js12
-rw-r--r--vanilla/node_modules/undici/lib/mock/mock-agent.js232
-rw-r--r--vanilla/node_modules/undici/lib/mock/mock-call-history.js248
-rw-r--r--vanilla/node_modules/undici/lib/mock/mock-client.js68
-rw-r--r--vanilla/node_modules/undici/lib/mock/mock-errors.js29
-rw-r--r--vanilla/node_modules/undici/lib/mock/mock-interceptor.js209
-rw-r--r--vanilla/node_modules/undici/lib/mock/mock-pool.js68
-rw-r--r--vanilla/node_modules/undici/lib/mock/mock-symbols.js31
-rw-r--r--vanilla/node_modules/undici/lib/mock/mock-utils.js480
-rw-r--r--vanilla/node_modules/undici/lib/mock/pending-interceptors-formatter.js43
-rw-r--r--vanilla/node_modules/undici/lib/mock/snapshot-agent.js353
-rw-r--r--vanilla/node_modules/undici/lib/mock/snapshot-recorder.js588
-rw-r--r--vanilla/node_modules/undici/lib/mock/snapshot-utils.js158
-rw-r--r--vanilla/node_modules/undici/lib/util/cache.js405
-rw-r--r--vanilla/node_modules/undici/lib/util/date.js653
-rw-r--r--vanilla/node_modules/undici/lib/util/promise.js28
-rw-r--r--vanilla/node_modules/undici/lib/util/runtime-features.js124
-rw-r--r--vanilla/node_modules/undici/lib/util/stats.js32
-rw-r--r--vanilla/node_modules/undici/lib/util/timers.js425
-rw-r--r--vanilla/node_modules/undici/lib/web/cache/cache.js864
-rw-r--r--vanilla/node_modules/undici/lib/web/cache/cachestorage.js152
-rw-r--r--vanilla/node_modules/undici/lib/web/cache/util.js45
-rw-r--r--vanilla/node_modules/undici/lib/web/cookies/constants.js12
-rw-r--r--vanilla/node_modules/undici/lib/web/cookies/index.js199
-rw-r--r--vanilla/node_modules/undici/lib/web/cookies/parse.js322
-rw-r--r--vanilla/node_modules/undici/lib/web/cookies/util.js282
-rw-r--r--vanilla/node_modules/undici/lib/web/eventsource/eventsource-stream.js399
-rw-r--r--vanilla/node_modules/undici/lib/web/eventsource/eventsource.js501
-rw-r--r--vanilla/node_modules/undici/lib/web/eventsource/util.js29
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/LICENSE21
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/body.js509
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/constants.js131
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/data-url.js596
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/formdata-parser.js575
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/formdata.js259
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/global.js40
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/headers.js719
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/index.js2372
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/request.js1115
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/response.js641
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/util.js1520
-rw-r--r--vanilla/node_modules/undici/lib/web/infra/index.js229
-rw-r--r--vanilla/node_modules/undici/lib/web/subresource-integrity/Readme.md9
-rw-r--r--vanilla/node_modules/undici/lib/web/subresource-integrity/subresource-integrity.js307
-rw-r--r--vanilla/node_modules/undici/lib/web/webidl/index.js1003
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/connection.js329
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/constants.js126
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/events.js331
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/frame.js133
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/permessage-deflate.js70
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/receiver.js444
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/sender.js109
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/stream/websocketerror.js104
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/stream/websocketstream.js497
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/util.js339
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/websocket.js739
113 files changed, 0 insertions, 34217 deletions
diff --git a/vanilla/node_modules/undici/lib/api/abort-signal.js b/vanilla/node_modules/undici/lib/api/abort-signal.js
deleted file mode 100644
index 608170b..0000000
--- a/vanilla/node_modules/undici/lib/api/abort-signal.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict'
-
-const { addAbortListener } = require('../core/util')
-const { RequestAbortedError } = require('../core/errors')
-
-const kListener = Symbol('kListener')
-const kSignal = Symbol('kSignal')
-
-function abort (self) {
- if (self.abort) {
- self.abort(self[kSignal]?.reason)
- } else {
- self.reason = self[kSignal]?.reason ?? new RequestAbortedError()
- }
- removeSignal(self)
-}
-
-function addSignal (self, signal) {
- self.reason = null
-
- self[kSignal] = null
- self[kListener] = null
-
- if (!signal) {
- return
- }
-
- if (signal.aborted) {
- abort(self)
- return
- }
-
- self[kSignal] = signal
- self[kListener] = () => {
- abort(self)
- }
-
- addAbortListener(self[kSignal], self[kListener])
-}
-
-function removeSignal (self) {
- if (!self[kSignal]) {
- return
- }
-
- if ('removeEventListener' in self[kSignal]) {
- self[kSignal].removeEventListener('abort', self[kListener])
- } else {
- self[kSignal].removeListener('abort', self[kListener])
- }
-
- self[kSignal] = null
- self[kListener] = null
-}
-
-module.exports = {
- addSignal,
- removeSignal
-}
diff --git a/vanilla/node_modules/undici/lib/api/api-connect.js b/vanilla/node_modules/undici/lib/api/api-connect.js
deleted file mode 100644
index c8b86dd..0000000
--- a/vanilla/node_modules/undici/lib/api/api-connect.js
+++ /dev/null
@@ -1,110 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { AsyncResource } = require('node:async_hooks')
-const { InvalidArgumentError, SocketError } = require('../core/errors')
-const util = require('../core/util')
-const { addSignal, removeSignal } = require('./abort-signal')
-
-class ConnectHandler extends AsyncResource {
- constructor (opts, callback) {
- if (!opts || typeof opts !== 'object') {
- throw new InvalidArgumentError('invalid opts')
- }
-
- if (typeof callback !== 'function') {
- throw new InvalidArgumentError('invalid callback')
- }
-
- const { signal, opaque, responseHeaders } = opts
-
- if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
- throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
- }
-
- super('UNDICI_CONNECT')
-
- this.opaque = opaque || null
- this.responseHeaders = responseHeaders || null
- this.callback = callback
- this.abort = null
-
- addSignal(this, signal)
- }
-
- onConnect (abort, context) {
- if (this.reason) {
- abort(this.reason)
- return
- }
-
- assert(this.callback)
-
- this.abort = abort
- this.context = context
- }
-
- onHeaders () {
- throw new SocketError('bad connect', null)
- }
-
- onUpgrade (statusCode, rawHeaders, socket) {
- const { callback, opaque, context } = this
-
- removeSignal(this)
-
- this.callback = null
-
- let headers = rawHeaders
- // Indicates is an HTTP2Session
- if (headers != null) {
- headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
- }
-
- this.runInAsyncScope(callback, null, null, {
- statusCode,
- headers,
- socket,
- opaque,
- context
- })
- }
-
- onError (err) {
- const { callback, opaque } = this
-
- removeSignal(this)
-
- if (callback) {
- this.callback = null
- queueMicrotask(() => {
- this.runInAsyncScope(callback, null, err, { opaque })
- })
- }
- }
-}
-
-function connect (opts, callback) {
- if (callback === undefined) {
- return new Promise((resolve, reject) => {
- connect.call(this, opts, (err, data) => {
- return err ? reject(err) : resolve(data)
- })
- })
- }
-
- try {
- const connectHandler = new ConnectHandler(opts, callback)
- const connectOptions = { ...opts, method: 'CONNECT' }
-
- this.dispatch(connectOptions, connectHandler)
- } catch (err) {
- if (typeof callback !== 'function') {
- throw err
- }
- const opaque = opts?.opaque
- queueMicrotask(() => callback(err, { opaque }))
- }
-}
-
-module.exports = connect
diff --git a/vanilla/node_modules/undici/lib/api/api-pipeline.js b/vanilla/node_modules/undici/lib/api/api-pipeline.js
deleted file mode 100644
index 77f3520..0000000
--- a/vanilla/node_modules/undici/lib/api/api-pipeline.js
+++ /dev/null
@@ -1,252 +0,0 @@
-'use strict'
-
-const {
- Readable,
- Duplex,
- PassThrough
-} = require('node:stream')
-const assert = require('node:assert')
-const { AsyncResource } = require('node:async_hooks')
-const {
- InvalidArgumentError,
- InvalidReturnValueError,
- RequestAbortedError
-} = require('../core/errors')
-const util = require('../core/util')
-const { addSignal, removeSignal } = require('./abort-signal')
-
-function noop () {}
-
-const kResume = Symbol('resume')
-
-class PipelineRequest extends Readable {
- constructor () {
- super({ autoDestroy: true })
-
- this[kResume] = null
- }
-
- _read () {
- const { [kResume]: resume } = this
-
- if (resume) {
- this[kResume] = null
- resume()
- }
- }
-
- _destroy (err, callback) {
- this._read()
-
- callback(err)
- }
-}
-
-class PipelineResponse extends Readable {
- constructor (resume) {
- super({ autoDestroy: true })
- this[kResume] = resume
- }
-
- _read () {
- this[kResume]()
- }
-
- _destroy (err, callback) {
- if (!err && !this._readableState.endEmitted) {
- err = new RequestAbortedError()
- }
-
- callback(err)
- }
-}
-
-class PipelineHandler extends AsyncResource {
- constructor (opts, handler) {
- if (!opts || typeof opts !== 'object') {
- throw new InvalidArgumentError('invalid opts')
- }
-
- if (typeof handler !== 'function') {
- throw new InvalidArgumentError('invalid handler')
- }
-
- const { signal, method, opaque, onInfo, responseHeaders } = opts
-
- if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
- throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
- }
-
- if (method === 'CONNECT') {
- throw new InvalidArgumentError('invalid method')
- }
-
- if (onInfo && typeof onInfo !== 'function') {
- throw new InvalidArgumentError('invalid onInfo callback')
- }
-
- super('UNDICI_PIPELINE')
-
- this.opaque = opaque || null
- this.responseHeaders = responseHeaders || null
- this.handler = handler
- this.abort = null
- this.context = null
- this.onInfo = onInfo || null
-
- this.req = new PipelineRequest().on('error', noop)
-
- this.ret = new Duplex({
- readableObjectMode: opts.objectMode,
- autoDestroy: true,
- read: () => {
- const { body } = this
-
- if (body?.resume) {
- body.resume()
- }
- },
- write: (chunk, encoding, callback) => {
- const { req } = this
-
- if (req.push(chunk, encoding) || req._readableState.destroyed) {
- callback()
- } else {
- req[kResume] = callback
- }
- },
- destroy: (err, callback) => {
- const { body, req, res, ret, abort } = this
-
- if (!err && !ret._readableState.endEmitted) {
- err = new RequestAbortedError()
- }
-
- if (abort && err) {
- abort()
- }
-
- util.destroy(body, err)
- util.destroy(req, err)
- util.destroy(res, err)
-
- removeSignal(this)
-
- callback(err)
- }
- }).on('prefinish', () => {
- const { req } = this
-
- // Node < 15 does not call _final in same tick.
- req.push(null)
- })
-
- this.res = null
-
- addSignal(this, signal)
- }
-
- onConnect (abort, context) {
- const { res } = this
-
- if (this.reason) {
- abort(this.reason)
- return
- }
-
- assert(!res, 'pipeline cannot be retried')
-
- this.abort = abort
- this.context = context
- }
-
- onHeaders (statusCode, rawHeaders, resume) {
- const { opaque, handler, context } = this
-
- if (statusCode < 200) {
- if (this.onInfo) {
- const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
- this.onInfo({ statusCode, headers })
- }
- return
- }
-
- this.res = new PipelineResponse(resume)
-
- let body
- try {
- this.handler = null
- const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
- body = this.runInAsyncScope(handler, null, {
- statusCode,
- headers,
- opaque,
- body: this.res,
- context
- })
- } catch (err) {
- this.res.on('error', noop)
- throw err
- }
-
- if (!body || typeof body.on !== 'function') {
- throw new InvalidReturnValueError('expected Readable')
- }
-
- body
- .on('data', (chunk) => {
- const { ret, body } = this
-
- if (!ret.push(chunk) && body.pause) {
- body.pause()
- }
- })
- .on('error', (err) => {
- const { ret } = this
-
- util.destroy(ret, err)
- })
- .on('end', () => {
- const { ret } = this
-
- ret.push(null)
- })
- .on('close', () => {
- const { ret } = this
-
- if (!ret._readableState.ended) {
- util.destroy(ret, new RequestAbortedError())
- }
- })
-
- this.body = body
- }
-
- onData (chunk) {
- const { res } = this
- return res.push(chunk)
- }
-
- onComplete (trailers) {
- const { res } = this
- res.push(null)
- }
-
- onError (err) {
- const { ret } = this
- this.handler = null
- util.destroy(ret, err)
- }
-}
-
-function pipeline (opts, handler) {
- try {
- const pipelineHandler = new PipelineHandler(opts, handler)
- this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler)
- return pipelineHandler.ret
- } catch (err) {
- return new PassThrough().destroy(err)
- }
-}
-
-module.exports = pipeline
diff --git a/vanilla/node_modules/undici/lib/api/api-request.js b/vanilla/node_modules/undici/lib/api/api-request.js
deleted file mode 100644
index f6d15f7..0000000
--- a/vanilla/node_modules/undici/lib/api/api-request.js
+++ /dev/null
@@ -1,214 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { AsyncResource } = require('node:async_hooks')
-const { Readable } = require('./readable')
-const { InvalidArgumentError, RequestAbortedError } = require('../core/errors')
-const util = require('../core/util')
-
-function noop () {}
-
-class RequestHandler extends AsyncResource {
- constructor (opts, callback) {
- if (!opts || typeof opts !== 'object') {
- throw new InvalidArgumentError('invalid opts')
- }
-
- const { signal, method, opaque, body, onInfo, responseHeaders, highWaterMark } = opts
-
- try {
- if (typeof callback !== 'function') {
- throw new InvalidArgumentError('invalid callback')
- }
-
- if (highWaterMark && (typeof highWaterMark !== 'number' || highWaterMark < 0)) {
- throw new InvalidArgumentError('invalid highWaterMark')
- }
-
- if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
- throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
- }
-
- if (method === 'CONNECT') {
- throw new InvalidArgumentError('invalid method')
- }
-
- if (onInfo && typeof onInfo !== 'function') {
- throw new InvalidArgumentError('invalid onInfo callback')
- }
-
- super('UNDICI_REQUEST')
- } catch (err) {
- if (util.isStream(body)) {
- util.destroy(body.on('error', noop), err)
- }
- throw err
- }
-
- this.method = method
- this.responseHeaders = responseHeaders || null
- this.opaque = opaque || null
- this.callback = callback
- this.res = null
- this.abort = null
- this.body = body
- this.trailers = {}
- this.context = null
- this.onInfo = onInfo || null
- this.highWaterMark = highWaterMark
- this.reason = null
- this.removeAbortListener = null
-
- if (signal?.aborted) {
- this.reason = signal.reason ?? new RequestAbortedError()
- } else if (signal) {
- this.removeAbortListener = util.addAbortListener(signal, () => {
- this.reason = signal.reason ?? new RequestAbortedError()
- if (this.res) {
- util.destroy(this.res.on('error', noop), this.reason)
- } else if (this.abort) {
- this.abort(this.reason)
- }
- })
- }
- }
-
- onConnect (abort, context) {
- if (this.reason) {
- abort(this.reason)
- return
- }
-
- assert(this.callback)
-
- this.abort = abort
- this.context = context
- }
-
- onHeaders (statusCode, rawHeaders, resume, statusMessage) {
- const { callback, opaque, abort, context, responseHeaders, highWaterMark } = this
-
- const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
-
- if (statusCode < 200) {
- if (this.onInfo) {
- this.onInfo({ statusCode, headers })
- }
- return
- }
-
- const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers
- const contentType = parsedHeaders['content-type']
- const contentLength = parsedHeaders['content-length']
- const res = new Readable({
- resume,
- abort,
- contentType,
- contentLength: this.method !== 'HEAD' && contentLength
- ? Number(contentLength)
- : null,
- highWaterMark
- })
-
- if (this.removeAbortListener) {
- res.on('close', this.removeAbortListener)
- this.removeAbortListener = null
- }
-
- this.callback = null
- this.res = res
- if (callback !== null) {
- try {
- this.runInAsyncScope(callback, null, null, {
- statusCode,
- statusText: statusMessage,
- headers,
- trailers: this.trailers,
- opaque,
- body: res,
- context
- })
- } catch (err) {
- // If the callback throws synchronously, we need to handle it
- // Remove reference to res to allow res being garbage collected
- this.res = null
-
- // Destroy the response stream
- util.destroy(res.on('error', noop), err)
-
- // Use queueMicrotask to re-throw the error so it reaches uncaughtException
- queueMicrotask(() => {
- throw err
- })
- }
- }
- }
-
- onData (chunk) {
- return this.res.push(chunk)
- }
-
- onComplete (trailers) {
- util.parseHeaders(trailers, this.trailers)
- this.res.push(null)
- }
-
- onError (err) {
- const { res, callback, body, opaque } = this
-
- if (callback) {
- // TODO: Does this need queueMicrotask?
- this.callback = null
- queueMicrotask(() => {
- this.runInAsyncScope(callback, null, err, { opaque })
- })
- }
-
- if (res) {
- this.res = null
- // Ensure all queued handlers are invoked before destroying res.
- queueMicrotask(() => {
- util.destroy(res.on('error', noop), err)
- })
- }
-
- if (body) {
- this.body = null
-
- if (util.isStream(body)) {
- body.on('error', noop)
- util.destroy(body, err)
- }
- }
-
- if (this.removeAbortListener) {
- this.removeAbortListener()
- this.removeAbortListener = null
- }
- }
-}
-
-function request (opts, callback) {
- if (callback === undefined) {
- return new Promise((resolve, reject) => {
- request.call(this, opts, (err, data) => {
- return err ? reject(err) : resolve(data)
- })
- })
- }
-
- try {
- const handler = new RequestHandler(opts, callback)
-
- this.dispatch(opts, handler)
- } catch (err) {
- if (typeof callback !== 'function') {
- throw err
- }
- const opaque = opts?.opaque
- queueMicrotask(() => callback(err, { opaque }))
- }
-}
-
-module.exports = request
-module.exports.RequestHandler = RequestHandler
diff --git a/vanilla/node_modules/undici/lib/api/api-stream.js b/vanilla/node_modules/undici/lib/api/api-stream.js
deleted file mode 100644
index 5d0b3fb..0000000
--- a/vanilla/node_modules/undici/lib/api/api-stream.js
+++ /dev/null
@@ -1,209 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { finished } = require('node:stream')
-const { AsyncResource } = require('node:async_hooks')
-const { InvalidArgumentError, InvalidReturnValueError } = require('../core/errors')
-const util = require('../core/util')
-const { addSignal, removeSignal } = require('./abort-signal')
-
-function noop () {}
-
-class StreamHandler extends AsyncResource {
- constructor (opts, factory, callback) {
- if (!opts || typeof opts !== 'object') {
- throw new InvalidArgumentError('invalid opts')
- }
-
- const { signal, method, opaque, body, onInfo, responseHeaders } = opts
-
- try {
- if (typeof callback !== 'function') {
- throw new InvalidArgumentError('invalid callback')
- }
-
- if (typeof factory !== 'function') {
- throw new InvalidArgumentError('invalid factory')
- }
-
- if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
- throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
- }
-
- if (method === 'CONNECT') {
- throw new InvalidArgumentError('invalid method')
- }
-
- if (onInfo && typeof onInfo !== 'function') {
- throw new InvalidArgumentError('invalid onInfo callback')
- }
-
- super('UNDICI_STREAM')
- } catch (err) {
- if (util.isStream(body)) {
- util.destroy(body.on('error', noop), err)
- }
- throw err
- }
-
- this.responseHeaders = responseHeaders || null
- this.opaque = opaque || null
- this.factory = factory
- this.callback = callback
- this.res = null
- this.abort = null
- this.context = null
- this.trailers = null
- this.body = body
- this.onInfo = onInfo || null
-
- if (util.isStream(body)) {
- body.on('error', (err) => {
- this.onError(err)
- })
- }
-
- addSignal(this, signal)
- }
-
- onConnect (abort, context) {
- if (this.reason) {
- abort(this.reason)
- return
- }
-
- assert(this.callback)
-
- this.abort = abort
- this.context = context
- }
-
- onHeaders (statusCode, rawHeaders, resume, statusMessage) {
- const { factory, opaque, context, responseHeaders } = this
-
- const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
-
- if (statusCode < 200) {
- if (this.onInfo) {
- this.onInfo({ statusCode, headers })
- }
- return
- }
-
- this.factory = null
-
- if (factory === null) {
- return
- }
-
- const res = this.runInAsyncScope(factory, null, {
- statusCode,
- headers,
- opaque,
- context
- })
-
- if (
- !res ||
- typeof res.write !== 'function' ||
- typeof res.end !== 'function' ||
- typeof res.on !== 'function'
- ) {
- throw new InvalidReturnValueError('expected Writable')
- }
-
- // TODO: Avoid finished. It registers an unnecessary amount of listeners.
- finished(res, { readable: false }, (err) => {
- const { callback, res, opaque, trailers, abort } = this
-
- this.res = null
- if (err || !res?.readable) {
- util.destroy(res, err)
- }
-
- this.callback = null
- this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
-
- if (err) {
- abort()
- }
- })
-
- res.on('drain', resume)
-
- this.res = res
-
- const needDrain = res.writableNeedDrain !== undefined
- ? res.writableNeedDrain
- : res._writableState?.needDrain
-
- return needDrain !== true
- }
-
- onData (chunk) {
- const { res } = this
-
- return res ? res.write(chunk) : true
- }
-
- onComplete (trailers) {
- const { res } = this
-
- removeSignal(this)
-
- if (!res) {
- return
- }
-
- this.trailers = util.parseHeaders(trailers)
-
- res.end()
- }
-
- onError (err) {
- const { res, callback, opaque, body } = this
-
- removeSignal(this)
-
- this.factory = null
-
- if (res) {
- this.res = null
- util.destroy(res, err)
- } else if (callback) {
- this.callback = null
- queueMicrotask(() => {
- this.runInAsyncScope(callback, null, err, { opaque })
- })
- }
-
- if (body) {
- this.body = null
- util.destroy(body, err)
- }
- }
-}
-
-function stream (opts, factory, callback) {
- if (callback === undefined) {
- return new Promise((resolve, reject) => {
- stream.call(this, opts, factory, (err, data) => {
- return err ? reject(err) : resolve(data)
- })
- })
- }
-
- try {
- const handler = new StreamHandler(opts, factory, callback)
-
- this.dispatch(opts, handler)
- } catch (err) {
- if (typeof callback !== 'function') {
- throw err
- }
- const opaque = opts?.opaque
- queueMicrotask(() => callback(err, { opaque }))
- }
-}
-
-module.exports = stream
diff --git a/vanilla/node_modules/undici/lib/api/api-upgrade.js b/vanilla/node_modules/undici/lib/api/api-upgrade.js
deleted file mode 100644
index 2b03f20..0000000
--- a/vanilla/node_modules/undici/lib/api/api-upgrade.js
+++ /dev/null
@@ -1,111 +0,0 @@
-'use strict'
-
-const { InvalidArgumentError, SocketError } = require('../core/errors')
-const { AsyncResource } = require('node:async_hooks')
-const assert = require('node:assert')
-const util = require('../core/util')
-const { kHTTP2Stream } = require('../core/symbols')
-const { addSignal, removeSignal } = require('./abort-signal')
-
-class UpgradeHandler extends AsyncResource {
- constructor (opts, callback) {
- if (!opts || typeof opts !== 'object') {
- throw new InvalidArgumentError('invalid opts')
- }
-
- if (typeof callback !== 'function') {
- throw new InvalidArgumentError('invalid callback')
- }
-
- const { signal, opaque, responseHeaders } = opts
-
- if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
- throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
- }
-
- super('UNDICI_UPGRADE')
-
- this.responseHeaders = responseHeaders || null
- this.opaque = opaque || null
- this.callback = callback
- this.abort = null
- this.context = null
-
- addSignal(this, signal)
- }
-
- onConnect (abort, context) {
- if (this.reason) {
- abort(this.reason)
- return
- }
-
- assert(this.callback)
-
- this.abort = abort
- this.context = null
- }
-
- onHeaders () {
- throw new SocketError('bad upgrade', null)
- }
-
- onUpgrade (statusCode, rawHeaders, socket) {
- assert(socket[kHTTP2Stream] === true ? statusCode === 200 : statusCode === 101)
-
- const { callback, opaque, context } = this
-
- removeSignal(this)
-
- this.callback = null
- const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
- this.runInAsyncScope(callback, null, null, {
- headers,
- socket,
- opaque,
- context
- })
- }
-
- onError (err) {
- const { callback, opaque } = this
-
- removeSignal(this)
-
- if (callback) {
- this.callback = null
- queueMicrotask(() => {
- this.runInAsyncScope(callback, null, err, { opaque })
- })
- }
- }
-}
-
-function upgrade (opts, callback) {
- if (callback === undefined) {
- return new Promise((resolve, reject) => {
- upgrade.call(this, opts, (err, data) => {
- return err ? reject(err) : resolve(data)
- })
- })
- }
-
- try {
- const upgradeHandler = new UpgradeHandler(opts, callback)
- const upgradeOpts = {
- ...opts,
- method: opts.method || 'GET',
- upgrade: opts.protocol || 'Websocket'
- }
-
- this.dispatch(upgradeOpts, upgradeHandler)
- } catch (err) {
- if (typeof callback !== 'function') {
- throw err
- }
- const opaque = opts?.opaque
- queueMicrotask(() => callback(err, { opaque }))
- }
-}
-
-module.exports = upgrade
diff --git a/vanilla/node_modules/undici/lib/api/index.js b/vanilla/node_modules/undici/lib/api/index.js
deleted file mode 100644
index 8983a5e..0000000
--- a/vanilla/node_modules/undici/lib/api/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-'use strict'
-
-module.exports.request = require('./api-request')
-module.exports.stream = require('./api-stream')
-module.exports.pipeline = require('./api-pipeline')
-module.exports.upgrade = require('./api-upgrade')
-module.exports.connect = require('./api-connect')
diff --git a/vanilla/node_modules/undici/lib/api/readable.js b/vanilla/node_modules/undici/lib/api/readable.js
deleted file mode 100644
index cdede95..0000000
--- a/vanilla/node_modules/undici/lib/api/readable.js
+++ /dev/null
@@ -1,580 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { Readable } = require('node:stream')
-const { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = require('../core/errors')
-const util = require('../core/util')
-const { ReadableStreamFrom } = require('../core/util')
-
-const kConsume = Symbol('kConsume')
-const kReading = Symbol('kReading')
-const kBody = Symbol('kBody')
-const kAbort = Symbol('kAbort')
-const kContentType = Symbol('kContentType')
-const kContentLength = Symbol('kContentLength')
-const kUsed = Symbol('kUsed')
-const kBytesRead = Symbol('kBytesRead')
-
-const noop = () => {}
-
-/**
- * @class
- * @extends {Readable}
- * @see https://fetch.spec.whatwg.org/#body
- */
-class BodyReadable extends Readable {
- /**
- * @param {object} opts
- * @param {(this: Readable, size: number) => void} opts.resume
- * @param {() => (void | null)} opts.abort
- * @param {string} [opts.contentType = '']
- * @param {number} [opts.contentLength]
- * @param {number} [opts.highWaterMark = 64 * 1024]
- */
- constructor ({
- resume,
- abort,
- contentType = '',
- contentLength,
- highWaterMark = 64 * 1024 // Same as nodejs fs streams.
- }) {
- super({
- autoDestroy: true,
- read: resume,
- highWaterMark
- })
-
- this._readableState.dataEmitted = false
-
- this[kAbort] = abort
-
- /** @type {Consume | null} */
- this[kConsume] = null
-
- /** @type {number} */
- this[kBytesRead] = 0
-
- /** @type {ReadableStream|null} */
- this[kBody] = null
-
- /** @type {boolean} */
- this[kUsed] = false
-
- /** @type {string} */
- this[kContentType] = contentType
-
- /** @type {number|null} */
- this[kContentLength] = Number.isFinite(contentLength) ? contentLength : null
-
- /**
- * Is stream being consumed through Readable API?
- * This is an optimization so that we avoid checking
- * for 'data' and 'readable' listeners in the hot path
- * inside push().
- *
- * @type {boolean}
- */
- this[kReading] = false
- }
-
- /**
- * @param {Error|null} err
- * @param {(error:(Error|null)) => void} callback
- * @returns {void}
- */
- _destroy (err, callback) {
- if (!err && !this._readableState.endEmitted) {
- err = new RequestAbortedError()
- }
-
- if (err) {
- this[kAbort]()
- }
-
- // Workaround for Node "bug". If the stream is destroyed in same
- // tick as it is created, then a user who is waiting for a
- // promise (i.e micro tick) for installing an 'error' listener will
- // never get a chance and will always encounter an unhandled exception.
- if (!this[kUsed]) {
- setImmediate(callback, err)
- } else {
- callback(err)
- }
- }
-
- /**
- * @param {string|symbol} event
- * @param {(...args: any[]) => void} listener
- * @returns {this}
- */
- on (event, listener) {
- if (event === 'data' || event === 'readable') {
- this[kReading] = true
- this[kUsed] = true
- }
- return super.on(event, listener)
- }
-
- /**
- * @param {string|symbol} event
- * @param {(...args: any[]) => void} listener
- * @returns {this}
- */
- addListener (event, listener) {
- return this.on(event, listener)
- }
-
- /**
- * @param {string|symbol} event
- * @param {(...args: any[]) => void} listener
- * @returns {this}
- */
- off (event, listener) {
- const ret = super.off(event, listener)
- if (event === 'data' || event === 'readable') {
- this[kReading] = (
- this.listenerCount('data') > 0 ||
- this.listenerCount('readable') > 0
- )
- }
- return ret
- }
-
- /**
- * @param {string|symbol} event
- * @param {(...args: any[]) => void} listener
- * @returns {this}
- */
- removeListener (event, listener) {
- return this.off(event, listener)
- }
-
- /**
- * @param {Buffer|null} chunk
- * @returns {boolean}
- */
- push (chunk) {
- if (chunk) {
- this[kBytesRead] += chunk.length
- if (this[kConsume]) {
- consumePush(this[kConsume], chunk)
- return this[kReading] ? super.push(chunk) : true
- }
- }
-
- return super.push(chunk)
- }
-
- /**
- * Consumes and returns the body as a string.
- *
- * @see https://fetch.spec.whatwg.org/#dom-body-text
- * @returns {Promise<string>}
- */
- text () {
- return consume(this, 'text')
- }
-
- /**
- * Consumes and returns the body as a JavaScript Object.
- *
- * @see https://fetch.spec.whatwg.org/#dom-body-json
- * @returns {Promise<unknown>}
- */
- json () {
- return consume(this, 'json')
- }
-
- /**
- * Consumes and returns the body as a Blob
- *
- * @see https://fetch.spec.whatwg.org/#dom-body-blob
- * @returns {Promise<Blob>}
- */
- blob () {
- return consume(this, 'blob')
- }
-
- /**
- * Consumes and returns the body as an Uint8Array.
- *
- * @see https://fetch.spec.whatwg.org/#dom-body-bytes
- * @returns {Promise<Uint8Array>}
- */
- bytes () {
- return consume(this, 'bytes')
- }
-
- /**
- * Consumes and returns the body as an ArrayBuffer.
- *
- * @see https://fetch.spec.whatwg.org/#dom-body-arraybuffer
- * @returns {Promise<ArrayBuffer>}
- */
- arrayBuffer () {
- return consume(this, 'arrayBuffer')
- }
-
- /**
- * Not implemented
- *
- * @see https://fetch.spec.whatwg.org/#dom-body-formdata
- * @throws {NotSupportedError}
- */
- async formData () {
- // TODO: Implement.
- throw new NotSupportedError()
- }
-
- /**
- * Returns true if the body is not null and the body has been consumed.
- * Otherwise, returns false.
- *
- * @see https://fetch.spec.whatwg.org/#dom-body-bodyused
- * @readonly
- * @returns {boolean}
- */
- get bodyUsed () {
- return util.isDisturbed(this)
- }
-
- /**
- * @see https://fetch.spec.whatwg.org/#dom-body-body
- * @readonly
- * @returns {ReadableStream}
- */
- get body () {
- if (!this[kBody]) {
- this[kBody] = ReadableStreamFrom(this)
- if (this[kConsume]) {
- // TODO: Is this the best way to force a lock?
- this[kBody].getReader() // Ensure stream is locked.
- assert(this[kBody].locked)
- }
- }
- return this[kBody]
- }
-
- /**
- * Dumps the response body by reading `limit` number of bytes.
- * @param {object} opts
- * @param {number} [opts.limit = 131072] Number of bytes to read.
- * @param {AbortSignal} [opts.signal] An AbortSignal to cancel the dump.
- * @returns {Promise<null>}
- */
- dump (opts) {
- const signal = opts?.signal
-
- if (signal != null && (typeof signal !== 'object' || !('aborted' in signal))) {
- return Promise.reject(new InvalidArgumentError('signal must be an AbortSignal'))
- }
-
- const limit = opts?.limit && Number.isFinite(opts.limit)
- ? opts.limit
- : 128 * 1024
-
- if (signal?.aborted) {
- return Promise.reject(signal.reason ?? new AbortError())
- }
-
- if (this._readableState.closeEmitted) {
- return Promise.resolve(null)
- }
-
- return new Promise((resolve, reject) => {
- if (
- (this[kContentLength] && (this[kContentLength] > limit)) ||
- this[kBytesRead] > limit
- ) {
- this.destroy(new AbortError())
- }
-
- if (signal) {
- const onAbort = () => {
- this.destroy(signal.reason ?? new AbortError())
- }
- signal.addEventListener('abort', onAbort)
- this
- .on('close', function () {
- signal.removeEventListener('abort', onAbort)
- if (signal.aborted) {
- reject(signal.reason ?? new AbortError())
- } else {
- resolve(null)
- }
- })
- } else {
- this.on('close', resolve)
- }
-
- this
- .on('error', noop)
- .on('data', () => {
- if (this[kBytesRead] > limit) {
- this.destroy()
- }
- })
- .resume()
- })
- }
-
- /**
- * @param {BufferEncoding} encoding
- * @returns {this}
- */
- setEncoding (encoding) {
- if (Buffer.isEncoding(encoding)) {
- this._readableState.encoding = encoding
- }
- return this
- }
-}
-
-/**
- * @see https://streams.spec.whatwg.org/#readablestream-locked
- * @param {BodyReadable} bodyReadable
- * @returns {boolean}
- */
-function isLocked (bodyReadable) {
- // Consume is an implicit lock.
- return bodyReadable[kBody]?.locked === true || bodyReadable[kConsume] !== null
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#body-unusable
- * @param {BodyReadable} bodyReadable
- * @returns {boolean}
- */
-function isUnusable (bodyReadable) {
- return util.isDisturbed(bodyReadable) || isLocked(bodyReadable)
-}
-
-/**
- * @typedef {'text' | 'json' | 'blob' | 'bytes' | 'arrayBuffer'} ConsumeType
- */
-
-/**
- * @template {ConsumeType} T
- * @typedef {T extends 'text' ? string :
- * T extends 'json' ? unknown :
- * T extends 'blob' ? Blob :
- * T extends 'arrayBuffer' ? ArrayBuffer :
- * T extends 'bytes' ? Uint8Array :
- * never
- * } ConsumeReturnType
- */
-/**
- * @typedef {object} Consume
- * @property {ConsumeType} type
- * @property {BodyReadable} stream
- * @property {((value?: any) => void)} resolve
- * @property {((err: Error) => void)} reject
- * @property {number} length
- * @property {Buffer[]} body
- */
-
-/**
- * @template {ConsumeType} T
- * @param {BodyReadable} stream
- * @param {T} type
- * @returns {Promise<ConsumeReturnType<T>>}
- */
-function consume (stream, type) {
- assert(!stream[kConsume])
-
- return new Promise((resolve, reject) => {
- if (isUnusable(stream)) {
- const rState = stream._readableState
- if (rState.destroyed && rState.closeEmitted === false) {
- stream
- .on('error', reject)
- .on('close', () => {
- reject(new TypeError('unusable'))
- })
- } else {
- reject(rState.errored ?? new TypeError('unusable'))
- }
- } else {
- queueMicrotask(() => {
- stream[kConsume] = {
- type,
- stream,
- resolve,
- reject,
- length: 0,
- body: []
- }
-
- stream
- .on('error', function (err) {
- consumeFinish(this[kConsume], err)
- })
- .on('close', function () {
- if (this[kConsume].body !== null) {
- consumeFinish(this[kConsume], new RequestAbortedError())
- }
- })
-
- consumeStart(stream[kConsume])
- })
- }
- })
-}
-
-/**
- * @param {Consume} consume
- * @returns {void}
- */
-function consumeStart (consume) {
- if (consume.body === null) {
- return
- }
-
- const { _readableState: state } = consume.stream
-
- if (state.bufferIndex) {
- const start = state.bufferIndex
- const end = state.buffer.length
- for (let n = start; n < end; n++) {
- consumePush(consume, state.buffer[n])
- }
- } else {
- for (const chunk of state.buffer) {
- consumePush(consume, chunk)
- }
- }
-
- if (state.endEmitted) {
- consumeEnd(this[kConsume], this._readableState.encoding)
- } else {
- consume.stream.on('end', function () {
- consumeEnd(this[kConsume], this._readableState.encoding)
- })
- }
-
- consume.stream.resume()
-
- while (consume.stream.read() != null) {
- // Loop
- }
-}
-
-/**
- * @param {Buffer[]} chunks
- * @param {number} length
- * @param {BufferEncoding} [encoding='utf8']
- * @returns {string}
- */
-function chunksDecode (chunks, length, encoding) {
- if (chunks.length === 0 || length === 0) {
- return ''
- }
- const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks, length)
- const bufferLength = buffer.length
-
- // Skip BOM.
- const start =
- bufferLength > 2 &&
- buffer[0] === 0xef &&
- buffer[1] === 0xbb &&
- buffer[2] === 0xbf
- ? 3
- : 0
- if (!encoding || encoding === 'utf8' || encoding === 'utf-8') {
- return buffer.utf8Slice(start, bufferLength)
- } else {
- return buffer.subarray(start, bufferLength).toString(encoding)
- }
-}
-
-/**
- * @param {Buffer[]} chunks
- * @param {number} length
- * @returns {Uint8Array}
- */
-function chunksConcat (chunks, length) {
- if (chunks.length === 0 || length === 0) {
- return new Uint8Array(0)
- }
- if (chunks.length === 1) {
- // fast-path
- return new Uint8Array(chunks[0])
- }
- const buffer = new Uint8Array(Buffer.allocUnsafeSlow(length).buffer)
-
- let offset = 0
- for (let i = 0; i < chunks.length; ++i) {
- const chunk = chunks[i]
- buffer.set(chunk, offset)
- offset += chunk.length
- }
-
- return buffer
-}
-
-/**
- * @param {Consume} consume
- * @param {BufferEncoding} encoding
- * @returns {void}
- */
-function consumeEnd (consume, encoding) {
- const { type, body, resolve, stream, length } = consume
-
- try {
- if (type === 'text') {
- resolve(chunksDecode(body, length, encoding))
- } else if (type === 'json') {
- resolve(JSON.parse(chunksDecode(body, length, encoding)))
- } else if (type === 'arrayBuffer') {
- resolve(chunksConcat(body, length).buffer)
- } else if (type === 'blob') {
- resolve(new Blob(body, { type: stream[kContentType] }))
- } else if (type === 'bytes') {
- resolve(chunksConcat(body, length))
- }
-
- consumeFinish(consume)
- } catch (err) {
- stream.destroy(err)
- }
-}
-
-/**
- * @param {Consume} consume
- * @param {Buffer} chunk
- * @returns {void}
- */
-function consumePush (consume, chunk) {
- consume.length += chunk.length
- consume.body.push(chunk)
-}
-
-/**
- * @param {Consume} consume
- * @param {Error} [err]
- * @returns {void}
- */
-function consumeFinish (consume, err) {
- if (consume.body === null) {
- return
- }
-
- if (err) {
- consume.reject(err)
- } else {
- consume.resolve()
- }
-
- // Reset the consume object to allow for garbage collection.
- consume.type = null
- consume.stream = null
- consume.resolve = null
- consume.reject = null
- consume.length = 0
- consume.body = null
-}
-
-module.exports = {
- Readable: BodyReadable,
- chunksDecode
-}
diff --git a/vanilla/node_modules/undici/lib/cache/memory-cache-store.js b/vanilla/node_modules/undici/lib/cache/memory-cache-store.js
deleted file mode 100644
index dba29ae..0000000
--- a/vanilla/node_modules/undici/lib/cache/memory-cache-store.js
+++ /dev/null
@@ -1,234 +0,0 @@
-'use strict'
-
-const { Writable } = require('node:stream')
-const { EventEmitter } = require('node:events')
-const { assertCacheKey, assertCacheValue } = require('../util/cache.js')
-
-/**
- * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheKey} CacheKey
- * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheValue} CacheValue
- * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore
- * @typedef {import('../../types/cache-interceptor.d.ts').default.GetResult} GetResult
- */
-
-/**
- * @implements {CacheStore}
- * @extends {EventEmitter}
- */
-class MemoryCacheStore extends EventEmitter {
- #maxCount = 1024
- #maxSize = 104857600 // 100MB
- #maxEntrySize = 5242880 // 5MB
-
- #size = 0
- #count = 0
- #entries = new Map()
- #hasEmittedMaxSizeEvent = false
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.MemoryCacheStoreOpts | undefined} [opts]
- */
- constructor (opts) {
- super()
- if (opts) {
- if (typeof opts !== 'object') {
- throw new TypeError('MemoryCacheStore options must be an object')
- }
-
- if (opts.maxCount !== undefined) {
- if (
- typeof opts.maxCount !== 'number' ||
- !Number.isInteger(opts.maxCount) ||
- opts.maxCount < 0
- ) {
- throw new TypeError('MemoryCacheStore options.maxCount must be a non-negative integer')
- }
- this.#maxCount = opts.maxCount
- }
-
- if (opts.maxSize !== undefined) {
- if (
- typeof opts.maxSize !== 'number' ||
- !Number.isInteger(opts.maxSize) ||
- opts.maxSize < 0
- ) {
- throw new TypeError('MemoryCacheStore options.maxSize must be a non-negative integer')
- }
- this.#maxSize = opts.maxSize
- }
-
- if (opts.maxEntrySize !== undefined) {
- if (
- typeof opts.maxEntrySize !== 'number' ||
- !Number.isInteger(opts.maxEntrySize) ||
- opts.maxEntrySize < 0
- ) {
- throw new TypeError('MemoryCacheStore options.maxEntrySize must be a non-negative integer')
- }
- this.#maxEntrySize = opts.maxEntrySize
- }
- }
- }
-
- /**
- * Get the current size of the cache in bytes
- * @returns {number} The current size of the cache in bytes
- */
- get size () {
- return this.#size
- }
-
- /**
- * Check if the cache is full (either max size or max count reached)
- * @returns {boolean} True if the cache is full, false otherwise
- */
- isFull () {
- return this.#size >= this.#maxSize || this.#count >= this.#maxCount
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} req
- * @returns {import('../../types/cache-interceptor.d.ts').default.GetResult | undefined}
- */
- get (key) {
- assertCacheKey(key)
-
- const topLevelKey = `${key.origin}:${key.path}`
-
- const now = Date.now()
- const entries = this.#entries.get(topLevelKey)
-
- const entry = entries ? findEntry(key, entries, now) : null
-
- return entry == null
- ? undefined
- : {
- statusMessage: entry.statusMessage,
- statusCode: entry.statusCode,
- headers: entry.headers,
- body: entry.body,
- vary: entry.vary ? entry.vary : undefined,
- etag: entry.etag,
- cacheControlDirectives: entry.cacheControlDirectives,
- cachedAt: entry.cachedAt,
- staleAt: entry.staleAt,
- deleteAt: entry.deleteAt
- }
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheValue} val
- * @returns {Writable | undefined}
- */
- createWriteStream (key, val) {
- assertCacheKey(key)
- assertCacheValue(val)
-
- const topLevelKey = `${key.origin}:${key.path}`
-
- const store = this
- const entry = { ...key, ...val, body: [], size: 0 }
-
- return new Writable({
- write (chunk, encoding, callback) {
- if (typeof chunk === 'string') {
- chunk = Buffer.from(chunk, encoding)
- }
-
- entry.size += chunk.byteLength
-
- if (entry.size >= store.#maxEntrySize) {
- this.destroy()
- } else {
- entry.body.push(chunk)
- }
-
- callback(null)
- },
- final (callback) {
- let entries = store.#entries.get(topLevelKey)
- if (!entries) {
- entries = []
- store.#entries.set(topLevelKey, entries)
- }
- const previousEntry = findEntry(key, entries, Date.now())
- if (previousEntry) {
- const index = entries.indexOf(previousEntry)
- entries.splice(index, 1, entry)
- store.#size -= previousEntry.size
- } else {
- entries.push(entry)
- store.#count += 1
- }
-
- store.#size += entry.size
-
- // Check if cache is full and emit event if needed
- if (store.#size > store.#maxSize || store.#count > store.#maxCount) {
- // Emit maxSizeExceeded event if we haven't already
- if (!store.#hasEmittedMaxSizeEvent) {
- store.emit('maxSizeExceeded', {
- size: store.#size,
- maxSize: store.#maxSize,
- count: store.#count,
- maxCount: store.#maxCount
- })
- store.#hasEmittedMaxSizeEvent = true
- }
-
- // Perform eviction
- for (const [key, entries] of store.#entries) {
- for (const entry of entries.splice(0, entries.length / 2)) {
- store.#size -= entry.size
- store.#count -= 1
- }
- if (entries.length === 0) {
- store.#entries.delete(key)
- }
- }
-
- // Reset the event flag after eviction
- if (store.#size < store.#maxSize && store.#count < store.#maxCount) {
- store.#hasEmittedMaxSizeEvent = false
- }
- }
-
- callback(null)
- }
- })
- }
-
- /**
- * @param {CacheKey} key
- */
- delete (key) {
- if (typeof key !== 'object') {
- throw new TypeError(`expected key to be object, got ${typeof key}`)
- }
-
- const topLevelKey = `${key.origin}:${key.path}`
-
- for (const entry of this.#entries.get(topLevelKey) ?? []) {
- this.#size -= entry.size
- this.#count -= 1
- }
- this.#entries.delete(topLevelKey)
- }
-}
-
-function findEntry (key, entries, now) {
- return entries.find((entry) => (
- entry.deleteAt > now &&
- entry.method === key.method &&
- (entry.vary == null || Object.keys(entry.vary).every(headerName => {
- if (entry.vary[headerName] === null) {
- return key.headers[headerName] === undefined
- }
-
- return entry.vary[headerName] === key.headers[headerName]
- }))
- ))
-}
-
-module.exports = MemoryCacheStore
diff --git a/vanilla/node_modules/undici/lib/cache/sqlite-cache-store.js b/vanilla/node_modules/undici/lib/cache/sqlite-cache-store.js
deleted file mode 100644
index 7cb4aa7..0000000
--- a/vanilla/node_modules/undici/lib/cache/sqlite-cache-store.js
+++ /dev/null
@@ -1,461 +0,0 @@
-'use strict'
-
-const { Writable } = require('node:stream')
-const { assertCacheKey, assertCacheValue } = require('../util/cache.js')
-
-let DatabaseSync
-
-const VERSION = 3
-
-// 2gb
-const MAX_ENTRY_SIZE = 2 * 1000 * 1000 * 1000
-
-/**
- * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore
- * @implements {CacheStore}
- *
- * @typedef {{
- * id: Readonly<number>,
- * body?: Uint8Array
- * statusCode: number
- * statusMessage: string
- * headers?: string
- * vary?: string
- * etag?: string
- * cacheControlDirectives?: string
- * cachedAt: number
- * staleAt: number
- * deleteAt: number
- * }} SqliteStoreValue
- */
-module.exports = class SqliteCacheStore {
- #maxEntrySize = MAX_ENTRY_SIZE
- #maxCount = Infinity
-
- /**
- * @type {import('node:sqlite').DatabaseSync}
- */
- #db
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #getValuesQuery
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #updateValueQuery
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #insertValueQuery
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #deleteExpiredValuesQuery
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #deleteByUrlQuery
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #countEntriesQuery
-
- /**
- * @type {import('node:sqlite').StatementSync | null}
- */
- #deleteOldValuesQuery
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.SqliteCacheStoreOpts | undefined} opts
- */
- constructor (opts) {
- if (opts) {
- if (typeof opts !== 'object') {
- throw new TypeError('SqliteCacheStore options must be an object')
- }
-
- if (opts.maxEntrySize !== undefined) {
- if (
- typeof opts.maxEntrySize !== 'number' ||
- !Number.isInteger(opts.maxEntrySize) ||
- opts.maxEntrySize < 0
- ) {
- throw new TypeError('SqliteCacheStore options.maxEntrySize must be a non-negative integer')
- }
-
- if (opts.maxEntrySize > MAX_ENTRY_SIZE) {
- throw new TypeError('SqliteCacheStore options.maxEntrySize must be less than 2gb')
- }
-
- this.#maxEntrySize = opts.maxEntrySize
- }
-
- if (opts.maxCount !== undefined) {
- if (
- typeof opts.maxCount !== 'number' ||
- !Number.isInteger(opts.maxCount) ||
- opts.maxCount < 0
- ) {
- throw new TypeError('SqliteCacheStore options.maxCount must be a non-negative integer')
- }
- this.#maxCount = opts.maxCount
- }
- }
-
- if (!DatabaseSync) {
- DatabaseSync = require('node:sqlite').DatabaseSync
- }
- this.#db = new DatabaseSync(opts?.location ?? ':memory:')
-
- this.#db.exec(`
- PRAGMA journal_mode = WAL;
- PRAGMA synchronous = NORMAL;
- PRAGMA temp_store = memory;
- PRAGMA optimize;
-
- CREATE TABLE IF NOT EXISTS cacheInterceptorV${VERSION} (
- -- Data specific to us
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- url TEXT NOT NULL,
- method TEXT NOT NULL,
-
- -- Data returned to the interceptor
- body BUF NULL,
- deleteAt INTEGER NOT NULL,
- statusCode INTEGER NOT NULL,
- statusMessage TEXT NOT NULL,
- headers TEXT NULL,
- cacheControlDirectives TEXT NULL,
- etag TEXT NULL,
- vary TEXT NULL,
- cachedAt INTEGER NOT NULL,
- staleAt INTEGER NOT NULL
- );
-
- CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_getValuesQuery ON cacheInterceptorV${VERSION}(url, method, deleteAt);
- CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_deleteByUrlQuery ON cacheInterceptorV${VERSION}(deleteAt);
- `)
-
- this.#getValuesQuery = this.#db.prepare(`
- SELECT
- id,
- body,
- deleteAt,
- statusCode,
- statusMessage,
- headers,
- etag,
- cacheControlDirectives,
- vary,
- cachedAt,
- staleAt
- FROM cacheInterceptorV${VERSION}
- WHERE
- url = ?
- AND method = ?
- ORDER BY
- deleteAt ASC
- `)
-
- this.#updateValueQuery = this.#db.prepare(`
- UPDATE cacheInterceptorV${VERSION} SET
- body = ?,
- deleteAt = ?,
- statusCode = ?,
- statusMessage = ?,
- headers = ?,
- etag = ?,
- cacheControlDirectives = ?,
- cachedAt = ?,
- staleAt = ?
- WHERE
- id = ?
- `)
-
- this.#insertValueQuery = this.#db.prepare(`
- INSERT INTO cacheInterceptorV${VERSION} (
- url,
- method,
- body,
- deleteAt,
- statusCode,
- statusMessage,
- headers,
- etag,
- cacheControlDirectives,
- vary,
- cachedAt,
- staleAt
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- `)
-
- this.#deleteByUrlQuery = this.#db.prepare(
- `DELETE FROM cacheInterceptorV${VERSION} WHERE url = ?`
- )
-
- this.#countEntriesQuery = this.#db.prepare(
- `SELECT COUNT(*) AS total FROM cacheInterceptorV${VERSION}`
- )
-
- this.#deleteExpiredValuesQuery = this.#db.prepare(
- `DELETE FROM cacheInterceptorV${VERSION} WHERE deleteAt <= ?`
- )
-
- this.#deleteOldValuesQuery = this.#maxCount === Infinity
- ? null
- : this.#db.prepare(`
- DELETE FROM cacheInterceptorV${VERSION}
- WHERE id IN (
- SELECT
- id
- FROM cacheInterceptorV${VERSION}
- ORDER BY cachedAt DESC
- LIMIT ?
- )
- `)
- }
-
- close () {
- this.#db.close()
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @returns {(import('../../types/cache-interceptor.d.ts').default.GetResult & { body?: Buffer }) | undefined}
- */
- get (key) {
- assertCacheKey(key)
-
- const value = this.#findValue(key)
- return value
- ? {
- body: value.body ? Buffer.from(value.body.buffer, value.body.byteOffset, value.body.byteLength) : undefined,
- statusCode: value.statusCode,
- statusMessage: value.statusMessage,
- headers: value.headers ? JSON.parse(value.headers) : undefined,
- etag: value.etag ? value.etag : undefined,
- vary: value.vary ? JSON.parse(value.vary) : undefined,
- cacheControlDirectives: value.cacheControlDirectives
- ? JSON.parse(value.cacheControlDirectives)
- : undefined,
- cachedAt: value.cachedAt,
- staleAt: value.staleAt,
- deleteAt: value.deleteAt
- }
- : undefined
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheValue & { body: null | Buffer | Array<Buffer>}} value
- */
- set (key, value) {
- assertCacheKey(key)
-
- const url = this.#makeValueUrl(key)
- const body = Array.isArray(value.body) ? Buffer.concat(value.body) : value.body
- const size = body?.byteLength
-
- if (size && size > this.#maxEntrySize) {
- return
- }
-
- const existingValue = this.#findValue(key, true)
- if (existingValue) {
- // Updating an existing response, let's overwrite it
- this.#updateValueQuery.run(
- body,
- value.deleteAt,
- value.statusCode,
- value.statusMessage,
- value.headers ? JSON.stringify(value.headers) : null,
- value.etag ? value.etag : null,
- value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
- value.cachedAt,
- value.staleAt,
- existingValue.id
- )
- } else {
- this.#prune()
- // New response, let's insert it
- this.#insertValueQuery.run(
- url,
- key.method,
- body,
- value.deleteAt,
- value.statusCode,
- value.statusMessage,
- value.headers ? JSON.stringify(value.headers) : null,
- value.etag ? value.etag : null,
- value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
- value.vary ? JSON.stringify(value.vary) : null,
- value.cachedAt,
- value.staleAt
- )
- }
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheValue} value
- * @returns {Writable | undefined}
- */
- createWriteStream (key, value) {
- assertCacheKey(key)
- assertCacheValue(value)
-
- let size = 0
- /**
- * @type {Buffer[] | null}
- */
- const body = []
- const store = this
-
- return new Writable({
- decodeStrings: true,
- write (chunk, encoding, callback) {
- size += chunk.byteLength
-
- if (size < store.#maxEntrySize) {
- body.push(chunk)
- } else {
- this.destroy()
- }
-
- callback()
- },
- final (callback) {
- store.set(key, { ...value, body })
- callback()
- }
- })
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- */
- delete (key) {
- if (typeof key !== 'object') {
- throw new TypeError(`expected key to be object, got ${typeof key}`)
- }
-
- this.#deleteByUrlQuery.run(this.#makeValueUrl(key))
- }
-
- #prune () {
- if (Number.isFinite(this.#maxCount) && this.size <= this.#maxCount) {
- return 0
- }
-
- {
- const removed = this.#deleteExpiredValuesQuery.run(Date.now()).changes
- if (removed) {
- return removed
- }
- }
-
- {
- const removed = this.#deleteOldValuesQuery?.run(Math.max(Math.floor(this.#maxCount * 0.1), 1)).changes
- if (removed) {
- return removed
- }
- }
-
- return 0
- }
-
- /**
- * Counts the number of rows in the cache
- * @returns {Number}
- */
- get size () {
- const { total } = this.#countEntriesQuery.get()
- return total
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @returns {string}
- */
- #makeValueUrl (key) {
- return `${key.origin}/${key.path}`
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @param {boolean} [canBeExpired=false]
- * @returns {SqliteStoreValue | undefined}
- */
- #findValue (key, canBeExpired = false) {
- const url = this.#makeValueUrl(key)
- const { headers, method } = key
-
- /**
- * @type {SqliteStoreValue[]}
- */
- const values = this.#getValuesQuery.all(url, method)
-
- if (values.length === 0) {
- return undefined
- }
-
- const now = Date.now()
- for (const value of values) {
- if (now >= value.deleteAt && !canBeExpired) {
- return undefined
- }
-
- let matches = true
-
- if (value.vary) {
- const vary = JSON.parse(value.vary)
-
- for (const header in vary) {
- if (!headerValueEquals(headers[header], vary[header])) {
- matches = false
- break
- }
- }
- }
-
- if (matches) {
- return value
- }
- }
-
- return undefined
- }
-}
-
-/**
- * @param {string|string[]|null|undefined} lhs
- * @param {string|string[]|null|undefined} rhs
- * @returns {boolean}
- */
-function headerValueEquals (lhs, rhs) {
- if (lhs == null && rhs == null) {
- return true
- }
-
- if ((lhs == null && rhs != null) ||
- (lhs != null && rhs == null)) {
- return false
- }
-
- if (Array.isArray(lhs) && Array.isArray(rhs)) {
- if (lhs.length !== rhs.length) {
- return false
- }
-
- return lhs.every((x, i) => x === rhs[i])
- }
-
- return lhs === rhs
-}
diff --git a/vanilla/node_modules/undici/lib/core/connect.js b/vanilla/node_modules/undici/lib/core/connect.js
deleted file mode 100644
index a49af91..0000000
--- a/vanilla/node_modules/undici/lib/core/connect.js
+++ /dev/null
@@ -1,137 +0,0 @@
-'use strict'
-
-const net = require('node:net')
-const assert = require('node:assert')
-const util = require('./util')
-const { InvalidArgumentError } = require('./errors')
-
-let tls // include tls conditionally since it is not always available
-
-// TODO: session re-use does not wait for the first
-// connection to resolve the session and might therefore
-// resolve the same servername multiple times even when
-// re-use is enabled.
-
-const SessionCache = class WeakSessionCache {
- constructor (maxCachedSessions) {
- this._maxCachedSessions = maxCachedSessions
- this._sessionCache = new Map()
- this._sessionRegistry = new FinalizationRegistry((key) => {
- if (this._sessionCache.size < this._maxCachedSessions) {
- return
- }
-
- const ref = this._sessionCache.get(key)
- if (ref !== undefined && ref.deref() === undefined) {
- this._sessionCache.delete(key)
- }
- })
- }
-
- get (sessionKey) {
- const ref = this._sessionCache.get(sessionKey)
- return ref ? ref.deref() : null
- }
-
- set (sessionKey, session) {
- if (this._maxCachedSessions === 0) {
- return
- }
-
- this._sessionCache.set(sessionKey, new WeakRef(session))
- this._sessionRegistry.register(session, sessionKey)
- }
-}
-
-function buildConnector ({ allowH2, useH2c, maxCachedSessions, socketPath, timeout, session: customSession, ...opts }) {
- if (maxCachedSessions != null && (!Number.isInteger(maxCachedSessions) || maxCachedSessions < 0)) {
- throw new InvalidArgumentError('maxCachedSessions must be a positive integer or zero')
- }
-
- const options = { path: socketPath, ...opts }
- const sessionCache = new SessionCache(maxCachedSessions == null ? 100 : maxCachedSessions)
- timeout = timeout == null ? 10e3 : timeout
- allowH2 = allowH2 != null ? allowH2 : false
- return function connect ({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) {
- let socket
- if (protocol === 'https:') {
- if (!tls) {
- tls = require('node:tls')
- }
- servername = servername || options.servername || util.getServerName(host) || null
-
- const sessionKey = servername || hostname
- assert(sessionKey)
-
- const session = customSession || sessionCache.get(sessionKey) || null
-
- port = port || 443
-
- socket = tls.connect({
- highWaterMark: 16384, // TLS in node can't have bigger HWM anyway...
- ...options,
- servername,
- session,
- localAddress,
- ALPNProtocols: allowH2 ? ['http/1.1', 'h2'] : ['http/1.1'],
- socket: httpSocket, // upgrade socket connection
- port,
- host: hostname
- })
-
- socket
- .on('session', function (session) {
- // TODO (fix): Can a session become invalid once established? Don't think so?
- sessionCache.set(sessionKey, session)
- })
- } else {
- assert(!httpSocket, 'httpSocket can only be sent on TLS update')
-
- port = port || 80
-
- socket = net.connect({
- highWaterMark: 64 * 1024, // Same as nodejs fs streams.
- ...options,
- localAddress,
- port,
- host: hostname
- })
- if (useH2c === true) {
- socket.alpnProtocol = 'h2'
- }
- }
-
- // Set TCP keep alive options on the socket here instead of in connect() for the case of assigning the socket
- if (options.keepAlive == null || options.keepAlive) {
- const keepAliveInitialDelay = options.keepAliveInitialDelay === undefined ? 60e3 : options.keepAliveInitialDelay
- socket.setKeepAlive(true, keepAliveInitialDelay)
- }
-
- const clearConnectTimeout = util.setupConnectTimeout(new WeakRef(socket), { timeout, hostname, port })
-
- socket
- .setNoDelay(true)
- .once(protocol === 'https:' ? 'secureConnect' : 'connect', function () {
- queueMicrotask(clearConnectTimeout)
-
- if (callback) {
- const cb = callback
- callback = null
- cb(null, this)
- }
- })
- .on('error', function (err) {
- queueMicrotask(clearConnectTimeout)
-
- if (callback) {
- const cb = callback
- callback = null
- cb(err)
- }
- })
-
- return socket
- }
-}
-
-module.exports = buildConnector
diff --git a/vanilla/node_modules/undici/lib/core/constants.js b/vanilla/node_modules/undici/lib/core/constants.js
deleted file mode 100644
index 088cf47..0000000
--- a/vanilla/node_modules/undici/lib/core/constants.js
+++ /dev/null
@@ -1,143 +0,0 @@
-'use strict'
-
-/**
- * @see https://developer.mozilla.org/docs/Web/HTTP/Headers
- */
-const wellknownHeaderNames = /** @type {const} */ ([
- 'Accept',
- 'Accept-Encoding',
- 'Accept-Language',
- 'Accept-Ranges',
- 'Access-Control-Allow-Credentials',
- 'Access-Control-Allow-Headers',
- 'Access-Control-Allow-Methods',
- 'Access-Control-Allow-Origin',
- 'Access-Control-Expose-Headers',
- 'Access-Control-Max-Age',
- 'Access-Control-Request-Headers',
- 'Access-Control-Request-Method',
- 'Age',
- 'Allow',
- 'Alt-Svc',
- 'Alt-Used',
- 'Authorization',
- 'Cache-Control',
- 'Clear-Site-Data',
- 'Connection',
- 'Content-Disposition',
- 'Content-Encoding',
- 'Content-Language',
- 'Content-Length',
- 'Content-Location',
- 'Content-Range',
- 'Content-Security-Policy',
- 'Content-Security-Policy-Report-Only',
- 'Content-Type',
- 'Cookie',
- 'Cross-Origin-Embedder-Policy',
- 'Cross-Origin-Opener-Policy',
- 'Cross-Origin-Resource-Policy',
- 'Date',
- 'Device-Memory',
- 'Downlink',
- 'ECT',
- 'ETag',
- 'Expect',
- 'Expect-CT',
- 'Expires',
- 'Forwarded',
- 'From',
- 'Host',
- 'If-Match',
- 'If-Modified-Since',
- 'If-None-Match',
- 'If-Range',
- 'If-Unmodified-Since',
- 'Keep-Alive',
- 'Last-Modified',
- 'Link',
- 'Location',
- 'Max-Forwards',
- 'Origin',
- 'Permissions-Policy',
- 'Pragma',
- 'Proxy-Authenticate',
- 'Proxy-Authorization',
- 'RTT',
- 'Range',
- 'Referer',
- 'Referrer-Policy',
- 'Refresh',
- 'Retry-After',
- 'Sec-WebSocket-Accept',
- 'Sec-WebSocket-Extensions',
- 'Sec-WebSocket-Key',
- 'Sec-WebSocket-Protocol',
- 'Sec-WebSocket-Version',
- 'Server',
- 'Server-Timing',
- 'Service-Worker-Allowed',
- 'Service-Worker-Navigation-Preload',
- 'Set-Cookie',
- 'SourceMap',
- 'Strict-Transport-Security',
- 'Supports-Loading-Mode',
- 'TE',
- 'Timing-Allow-Origin',
- 'Trailer',
- 'Transfer-Encoding',
- 'Upgrade',
- 'Upgrade-Insecure-Requests',
- 'User-Agent',
- 'Vary',
- 'Via',
- 'WWW-Authenticate',
- 'X-Content-Type-Options',
- 'X-DNS-Prefetch-Control',
- 'X-Frame-Options',
- 'X-Permitted-Cross-Domain-Policies',
- 'X-Powered-By',
- 'X-Requested-With',
- 'X-XSS-Protection'
-])
-
-/** @type {Record<typeof wellknownHeaderNames[number]|Lowercase<typeof wellknownHeaderNames[number]>, string>} */
-const headerNameLowerCasedRecord = {}
-
-// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.
-Object.setPrototypeOf(headerNameLowerCasedRecord, null)
-
-/**
- * @type {Record<Lowercase<typeof wellknownHeaderNames[number]>, Buffer>}
- */
-const wellknownHeaderNameBuffers = {}
-
-// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.
-Object.setPrototypeOf(wellknownHeaderNameBuffers, null)
-
-/**
- * @param {string} header Lowercased header
- * @returns {Buffer}
- */
-function getHeaderNameAsBuffer (header) {
- let buffer = wellknownHeaderNameBuffers[header]
-
- if (buffer === undefined) {
- buffer = Buffer.from(header)
- }
-
- return buffer
-}
-
-for (let i = 0; i < wellknownHeaderNames.length; ++i) {
- const key = wellknownHeaderNames[i]
- const lowerCasedKey = key.toLowerCase()
- headerNameLowerCasedRecord[key] = headerNameLowerCasedRecord[lowerCasedKey] =
- lowerCasedKey
-}
-
-module.exports = {
- wellknownHeaderNames,
- headerNameLowerCasedRecord,
- getHeaderNameAsBuffer
-}
diff --git a/vanilla/node_modules/undici/lib/core/diagnostics.js b/vanilla/node_modules/undici/lib/core/diagnostics.js
deleted file mode 100644
index ccd6870..0000000
--- a/vanilla/node_modules/undici/lib/core/diagnostics.js
+++ /dev/null
@@ -1,225 +0,0 @@
-'use strict'
-
-const diagnosticsChannel = require('node:diagnostics_channel')
-const util = require('node:util')
-
-const undiciDebugLog = util.debuglog('undici')
-const fetchDebuglog = util.debuglog('fetch')
-const websocketDebuglog = util.debuglog('websocket')
-
-const channels = {
- // Client
- beforeConnect: diagnosticsChannel.channel('undici:client:beforeConnect'),
- connected: diagnosticsChannel.channel('undici:client:connected'),
- connectError: diagnosticsChannel.channel('undici:client:connectError'),
- sendHeaders: diagnosticsChannel.channel('undici:client:sendHeaders'),
- // Request
- create: diagnosticsChannel.channel('undici:request:create'),
- bodySent: diagnosticsChannel.channel('undici:request:bodySent'),
- bodyChunkSent: diagnosticsChannel.channel('undici:request:bodyChunkSent'),
- bodyChunkReceived: diagnosticsChannel.channel('undici:request:bodyChunkReceived'),
- headers: diagnosticsChannel.channel('undici:request:headers'),
- trailers: diagnosticsChannel.channel('undici:request:trailers'),
- error: diagnosticsChannel.channel('undici:request:error'),
- // WebSocket
- open: diagnosticsChannel.channel('undici:websocket:open'),
- close: diagnosticsChannel.channel('undici:websocket:close'),
- socketError: diagnosticsChannel.channel('undici:websocket:socket_error'),
- ping: diagnosticsChannel.channel('undici:websocket:ping'),
- pong: diagnosticsChannel.channel('undici:websocket:pong'),
- // ProxyAgent
- proxyConnected: diagnosticsChannel.channel('undici:proxy:connected')
-}
-
-let isTrackingClientEvents = false
-
-function trackClientEvents (debugLog = undiciDebugLog) {
- if (isTrackingClientEvents) {
- return
- }
-
- // Check if any of the channels already have subscribers to prevent duplicate subscriptions
- // This can happen when both Node.js built-in undici and undici as a dependency are present
- if (channels.beforeConnect.hasSubscribers || channels.connected.hasSubscribers ||
- channels.connectError.hasSubscribers || channels.sendHeaders.hasSubscribers) {
- isTrackingClientEvents = true
- return
- }
-
- isTrackingClientEvents = true
-
- diagnosticsChannel.subscribe('undici:client:beforeConnect',
- evt => {
- const {
- connectParams: { version, protocol, port, host }
- } = evt
- debugLog(
- 'connecting to %s%s using %s%s',
- host,
- port ? `:${port}` : '',
- protocol,
- version
- )
- })
-
- diagnosticsChannel.subscribe('undici:client:connected',
- evt => {
- const {
- connectParams: { version, protocol, port, host }
- } = evt
- debugLog(
- 'connected to %s%s using %s%s',
- host,
- port ? `:${port}` : '',
- protocol,
- version
- )
- })
-
- diagnosticsChannel.subscribe('undici:client:connectError',
- evt => {
- const {
- connectParams: { version, protocol, port, host },
- error
- } = evt
- debugLog(
- 'connection to %s%s using %s%s errored - %s',
- host,
- port ? `:${port}` : '',
- protocol,
- version,
- error.message
- )
- })
-
- diagnosticsChannel.subscribe('undici:client:sendHeaders',
- evt => {
- const {
- request: { method, path, origin }
- } = evt
- debugLog('sending request to %s %s%s', method, origin, path)
- })
-}
-
-let isTrackingRequestEvents = false
-
-function trackRequestEvents (debugLog = undiciDebugLog) {
- if (isTrackingRequestEvents) {
- return
- }
-
- // Check if any of the channels already have subscribers to prevent duplicate subscriptions
- // This can happen when both Node.js built-in undici and undici as a dependency are present
- if (channels.headers.hasSubscribers || channels.trailers.hasSubscribers ||
- channels.error.hasSubscribers) {
- isTrackingRequestEvents = true
- return
- }
-
- isTrackingRequestEvents = true
-
- diagnosticsChannel.subscribe('undici:request:headers',
- evt => {
- const {
- request: { method, path, origin },
- response: { statusCode }
- } = evt
- debugLog(
- 'received response to %s %s%s - HTTP %d',
- method,
- origin,
- path,
- statusCode
- )
- })
-
- diagnosticsChannel.subscribe('undici:request:trailers',
- evt => {
- const {
- request: { method, path, origin }
- } = evt
- debugLog('trailers received from %s %s%s', method, origin, path)
- })
-
- diagnosticsChannel.subscribe('undici:request:error',
- evt => {
- const {
- request: { method, path, origin },
- error
- } = evt
- debugLog(
- 'request to %s %s%s errored - %s',
- method,
- origin,
- path,
- error.message
- )
- })
-}
-
-let isTrackingWebSocketEvents = false
-
-function trackWebSocketEvents (debugLog = websocketDebuglog) {
- if (isTrackingWebSocketEvents) {
- return
- }
-
- // Check if any of the channels already have subscribers to prevent duplicate subscriptions
- // This can happen when both Node.js built-in undici and undici as a dependency are present
- if (channels.open.hasSubscribers || channels.close.hasSubscribers ||
- channels.socketError.hasSubscribers || channels.ping.hasSubscribers ||
- channels.pong.hasSubscribers) {
- isTrackingWebSocketEvents = true
- return
- }
-
- isTrackingWebSocketEvents = true
-
- diagnosticsChannel.subscribe('undici:websocket:open',
- evt => {
- const {
- address: { address, port }
- } = evt
- debugLog('connection opened %s%s', address, port ? `:${port}` : '')
- })
-
- diagnosticsChannel.subscribe('undici:websocket:close',
- evt => {
- const { websocket, code, reason } = evt
- debugLog(
- 'closed connection to %s - %s %s',
- websocket.url,
- code,
- reason
- )
- })
-
- diagnosticsChannel.subscribe('undici:websocket:socket_error',
- err => {
- debugLog('connection errored - %s', err.message)
- })
-
- diagnosticsChannel.subscribe('undici:websocket:ping',
- evt => {
- debugLog('ping received')
- })
-
- diagnosticsChannel.subscribe('undici:websocket:pong',
- evt => {
- debugLog('pong received')
- })
-}
-
-if (undiciDebugLog.enabled || fetchDebuglog.enabled) {
- trackClientEvents(fetchDebuglog.enabled ? fetchDebuglog : undiciDebugLog)
- trackRequestEvents(fetchDebuglog.enabled ? fetchDebuglog : undiciDebugLog)
-}
-
-if (websocketDebuglog.enabled) {
- trackClientEvents(undiciDebugLog.enabled ? undiciDebugLog : websocketDebuglog)
- trackWebSocketEvents(websocketDebuglog)
-}
-
-module.exports = {
- channels
-}
diff --git a/vanilla/node_modules/undici/lib/core/errors.js b/vanilla/node_modules/undici/lib/core/errors.js
deleted file mode 100644
index 4b1a8a1..0000000
--- a/vanilla/node_modules/undici/lib/core/errors.js
+++ /dev/null
@@ -1,448 +0,0 @@
-'use strict'
-
-const kUndiciError = Symbol.for('undici.error.UND_ERR')
-class UndiciError extends Error {
- constructor (message, options) {
- super(message, options)
- this.name = 'UndiciError'
- this.code = 'UND_ERR'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kUndiciError] === true
- }
-
- get [kUndiciError] () {
- return true
- }
-}
-
-const kConnectTimeoutError = Symbol.for('undici.error.UND_ERR_CONNECT_TIMEOUT')
-class ConnectTimeoutError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'ConnectTimeoutError'
- this.message = message || 'Connect Timeout Error'
- this.code = 'UND_ERR_CONNECT_TIMEOUT'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kConnectTimeoutError] === true
- }
-
- get [kConnectTimeoutError] () {
- return true
- }
-}
-
-const kHeadersTimeoutError = Symbol.for('undici.error.UND_ERR_HEADERS_TIMEOUT')
-class HeadersTimeoutError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'HeadersTimeoutError'
- this.message = message || 'Headers Timeout Error'
- this.code = 'UND_ERR_HEADERS_TIMEOUT'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kHeadersTimeoutError] === true
- }
-
- get [kHeadersTimeoutError] () {
- return true
- }
-}
-
-const kHeadersOverflowError = Symbol.for('undici.error.UND_ERR_HEADERS_OVERFLOW')
-class HeadersOverflowError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'HeadersOverflowError'
- this.message = message || 'Headers Overflow Error'
- this.code = 'UND_ERR_HEADERS_OVERFLOW'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kHeadersOverflowError] === true
- }
-
- get [kHeadersOverflowError] () {
- return true
- }
-}
-
-const kBodyTimeoutError = Symbol.for('undici.error.UND_ERR_BODY_TIMEOUT')
-class BodyTimeoutError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'BodyTimeoutError'
- this.message = message || 'Body Timeout Error'
- this.code = 'UND_ERR_BODY_TIMEOUT'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kBodyTimeoutError] === true
- }
-
- get [kBodyTimeoutError] () {
- return true
- }
-}
-
-const kInvalidArgumentError = Symbol.for('undici.error.UND_ERR_INVALID_ARG')
-class InvalidArgumentError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'InvalidArgumentError'
- this.message = message || 'Invalid Argument Error'
- this.code = 'UND_ERR_INVALID_ARG'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kInvalidArgumentError] === true
- }
-
- get [kInvalidArgumentError] () {
- return true
- }
-}
-
-const kInvalidReturnValueError = Symbol.for('undici.error.UND_ERR_INVALID_RETURN_VALUE')
-class InvalidReturnValueError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'InvalidReturnValueError'
- this.message = message || 'Invalid Return Value Error'
- this.code = 'UND_ERR_INVALID_RETURN_VALUE'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kInvalidReturnValueError] === true
- }
-
- get [kInvalidReturnValueError] () {
- return true
- }
-}
-
-const kAbortError = Symbol.for('undici.error.UND_ERR_ABORT')
-class AbortError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'AbortError'
- this.message = message || 'The operation was aborted'
- this.code = 'UND_ERR_ABORT'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kAbortError] === true
- }
-
- get [kAbortError] () {
- return true
- }
-}
-
-const kRequestAbortedError = Symbol.for('undici.error.UND_ERR_ABORTED')
-class RequestAbortedError extends AbortError {
- constructor (message) {
- super(message)
- this.name = 'AbortError'
- this.message = message || 'Request aborted'
- this.code = 'UND_ERR_ABORTED'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kRequestAbortedError] === true
- }
-
- get [kRequestAbortedError] () {
- return true
- }
-}
-
-const kInformationalError = Symbol.for('undici.error.UND_ERR_INFO')
-class InformationalError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'InformationalError'
- this.message = message || 'Request information'
- this.code = 'UND_ERR_INFO'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kInformationalError] === true
- }
-
- get [kInformationalError] () {
- return true
- }
-}
-
-const kRequestContentLengthMismatchError = Symbol.for('undici.error.UND_ERR_REQ_CONTENT_LENGTH_MISMATCH')
-class RequestContentLengthMismatchError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'RequestContentLengthMismatchError'
- this.message = message || 'Request body length does not match content-length header'
- this.code = 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kRequestContentLengthMismatchError] === true
- }
-
- get [kRequestContentLengthMismatchError] () {
- return true
- }
-}
-
-const kResponseContentLengthMismatchError = Symbol.for('undici.error.UND_ERR_RES_CONTENT_LENGTH_MISMATCH')
-class ResponseContentLengthMismatchError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'ResponseContentLengthMismatchError'
- this.message = message || 'Response body length does not match content-length header'
- this.code = 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kResponseContentLengthMismatchError] === true
- }
-
- get [kResponseContentLengthMismatchError] () {
- return true
- }
-}
-
-const kClientDestroyedError = Symbol.for('undici.error.UND_ERR_DESTROYED')
-class ClientDestroyedError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'ClientDestroyedError'
- this.message = message || 'The client is destroyed'
- this.code = 'UND_ERR_DESTROYED'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kClientDestroyedError] === true
- }
-
- get [kClientDestroyedError] () {
- return true
- }
-}
-
-const kClientClosedError = Symbol.for('undici.error.UND_ERR_CLOSED')
-class ClientClosedError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'ClientClosedError'
- this.message = message || 'The client is closed'
- this.code = 'UND_ERR_CLOSED'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kClientClosedError] === true
- }
-
- get [kClientClosedError] () {
- return true
- }
-}
-
-const kSocketError = Symbol.for('undici.error.UND_ERR_SOCKET')
-class SocketError extends UndiciError {
- constructor (message, socket) {
- super(message)
- this.name = 'SocketError'
- this.message = message || 'Socket error'
- this.code = 'UND_ERR_SOCKET'
- this.socket = socket
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kSocketError] === true
- }
-
- get [kSocketError] () {
- return true
- }
-}
-
-const kNotSupportedError = Symbol.for('undici.error.UND_ERR_NOT_SUPPORTED')
-class NotSupportedError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'NotSupportedError'
- this.message = message || 'Not supported error'
- this.code = 'UND_ERR_NOT_SUPPORTED'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kNotSupportedError] === true
- }
-
- get [kNotSupportedError] () {
- return true
- }
-}
-
-const kBalancedPoolMissingUpstreamError = Symbol.for('undici.error.UND_ERR_BPL_MISSING_UPSTREAM')
-class BalancedPoolMissingUpstreamError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'MissingUpstreamError'
- this.message = message || 'No upstream has been added to the BalancedPool'
- this.code = 'UND_ERR_BPL_MISSING_UPSTREAM'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kBalancedPoolMissingUpstreamError] === true
- }
-
- get [kBalancedPoolMissingUpstreamError] () {
- return true
- }
-}
-
-const kHTTPParserError = Symbol.for('undici.error.UND_ERR_HTTP_PARSER')
-class HTTPParserError extends Error {
- constructor (message, code, data) {
- super(message)
- this.name = 'HTTPParserError'
- this.code = code ? `HPE_${code}` : undefined
- this.data = data ? data.toString() : undefined
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kHTTPParserError] === true
- }
-
- get [kHTTPParserError] () {
- return true
- }
-}
-
-const kResponseExceededMaxSizeError = Symbol.for('undici.error.UND_ERR_RES_EXCEEDED_MAX_SIZE')
-class ResponseExceededMaxSizeError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'ResponseExceededMaxSizeError'
- this.message = message || 'Response content exceeded max size'
- this.code = 'UND_ERR_RES_EXCEEDED_MAX_SIZE'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kResponseExceededMaxSizeError] === true
- }
-
- get [kResponseExceededMaxSizeError] () {
- return true
- }
-}
-
-const kRequestRetryError = Symbol.for('undici.error.UND_ERR_REQ_RETRY')
-class RequestRetryError extends UndiciError {
- constructor (message, code, { headers, data }) {
- super(message)
- this.name = 'RequestRetryError'
- this.message = message || 'Request retry error'
- this.code = 'UND_ERR_REQ_RETRY'
- this.statusCode = code
- this.data = data
- this.headers = headers
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kRequestRetryError] === true
- }
-
- get [kRequestRetryError] () {
- return true
- }
-}
-
-const kResponseError = Symbol.for('undici.error.UND_ERR_RESPONSE')
-class ResponseError extends UndiciError {
- constructor (message, code, { headers, body }) {
- super(message)
- this.name = 'ResponseError'
- this.message = message || 'Response error'
- this.code = 'UND_ERR_RESPONSE'
- this.statusCode = code
- this.body = body
- this.headers = headers
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kResponseError] === true
- }
-
- get [kResponseError] () {
- return true
- }
-}
-
-const kSecureProxyConnectionError = Symbol.for('undici.error.UND_ERR_PRX_TLS')
-class SecureProxyConnectionError extends UndiciError {
- constructor (cause, message, options = {}) {
- super(message, { cause, ...options })
- this.name = 'SecureProxyConnectionError'
- this.message = message || 'Secure Proxy Connection failed'
- this.code = 'UND_ERR_PRX_TLS'
- this.cause = cause
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kSecureProxyConnectionError] === true
- }
-
- get [kSecureProxyConnectionError] () {
- return true
- }
-}
-
-const kMaxOriginsReachedError = Symbol.for('undici.error.UND_ERR_MAX_ORIGINS_REACHED')
-class MaxOriginsReachedError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'MaxOriginsReachedError'
- this.message = message || 'Maximum allowed origins reached'
- this.code = 'UND_ERR_MAX_ORIGINS_REACHED'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kMaxOriginsReachedError] === true
- }
-
- get [kMaxOriginsReachedError] () {
- return true
- }
-}
-
-module.exports = {
- AbortError,
- HTTPParserError,
- UndiciError,
- HeadersTimeoutError,
- HeadersOverflowError,
- BodyTimeoutError,
- RequestContentLengthMismatchError,
- ConnectTimeoutError,
- InvalidArgumentError,
- InvalidReturnValueError,
- RequestAbortedError,
- ClientDestroyedError,
- ClientClosedError,
- InformationalError,
- SocketError,
- NotSupportedError,
- ResponseContentLengthMismatchError,
- BalancedPoolMissingUpstreamError,
- ResponseExceededMaxSizeError,
- RequestRetryError,
- ResponseError,
- SecureProxyConnectionError,
- MaxOriginsReachedError
-}
diff --git a/vanilla/node_modules/undici/lib/core/request.js b/vanilla/node_modules/undici/lib/core/request.js
deleted file mode 100644
index 7dbf781..0000000
--- a/vanilla/node_modules/undici/lib/core/request.js
+++ /dev/null
@@ -1,412 +0,0 @@
-'use strict'
-
-const {
- InvalidArgumentError,
- NotSupportedError
-} = require('./errors')
-const assert = require('node:assert')
-const {
- isValidHTTPToken,
- isValidHeaderValue,
- isStream,
- destroy,
- isBuffer,
- isFormDataLike,
- isIterable,
- isBlobLike,
- serializePathWithQuery,
- assertRequestHandler,
- getServerName,
- normalizedMethodRecords,
- getProtocolFromUrlString
-} = require('./util')
-const { channels } = require('./diagnostics.js')
-const { headerNameLowerCasedRecord } = require('./constants')
-
-// Verifies that a given path is valid does not contain control chars \x00 to \x20
-const invalidPathRegex = /[^\u0021-\u00ff]/
-
-const kHandler = Symbol('handler')
-
-class Request {
- constructor (origin, {
- path,
- method,
- body,
- headers,
- query,
- idempotent,
- blocking,
- upgrade,
- headersTimeout,
- bodyTimeout,
- reset,
- expectContinue,
- servername,
- throwOnError,
- maxRedirections
- }, handler) {
- if (typeof path !== 'string') {
- throw new InvalidArgumentError('path must be a string')
- } else if (
- path[0] !== '/' &&
- !(path.startsWith('http://') || path.startsWith('https://')) &&
- method !== 'CONNECT'
- ) {
- throw new InvalidArgumentError('path must be an absolute URL or start with a slash')
- } else if (invalidPathRegex.test(path)) {
- throw new InvalidArgumentError('invalid request path')
- }
-
- if (typeof method !== 'string') {
- throw new InvalidArgumentError('method must be a string')
- } else if (normalizedMethodRecords[method] === undefined && !isValidHTTPToken(method)) {
- throw new InvalidArgumentError('invalid request method')
- }
-
- if (upgrade && typeof upgrade !== 'string') {
- throw new InvalidArgumentError('upgrade must be a string')
- }
-
- if (headersTimeout != null && (!Number.isFinite(headersTimeout) || headersTimeout < 0)) {
- throw new InvalidArgumentError('invalid headersTimeout')
- }
-
- if (bodyTimeout != null && (!Number.isFinite(bodyTimeout) || bodyTimeout < 0)) {
- throw new InvalidArgumentError('invalid bodyTimeout')
- }
-
- if (reset != null && typeof reset !== 'boolean') {
- throw new InvalidArgumentError('invalid reset')
- }
-
- if (expectContinue != null && typeof expectContinue !== 'boolean') {
- throw new InvalidArgumentError('invalid expectContinue')
- }
-
- if (throwOnError != null) {
- throw new InvalidArgumentError('invalid throwOnError')
- }
-
- if (maxRedirections != null && maxRedirections !== 0) {
- throw new InvalidArgumentError('maxRedirections is not supported, use the redirect interceptor')
- }
-
- this.headersTimeout = headersTimeout
-
- this.bodyTimeout = bodyTimeout
-
- this.method = method
-
- this.abort = null
-
- if (body == null) {
- this.body = null
- } else if (isStream(body)) {
- this.body = body
-
- const rState = this.body._readableState
- if (!rState || !rState.autoDestroy) {
- this.endHandler = function autoDestroy () {
- destroy(this)
- }
- this.body.on('end', this.endHandler)
- }
-
- this.errorHandler = err => {
- if (this.abort) {
- this.abort(err)
- } else {
- this.error = err
- }
- }
- this.body.on('error', this.errorHandler)
- } else if (isBuffer(body)) {
- this.body = body.byteLength ? body : null
- } else if (ArrayBuffer.isView(body)) {
- this.body = body.buffer.byteLength ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : null
- } else if (body instanceof ArrayBuffer) {
- this.body = body.byteLength ? Buffer.from(body) : null
- } else if (typeof body === 'string') {
- this.body = body.length ? Buffer.from(body) : null
- } else if (isFormDataLike(body) || isIterable(body) || isBlobLike(body)) {
- this.body = body
- } else {
- throw new InvalidArgumentError('body must be a string, a Buffer, a Readable stream, an iterable, or an async iterable')
- }
-
- this.completed = false
- this.aborted = false
-
- this.upgrade = upgrade || null
-
- this.path = query ? serializePathWithQuery(path, query) : path
-
- // TODO: shall we maybe standardize it to an URL object?
- this.origin = origin
-
- this.protocol = getProtocolFromUrlString(origin)
-
- this.idempotent = idempotent == null
- ? method === 'HEAD' || method === 'GET'
- : idempotent
-
- this.blocking = blocking ?? this.method !== 'HEAD'
-
- this.reset = reset == null ? null : reset
-
- this.host = null
-
- this.contentLength = null
-
- this.contentType = null
-
- this.headers = []
-
- // Only for H2
- this.expectContinue = expectContinue != null ? expectContinue : false
-
- if (Array.isArray(headers)) {
- if (headers.length % 2 !== 0) {
- throw new InvalidArgumentError('headers array must be even')
- }
- for (let i = 0; i < headers.length; i += 2) {
- processHeader(this, headers[i], headers[i + 1])
- }
- } else if (headers && typeof headers === 'object') {
- if (headers[Symbol.iterator]) {
- for (const header of headers) {
- if (!Array.isArray(header) || header.length !== 2) {
- throw new InvalidArgumentError('headers must be in key-value pair format')
- }
- processHeader(this, header[0], header[1])
- }
- } else {
- const keys = Object.keys(headers)
- for (let i = 0; i < keys.length; ++i) {
- processHeader(this, keys[i], headers[keys[i]])
- }
- }
- } else if (headers != null) {
- throw new InvalidArgumentError('headers must be an object or an array')
- }
-
- assertRequestHandler(handler, method, upgrade)
-
- this.servername = servername || getServerName(this.host) || null
-
- this[kHandler] = handler
-
- if (channels.create.hasSubscribers) {
- channels.create.publish({ request: this })
- }
- }
-
- onBodySent (chunk) {
- if (channels.bodyChunkSent.hasSubscribers) {
- channels.bodyChunkSent.publish({ request: this, chunk })
- }
- if (this[kHandler].onBodySent) {
- try {
- return this[kHandler].onBodySent(chunk)
- } catch (err) {
- this.abort(err)
- }
- }
- }
-
- onRequestSent () {
- if (channels.bodySent.hasSubscribers) {
- channels.bodySent.publish({ request: this })
- }
-
- if (this[kHandler].onRequestSent) {
- try {
- return this[kHandler].onRequestSent()
- } catch (err) {
- this.abort(err)
- }
- }
- }
-
- onConnect (abort) {
- assert(!this.aborted)
- assert(!this.completed)
-
- if (this.error) {
- abort(this.error)
- } else {
- this.abort = abort
- return this[kHandler].onConnect(abort)
- }
- }
-
- onResponseStarted () {
- return this[kHandler].onResponseStarted?.()
- }
-
- onHeaders (statusCode, headers, resume, statusText) {
- assert(!this.aborted)
- assert(!this.completed)
-
- if (channels.headers.hasSubscribers) {
- channels.headers.publish({ request: this, response: { statusCode, headers, statusText } })
- }
-
- try {
- return this[kHandler].onHeaders(statusCode, headers, resume, statusText)
- } catch (err) {
- this.abort(err)
- }
- }
-
- onData (chunk) {
- assert(!this.aborted)
- assert(!this.completed)
-
- if (channels.bodyChunkReceived.hasSubscribers) {
- channels.bodyChunkReceived.publish({ request: this, chunk })
- }
- try {
- return this[kHandler].onData(chunk)
- } catch (err) {
- this.abort(err)
- return false
- }
- }
-
- onUpgrade (statusCode, headers, socket) {
- assert(!this.aborted)
- assert(!this.completed)
-
- return this[kHandler].onUpgrade(statusCode, headers, socket)
- }
-
- onComplete (trailers) {
- this.onFinally()
-
- assert(!this.aborted)
- assert(!this.completed)
-
- this.completed = true
- if (channels.trailers.hasSubscribers) {
- channels.trailers.publish({ request: this, trailers })
- }
-
- try {
- return this[kHandler].onComplete(trailers)
- } catch (err) {
- // TODO (fix): This might be a bad idea?
- this.onError(err)
- }
- }
-
- onError (error) {
- this.onFinally()
-
- if (channels.error.hasSubscribers) {
- channels.error.publish({ request: this, error })
- }
-
- if (this.aborted) {
- return
- }
- this.aborted = true
-
- return this[kHandler].onError(error)
- }
-
- onFinally () {
- if (this.errorHandler) {
- this.body.off('error', this.errorHandler)
- this.errorHandler = null
- }
-
- if (this.endHandler) {
- this.body.off('end', this.endHandler)
- this.endHandler = null
- }
- }
-
- addHeader (key, value) {
- processHeader(this, key, value)
- return this
- }
-}
-
-function processHeader (request, key, val) {
- if (val && (typeof val === 'object' && !Array.isArray(val))) {
- throw new InvalidArgumentError(`invalid ${key} header`)
- } else if (val === undefined) {
- return
- }
-
- let headerName = headerNameLowerCasedRecord[key]
-
- if (headerName === undefined) {
- headerName = key.toLowerCase()
- if (headerNameLowerCasedRecord[headerName] === undefined && !isValidHTTPToken(headerName)) {
- throw new InvalidArgumentError('invalid header key')
- }
- }
-
- if (Array.isArray(val)) {
- const arr = []
- for (let i = 0; i < val.length; i++) {
- if (typeof val[i] === 'string') {
- if (!isValidHeaderValue(val[i])) {
- throw new InvalidArgumentError(`invalid ${key} header`)
- }
- arr.push(val[i])
- } else if (val[i] === null) {
- arr.push('')
- } else if (typeof val[i] === 'object') {
- throw new InvalidArgumentError(`invalid ${key} header`)
- } else {
- arr.push(`${val[i]}`)
- }
- }
- val = arr
- } else if (typeof val === 'string') {
- if (!isValidHeaderValue(val)) {
- throw new InvalidArgumentError(`invalid ${key} header`)
- }
- } else if (val === null) {
- val = ''
- } else {
- val = `${val}`
- }
-
- if (request.host === null && headerName === 'host') {
- if (typeof val !== 'string') {
- throw new InvalidArgumentError('invalid host header')
- }
- // Consumed by Client
- request.host = val
- } else if (request.contentLength === null && headerName === 'content-length') {
- request.contentLength = parseInt(val, 10)
- if (!Number.isFinite(request.contentLength)) {
- throw new InvalidArgumentError('invalid content-length header')
- }
- } else if (request.contentType === null && headerName === 'content-type') {
- request.contentType = val
- request.headers.push(key, val)
- } else if (headerName === 'transfer-encoding' || headerName === 'keep-alive' || headerName === 'upgrade') {
- throw new InvalidArgumentError(`invalid ${headerName} header`)
- } else if (headerName === 'connection') {
- const value = typeof val === 'string' ? val.toLowerCase() : null
- if (value !== 'close' && value !== 'keep-alive') {
- throw new InvalidArgumentError('invalid connection header')
- }
-
- if (value === 'close') {
- request.reset = true
- }
- } else if (headerName === 'expect') {
- throw new NotSupportedError('expect header not supported')
- } else {
- request.headers.push(key, val)
- }
-}
-
-module.exports = Request
diff --git a/vanilla/node_modules/undici/lib/core/symbols.js b/vanilla/node_modules/undici/lib/core/symbols.js
deleted file mode 100644
index fd7af0c..0000000
--- a/vanilla/node_modules/undici/lib/core/symbols.js
+++ /dev/null
@@ -1,74 +0,0 @@
-'use strict'
-
-module.exports = {
- kClose: Symbol('close'),
- kDestroy: Symbol('destroy'),
- kDispatch: Symbol('dispatch'),
- kUrl: Symbol('url'),
- kWriting: Symbol('writing'),
- kResuming: Symbol('resuming'),
- kQueue: Symbol('queue'),
- kConnect: Symbol('connect'),
- kConnecting: Symbol('connecting'),
- kKeepAliveDefaultTimeout: Symbol('default keep alive timeout'),
- kKeepAliveMaxTimeout: Symbol('max keep alive timeout'),
- kKeepAliveTimeoutThreshold: Symbol('keep alive timeout threshold'),
- kKeepAliveTimeoutValue: Symbol('keep alive timeout'),
- kKeepAlive: Symbol('keep alive'),
- kHeadersTimeout: Symbol('headers timeout'),
- kBodyTimeout: Symbol('body timeout'),
- kServerName: Symbol('server name'),
- kLocalAddress: Symbol('local address'),
- kHost: Symbol('host'),
- kNoRef: Symbol('no ref'),
- kBodyUsed: Symbol('used'),
- kBody: Symbol('abstracted request body'),
- kRunning: Symbol('running'),
- kBlocking: Symbol('blocking'),
- kPending: Symbol('pending'),
- kSize: Symbol('size'),
- kBusy: Symbol('busy'),
- kQueued: Symbol('queued'),
- kFree: Symbol('free'),
- kConnected: Symbol('connected'),
- kClosed: Symbol('closed'),
- kNeedDrain: Symbol('need drain'),
- kReset: Symbol('reset'),
- kDestroyed: Symbol.for('nodejs.stream.destroyed'),
- kResume: Symbol('resume'),
- kOnError: Symbol('on error'),
- kMaxHeadersSize: Symbol('max headers size'),
- kRunningIdx: Symbol('running index'),
- kPendingIdx: Symbol('pending index'),
- kError: Symbol('error'),
- kClients: Symbol('clients'),
- kClient: Symbol('client'),
- kParser: Symbol('parser'),
- kOnDestroyed: Symbol('destroy callbacks'),
- kPipelining: Symbol('pipelining'),
- kSocket: Symbol('socket'),
- kHostHeader: Symbol('host header'),
- kConnector: Symbol('connector'),
- kStrictContentLength: Symbol('strict content length'),
- kMaxRedirections: Symbol('maxRedirections'),
- kMaxRequests: Symbol('maxRequestsPerClient'),
- kProxy: Symbol('proxy agent options'),
- kCounter: Symbol('socket request counter'),
- kMaxResponseSize: Symbol('max response size'),
- kHTTP2Session: Symbol('http2Session'),
- kHTTP2SessionState: Symbol('http2Session state'),
- kRetryHandlerDefaultRetry: Symbol('retry agent default retry'),
- kConstruct: Symbol('constructable'),
- kListeners: Symbol('listeners'),
- kHTTPContext: Symbol('http context'),
- kMaxConcurrentStreams: Symbol('max concurrent streams'),
- kHTTP2InitialWindowSize: Symbol('http2 initial window size'),
- kHTTP2ConnectionWindowSize: Symbol('http2 connection window size'),
- kEnableConnectProtocol: Symbol('http2session connect protocol'),
- kRemoteSettings: Symbol('http2session remote settings'),
- kHTTP2Stream: Symbol('http2session client stream'),
- kPingInterval: Symbol('ping interval'),
- kNoProxyAgent: Symbol('no proxy agent'),
- kHttpProxyAgent: Symbol('http proxy agent'),
- kHttpsProxyAgent: Symbol('https proxy agent')
-}
diff --git a/vanilla/node_modules/undici/lib/core/tree.js b/vanilla/node_modules/undici/lib/core/tree.js
deleted file mode 100644
index 6eed58a..0000000
--- a/vanilla/node_modules/undici/lib/core/tree.js
+++ /dev/null
@@ -1,160 +0,0 @@
-'use strict'
-
-const {
- wellknownHeaderNames,
- headerNameLowerCasedRecord
-} = require('./constants')
-
-class TstNode {
- /** @type {any} */
- value = null
- /** @type {null | TstNode} */
- left = null
- /** @type {null | TstNode} */
- middle = null
- /** @type {null | TstNode} */
- right = null
- /** @type {number} */
- code
- /**
- * @param {string} key
- * @param {any} value
- * @param {number} index
- */
- constructor (key, value, index) {
- if (index === undefined || index >= key.length) {
- throw new TypeError('Unreachable')
- }
- const code = this.code = key.charCodeAt(index)
- // check code is ascii string
- if (code > 0x7F) {
- throw new TypeError('key must be ascii string')
- }
- if (key.length !== ++index) {
- this.middle = new TstNode(key, value, index)
- } else {
- this.value = value
- }
- }
-
- /**
- * @param {string} key
- * @param {any} value
- * @returns {void}
- */
- add (key, value) {
- const length = key.length
- if (length === 0) {
- throw new TypeError('Unreachable')
- }
- let index = 0
- /**
- * @type {TstNode}
- */
- let node = this
- while (true) {
- const code = key.charCodeAt(index)
- // check code is ascii string
- if (code > 0x7F) {
- throw new TypeError('key must be ascii string')
- }
- if (node.code === code) {
- if (length === ++index) {
- node.value = value
- break
- } else if (node.middle !== null) {
- node = node.middle
- } else {
- node.middle = new TstNode(key, value, index)
- break
- }
- } else if (node.code < code) {
- if (node.left !== null) {
- node = node.left
- } else {
- node.left = new TstNode(key, value, index)
- break
- }
- } else if (node.right !== null) {
- node = node.right
- } else {
- node.right = new TstNode(key, value, index)
- break
- }
- }
- }
-
- /**
- * @param {Uint8Array} key
- * @returns {TstNode | null}
- */
- search (key) {
- const keylength = key.length
- let index = 0
- /**
- * @type {TstNode|null}
- */
- let node = this
- while (node !== null && index < keylength) {
- let code = key[index]
- // A-Z
- // First check if it is bigger than 0x5a.
- // Lowercase letters have higher char codes than uppercase ones.
- // Also we assume that headers will mostly contain lowercase characters.
- if (code <= 0x5a && code >= 0x41) {
- // Lowercase for uppercase.
- code |= 32
- }
- while (node !== null) {
- if (code === node.code) {
- if (keylength === ++index) {
- // Returns Node since it is the last key.
- return node
- }
- node = node.middle
- break
- }
- node = node.code < code ? node.left : node.right
- }
- }
- return null
- }
-}
-
-class TernarySearchTree {
- /** @type {TstNode | null} */
- node = null
-
- /**
- * @param {string} key
- * @param {any} value
- * @returns {void}
- * */
- insert (key, value) {
- if (this.node === null) {
- this.node = new TstNode(key, value, 0)
- } else {
- this.node.add(key, value)
- }
- }
-
- /**
- * @param {Uint8Array} key
- * @returns {any}
- */
- lookup (key) {
- return this.node?.search(key)?.value ?? null
- }
-}
-
-const tree = new TernarySearchTree()
-
-for (let i = 0; i < wellknownHeaderNames.length; ++i) {
- const key = headerNameLowerCasedRecord[wellknownHeaderNames[i]]
- tree.insert(key, key)
-}
-
-module.exports = {
- TernarySearchTree,
- tree
-}
diff --git a/vanilla/node_modules/undici/lib/core/util.js b/vanilla/node_modules/undici/lib/core/util.js
deleted file mode 100644
index be2c1a7..0000000
--- a/vanilla/node_modules/undici/lib/core/util.js
+++ /dev/null
@@ -1,957 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { kDestroyed, kBodyUsed, kListeners, kBody } = require('./symbols')
-const { IncomingMessage } = require('node:http')
-const stream = require('node:stream')
-const net = require('node:net')
-const { stringify } = require('node:querystring')
-const { EventEmitter: EE } = require('node:events')
-const timers = require('../util/timers')
-const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
-const { headerNameLowerCasedRecord } = require('./constants')
-const { tree } = require('./tree')
-
-const [nodeMajor, nodeMinor] = process.versions.node.split('.', 2).map(v => Number(v))
-
-class BodyAsyncIterable {
- constructor (body) {
- this[kBody] = body
- this[kBodyUsed] = false
- }
-
- async * [Symbol.asyncIterator] () {
- assert(!this[kBodyUsed], 'disturbed')
- this[kBodyUsed] = true
- yield * this[kBody]
- }
-}
-
-function noop () {}
-
-/**
- * @param {*} body
- * @returns {*}
- */
-function wrapRequestBody (body) {
- if (isStream(body)) {
- // TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
- // so that it can be dispatched again?
- // TODO (fix): Do we need 100-expect support to provide a way to do this properly?
- if (bodyLength(body) === 0) {
- body
- .on('data', function () {
- assert(false)
- })
- }
-
- if (typeof body.readableDidRead !== 'boolean') {
- body[kBodyUsed] = false
- EE.prototype.on.call(body, 'data', function () {
- this[kBodyUsed] = true
- })
- }
-
- return body
- } else if (body && typeof body.pipeTo === 'function') {
- // TODO (fix): We can't access ReadableStream internal state
- // to determine whether or not it has been disturbed. This is just
- // a workaround.
- return new BodyAsyncIterable(body)
- } else if (body && isFormDataLike(body)) {
- return body
- } else if (
- body &&
- typeof body !== 'string' &&
- !ArrayBuffer.isView(body) &&
- isIterable(body)
- ) {
- // TODO: Should we allow re-using iterable if !this.opts.idempotent
- // or through some other flag?
- return new BodyAsyncIterable(body)
- } else {
- return body
- }
-}
-
-/**
- * @param {*} obj
- * @returns {obj is import('node:stream').Stream}
- */
-function isStream (obj) {
- return obj && typeof obj === 'object' && typeof obj.pipe === 'function' && typeof obj.on === 'function'
-}
-
-/**
- * @param {*} object
- * @returns {object is Blob}
- * based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License)
- */
-function isBlobLike (object) {
- if (object === null) {
- return false
- } else if (object instanceof Blob) {
- return true
- } else if (typeof object !== 'object') {
- return false
- } else {
- const sTag = object[Symbol.toStringTag]
-
- return (sTag === 'Blob' || sTag === 'File') && (
- ('stream' in object && typeof object.stream === 'function') ||
- ('arrayBuffer' in object && typeof object.arrayBuffer === 'function')
- )
- }
-}
-
-/**
- * @param {string} url The path to check for query strings or fragments.
- * @returns {boolean} Returns true if the path contains a query string or fragment.
- */
-function pathHasQueryOrFragment (url) {
- return (
- url.includes('?') ||
- url.includes('#')
- )
-}
-
-/**
- * @param {string} url The URL to add the query params to
- * @param {import('node:querystring').ParsedUrlQueryInput} queryParams The object to serialize into a URL query string
- * @returns {string} The URL with the query params added
- */
-function serializePathWithQuery (url, queryParams) {
- if (pathHasQueryOrFragment(url)) {
- throw new Error('Query params cannot be passed when url already contains "?" or "#".')
- }
-
- const stringified = stringify(queryParams)
-
- if (stringified) {
- url += '?' + stringified
- }
-
- return url
-}
-
-/**
- * @param {number|string|undefined} port
- * @returns {boolean}
- */
-function isValidPort (port) {
- const value = parseInt(port, 10)
- return (
- value === Number(port) &&
- value >= 0 &&
- value <= 65535
- )
-}
-
-/**
- * Check if the value is a valid http or https prefixed string.
- *
- * @param {string} value
- * @returns {boolean}
- */
-function isHttpOrHttpsPrefixed (value) {
- return (
- value != null &&
- value[0] === 'h' &&
- value[1] === 't' &&
- value[2] === 't' &&
- value[3] === 'p' &&
- (
- value[4] === ':' ||
- (
- value[4] === 's' &&
- value[5] === ':'
- )
- )
- )
-}
-
-/**
- * @param {string|URL|Record<string,string>} url
- * @returns {URL}
- */
-function parseURL (url) {
- if (typeof url === 'string') {
- /**
- * @type {URL}
- */
- url = new URL(url)
-
- if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
- throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')
- }
-
- return url
- }
-
- if (!url || typeof url !== 'object') {
- throw new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.')
- }
-
- if (!(url instanceof URL)) {
- if (url.port != null && url.port !== '' && isValidPort(url.port) === false) {
- throw new InvalidArgumentError('Invalid URL: port must be a valid integer or a string representation of an integer.')
- }
-
- if (url.path != null && typeof url.path !== 'string') {
- throw new InvalidArgumentError('Invalid URL path: the path must be a string or null/undefined.')
- }
-
- if (url.pathname != null && typeof url.pathname !== 'string') {
- throw new InvalidArgumentError('Invalid URL pathname: the pathname must be a string or null/undefined.')
- }
-
- if (url.hostname != null && typeof url.hostname !== 'string') {
- throw new InvalidArgumentError('Invalid URL hostname: the hostname must be a string or null/undefined.')
- }
-
- if (url.origin != null && typeof url.origin !== 'string') {
- throw new InvalidArgumentError('Invalid URL origin: the origin must be a string or null/undefined.')
- }
-
- if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
- throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')
- }
-
- const port = url.port != null
- ? url.port
- : (url.protocol === 'https:' ? 443 : 80)
- let origin = url.origin != null
- ? url.origin
- : `${url.protocol || ''}//${url.hostname || ''}:${port}`
- let path = url.path != null
- ? url.path
- : `${url.pathname || ''}${url.search || ''}`
-
- if (origin[origin.length - 1] === '/') {
- origin = origin.slice(0, origin.length - 1)
- }
-
- if (path && path[0] !== '/') {
- path = `/${path}`
- }
- // new URL(path, origin) is unsafe when `path` contains an absolute URL
- // From https://developer.mozilla.org/en-US/docs/Web/API/URL/URL:
- // If first parameter is a relative URL, second param is required, and will be used as the base URL.
- // If first parameter is an absolute URL, a given second param will be ignored.
- return new URL(`${origin}${path}`)
- }
-
- if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
- throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')
- }
-
- return url
-}
-
-/**
- * @param {string|URL|Record<string, string>} url
- * @returns {URL}
- */
-function parseOrigin (url) {
- url = parseURL(url)
-
- if (url.pathname !== '/' || url.search || url.hash) {
- throw new InvalidArgumentError('invalid url')
- }
-
- return url
-}
-
-/**
- * @param {string} host
- * @returns {string}
- */
-function getHostname (host) {
- if (host[0] === '[') {
- const idx = host.indexOf(']')
-
- assert(idx !== -1)
- return host.substring(1, idx)
- }
-
- const idx = host.indexOf(':')
- if (idx === -1) return host
-
- return host.substring(0, idx)
-}
-
-/**
- * IP addresses are not valid server names per RFC6066
- * Currently, the only server names supported are DNS hostnames
- * @param {string|null} host
- * @returns {string|null}
- */
-function getServerName (host) {
- if (!host) {
- return null
- }
-
- assert(typeof host === 'string')
-
- const servername = getHostname(host)
- if (net.isIP(servername)) {
- return ''
- }
-
- return servername
-}
-
-/**
- * @function
- * @template T
- * @param {T} obj
- * @returns {T}
- */
-function deepClone (obj) {
- return JSON.parse(JSON.stringify(obj))
-}
-
-/**
- * @param {*} obj
- * @returns {obj is AsyncIterable}
- */
-function isAsyncIterable (obj) {
- return !!(obj != null && typeof obj[Symbol.asyncIterator] === 'function')
-}
-
-/**
- * @param {*} obj
- * @returns {obj is Iterable}
- */
-function isIterable (obj) {
- return !!(obj != null && (typeof obj[Symbol.iterator] === 'function' || typeof obj[Symbol.asyncIterator] === 'function'))
-}
-
-/**
- * @param {Blob|Buffer|import ('stream').Stream} body
- * @returns {number|null}
- */
-function bodyLength (body) {
- if (body == null) {
- return 0
- } else if (isStream(body)) {
- const state = body._readableState
- return state && state.objectMode === false && state.ended === true && Number.isFinite(state.length)
- ? state.length
- : null
- } else if (isBlobLike(body)) {
- return body.size != null ? body.size : null
- } else if (isBuffer(body)) {
- return body.byteLength
- }
-
- return null
-}
-
-/**
- * @param {import ('stream').Stream} body
- * @returns {boolean}
- */
-function isDestroyed (body) {
- return body && !!(body.destroyed || body[kDestroyed] || (stream.isDestroyed?.(body)))
-}
-
-/**
- * @param {import ('stream').Stream} stream
- * @param {Error} [err]
- * @returns {void}
- */
-function destroy (stream, err) {
- if (stream == null || !isStream(stream) || isDestroyed(stream)) {
- return
- }
-
- if (typeof stream.destroy === 'function') {
- if (Object.getPrototypeOf(stream).constructor === IncomingMessage) {
- // See: https://github.com/nodejs/node/pull/38505/files
- stream.socket = null
- }
-
- stream.destroy(err)
- } else if (err) {
- queueMicrotask(() => {
- stream.emit('error', err)
- })
- }
-
- if (stream.destroyed !== true) {
- stream[kDestroyed] = true
- }
-}
-
-const KEEPALIVE_TIMEOUT_EXPR = /timeout=(\d+)/
-/**
- * @param {string} val
- * @returns {number | null}
- */
-function parseKeepAliveTimeout (val) {
- const m = val.match(KEEPALIVE_TIMEOUT_EXPR)
- return m ? parseInt(m[1], 10) * 1000 : null
-}
-
-/**
- * Retrieves a header name and returns its lowercase value.
- * @param {string | Buffer} value Header name
- * @returns {string}
- */
-function headerNameToString (value) {
- return typeof value === 'string'
- ? headerNameLowerCasedRecord[value] ?? value.toLowerCase()
- : tree.lookup(value) ?? value.toString('latin1').toLowerCase()
-}
-
-/**
- * Receive the buffer as a string and return its lowercase value.
- * @param {Buffer} value Header name
- * @returns {string}
- */
-function bufferToLowerCasedHeaderName (value) {
- return tree.lookup(value) ?? value.toString('latin1').toLowerCase()
-}
-
-/**
- * @param {(Buffer | string)[]} headers
- * @param {Record<string, string | string[]>} [obj]
- * @returns {Record<string, string | string[]>}
- */
-function parseHeaders (headers, obj) {
- if (obj === undefined) obj = {}
-
- for (let i = 0; i < headers.length; i += 2) {
- const key = headerNameToString(headers[i])
- let val = obj[key]
-
- if (val) {
- if (typeof val === 'string') {
- val = [val]
- obj[key] = val
- }
- val.push(headers[i + 1].toString('latin1'))
- } else {
- const headersValue = headers[i + 1]
- if (typeof headersValue === 'string') {
- obj[key] = headersValue
- } else {
- obj[key] = Array.isArray(headersValue) ? headersValue.map(x => x.toString('latin1')) : headersValue.toString('latin1')
- }
- }
- }
-
- return obj
-}
-
-/**
- * @param {Buffer[]} headers
- * @returns {string[]}
- */
-function parseRawHeaders (headers) {
- const headersLength = headers.length
- /**
- * @type {string[]}
- */
- const ret = new Array(headersLength)
-
- let key
- let val
-
- for (let n = 0; n < headersLength; n += 2) {
- key = headers[n]
- val = headers[n + 1]
-
- typeof key !== 'string' && (key = key.toString())
- typeof val !== 'string' && (val = val.toString('latin1'))
-
- ret[n] = key
- ret[n + 1] = val
- }
-
- return ret
-}
-
-/**
- * @param {string[]} headers
- * @param {Buffer[]} headers
- */
-function encodeRawHeaders (headers) {
- if (!Array.isArray(headers)) {
- throw new TypeError('expected headers to be an array')
- }
- return headers.map(x => Buffer.from(x))
-}
-
-/**
- * @param {*} buffer
- * @returns {buffer is Buffer}
- */
-function isBuffer (buffer) {
- // See, https://github.com/mcollina/undici/pull/319
- return buffer instanceof Uint8Array || Buffer.isBuffer(buffer)
-}
-
-/**
- * Asserts that the handler object is a request handler.
- *
- * @param {object} handler
- * @param {string} method
- * @param {string} [upgrade]
- * @returns {asserts handler is import('../api/api-request').RequestHandler}
- */
-function assertRequestHandler (handler, method, upgrade) {
- if (!handler || typeof handler !== 'object') {
- throw new InvalidArgumentError('handler must be an object')
- }
-
- if (typeof handler.onRequestStart === 'function') {
- // TODO (fix): More checks...
- return
- }
-
- if (typeof handler.onConnect !== 'function') {
- throw new InvalidArgumentError('invalid onConnect method')
- }
-
- if (typeof handler.onError !== 'function') {
- throw new InvalidArgumentError('invalid onError method')
- }
-
- if (typeof handler.onBodySent !== 'function' && handler.onBodySent !== undefined) {
- throw new InvalidArgumentError('invalid onBodySent method')
- }
-
- if (upgrade || method === 'CONNECT') {
- if (typeof handler.onUpgrade !== 'function') {
- throw new InvalidArgumentError('invalid onUpgrade method')
- }
- } else {
- if (typeof handler.onHeaders !== 'function') {
- throw new InvalidArgumentError('invalid onHeaders method')
- }
-
- if (typeof handler.onData !== 'function') {
- throw new InvalidArgumentError('invalid onData method')
- }
-
- if (typeof handler.onComplete !== 'function') {
- throw new InvalidArgumentError('invalid onComplete method')
- }
- }
-}
-
-/**
- * A body is disturbed if it has been read from and it cannot be re-used without
- * losing state or data.
- * @param {import('node:stream').Readable} body
- * @returns {boolean}
- */
-function isDisturbed (body) {
- // TODO (fix): Why is body[kBodyUsed] needed?
- return !!(body && (stream.isDisturbed(body) || body[kBodyUsed]))
-}
-
-/**
- * @typedef {object} SocketInfo
- * @property {string} [localAddress]
- * @property {number} [localPort]
- * @property {string} [remoteAddress]
- * @property {number} [remotePort]
- * @property {string} [remoteFamily]
- * @property {number} [timeout]
- * @property {number} bytesWritten
- * @property {number} bytesRead
- */
-
-/**
- * @param {import('net').Socket} socket
- * @returns {SocketInfo}
- */
-function getSocketInfo (socket) {
- return {
- localAddress: socket.localAddress,
- localPort: socket.localPort,
- remoteAddress: socket.remoteAddress,
- remotePort: socket.remotePort,
- remoteFamily: socket.remoteFamily,
- timeout: socket.timeout,
- bytesWritten: socket.bytesWritten,
- bytesRead: socket.bytesRead
- }
-}
-
-/**
- * @param {Iterable} iterable
- * @returns {ReadableStream}
- */
-function ReadableStreamFrom (iterable) {
- // We cannot use ReadableStream.from here because it does not return a byte stream.
-
- let iterator
- return new ReadableStream(
- {
- start () {
- iterator = iterable[Symbol.asyncIterator]()
- },
- pull (controller) {
- return iterator.next().then(({ done, value }) => {
- if (done) {
- return queueMicrotask(() => {
- controller.close()
- controller.byobRequest?.respond(0)
- })
- } else {
- const buf = Buffer.isBuffer(value) ? value : Buffer.from(value)
- if (buf.byteLength) {
- return controller.enqueue(new Uint8Array(buf))
- } else {
- return this.pull(controller)
- }
- }
- })
- },
- cancel () {
- return iterator.return()
- },
- type: 'bytes'
- }
- )
-}
-
-/**
- * The object should be a FormData instance and contains all the required
- * methods.
- * @param {*} object
- * @returns {object is FormData}
- */
-function isFormDataLike (object) {
- return (
- object &&
- typeof object === 'object' &&
- typeof object.append === 'function' &&
- typeof object.delete === 'function' &&
- typeof object.get === 'function' &&
- typeof object.getAll === 'function' &&
- typeof object.has === 'function' &&
- typeof object.set === 'function' &&
- object[Symbol.toStringTag] === 'FormData'
- )
-}
-
-function addAbortListener (signal, listener) {
- if ('addEventListener' in signal) {
- signal.addEventListener('abort', listener, { once: true })
- return () => signal.removeEventListener('abort', listener)
- }
- signal.once('abort', listener)
- return () => signal.removeListener('abort', listener)
-}
-
-const validTokenChars = new Uint8Array([
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
- 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32-47 (!"#$%&'()*+,-./)
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48-63 (0-9:;<=>?)
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64-79 (@A-O)
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80-95 (P-Z[\]^_)
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96-111 (`a-o)
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112-127 (p-z{|}~)
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128-143
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144-159
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-175
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176-191
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192-207
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208-223
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224-239
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240-255
-])
-
-/**
- * @see https://tools.ietf.org/html/rfc7230#section-3.2.6
- * @param {number} c
- * @returns {boolean}
- */
-function isTokenCharCode (c) {
- return (validTokenChars[c] === 1)
-}
-
-const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/
-
-/**
- * @param {string} characters
- * @returns {boolean}
- */
-function isValidHTTPToken (characters) {
- if (characters.length >= 12) return tokenRegExp.test(characters)
- if (characters.length === 0) return false
-
- for (let i = 0; i < characters.length; i++) {
- if (validTokenChars[characters.charCodeAt(i)] !== 1) {
- return false
- }
- }
- return true
-}
-
-// headerCharRegex have been lifted from
-// https://github.com/nodejs/node/blob/main/lib/_http_common.js
-
-/**
- * Matches if val contains an invalid field-vchar
- * field-value = *( field-content / obs-fold )
- * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
- * field-vchar = VCHAR / obs-text
- */
-const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/
-
-/**
- * @param {string} characters
- * @returns {boolean}
- */
-function isValidHeaderValue (characters) {
- return !headerCharRegex.test(characters)
-}
-
-const rangeHeaderRegex = /^bytes (\d+)-(\d+)\/(\d+)?$/
-
-/**
- * @typedef {object} RangeHeader
- * @property {number} start
- * @property {number | null} end
- * @property {number | null} size
- */
-
-/**
- * Parse accordingly to RFC 9110
- * @see https://www.rfc-editor.org/rfc/rfc9110#field.content-range
- * @param {string} [range]
- * @returns {RangeHeader|null}
- */
-function parseRangeHeader (range) {
- if (range == null || range === '') return { start: 0, end: null, size: null }
-
- const m = range ? range.match(rangeHeaderRegex) : null
- return m
- ? {
- start: parseInt(m[1]),
- end: m[2] ? parseInt(m[2]) : null,
- size: m[3] ? parseInt(m[3]) : null
- }
- : null
-}
-
-/**
- * @template {import("events").EventEmitter} T
- * @param {T} obj
- * @param {string} name
- * @param {(...args: any[]) => void} listener
- * @returns {T}
- */
-function addListener (obj, name, listener) {
- const listeners = (obj[kListeners] ??= [])
- listeners.push([name, listener])
- obj.on(name, listener)
- return obj
-}
-
-/**
- * @template {import("events").EventEmitter} T
- * @param {T} obj
- * @returns {T}
- */
-function removeAllListeners (obj) {
- if (obj[kListeners] != null) {
- for (const [name, listener] of obj[kListeners]) {
- obj.removeListener(name, listener)
- }
- obj[kListeners] = null
- }
- return obj
-}
-
-/**
- * @param {import ('../dispatcher/client')} client
- * @param {import ('../core/request')} request
- * @param {Error} err
- */
-function errorRequest (client, request, err) {
- try {
- request.onError(err)
- assert(request.aborted)
- } catch (err) {
- client.emit('error', err)
- }
-}
-
-/**
- * @param {WeakRef<net.Socket>} socketWeakRef
- * @param {object} opts
- * @param {number} opts.timeout
- * @param {string} opts.hostname
- * @param {number} opts.port
- * @returns {() => void}
- */
-const setupConnectTimeout = process.platform === 'win32'
- ? (socketWeakRef, opts) => {
- if (!opts.timeout) {
- return noop
- }
-
- let s1 = null
- let s2 = null
- const fastTimer = timers.setFastTimeout(() => {
- // setImmediate is added to make sure that we prioritize socket error events over timeouts
- s1 = setImmediate(() => {
- // Windows needs an extra setImmediate probably due to implementation differences in the socket logic
- s2 = setImmediate(() => onConnectTimeout(socketWeakRef.deref(), opts))
- })
- }, opts.timeout)
- return () => {
- timers.clearFastTimeout(fastTimer)
- clearImmediate(s1)
- clearImmediate(s2)
- }
- }
- : (socketWeakRef, opts) => {
- if (!opts.timeout) {
- return noop
- }
-
- let s1 = null
- const fastTimer = timers.setFastTimeout(() => {
- // setImmediate is added to make sure that we prioritize socket error events over timeouts
- s1 = setImmediate(() => {
- onConnectTimeout(socketWeakRef.deref(), opts)
- })
- }, opts.timeout)
- return () => {
- timers.clearFastTimeout(fastTimer)
- clearImmediate(s1)
- }
- }
-
-/**
- * @param {net.Socket} socket
- * @param {object} opts
- * @param {number} opts.timeout
- * @param {string} opts.hostname
- * @param {number} opts.port
- */
-function onConnectTimeout (socket, opts) {
- // The socket could be already garbage collected
- if (socket == null) {
- return
- }
-
- let message = 'Connect Timeout Error'
- if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
- message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')},`
- } else {
- message += ` (attempted address: ${opts.hostname}:${opts.port},`
- }
-
- message += ` timeout: ${opts.timeout}ms)`
-
- destroy(socket, new ConnectTimeoutError(message))
-}
-
-/**
- * @param {string} urlString
- * @returns {string}
- */
-function getProtocolFromUrlString (urlString) {
- if (
- urlString[0] === 'h' &&
- urlString[1] === 't' &&
- urlString[2] === 't' &&
- urlString[3] === 'p'
- ) {
- switch (urlString[4]) {
- case ':':
- return 'http:'
- case 's':
- if (urlString[5] === ':') {
- return 'https:'
- }
- }
- }
- // fallback if none of the usual suspects
- return urlString.slice(0, urlString.indexOf(':') + 1)
-}
-
-const kEnumerableProperty = Object.create(null)
-kEnumerableProperty.enumerable = true
-
-const normalizedMethodRecordsBase = {
- delete: 'DELETE',
- DELETE: 'DELETE',
- get: 'GET',
- GET: 'GET',
- head: 'HEAD',
- HEAD: 'HEAD',
- options: 'OPTIONS',
- OPTIONS: 'OPTIONS',
- post: 'POST',
- POST: 'POST',
- put: 'PUT',
- PUT: 'PUT'
-}
-
-const normalizedMethodRecords = {
- ...normalizedMethodRecordsBase,
- patch: 'patch',
- PATCH: 'PATCH'
-}
-
-// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.
-Object.setPrototypeOf(normalizedMethodRecordsBase, null)
-Object.setPrototypeOf(normalizedMethodRecords, null)
-
-module.exports = {
- kEnumerableProperty,
- isDisturbed,
- isBlobLike,
- parseOrigin,
- parseURL,
- getServerName,
- isStream,
- isIterable,
- isAsyncIterable,
- isDestroyed,
- headerNameToString,
- bufferToLowerCasedHeaderName,
- addListener,
- removeAllListeners,
- errorRequest,
- parseRawHeaders,
- encodeRawHeaders,
- parseHeaders,
- parseKeepAliveTimeout,
- destroy,
- bodyLength,
- deepClone,
- ReadableStreamFrom,
- isBuffer,
- assertRequestHandler,
- getSocketInfo,
- isFormDataLike,
- pathHasQueryOrFragment,
- serializePathWithQuery,
- addAbortListener,
- isValidHTTPToken,
- isValidHeaderValue,
- isTokenCharCode,
- parseRangeHeader,
- normalizedMethodRecordsBase,
- normalizedMethodRecords,
- isValidPort,
- isHttpOrHttpsPrefixed,
- nodeMajor,
- nodeMinor,
- safeHTTPMethods: Object.freeze(['GET', 'HEAD', 'OPTIONS', 'TRACE']),
- wrapRequestBody,
- setupConnectTimeout,
- getProtocolFromUrlString
-}
diff --git a/vanilla/node_modules/undici/lib/dispatcher/agent.js b/vanilla/node_modules/undici/lib/dispatcher/agent.js
deleted file mode 100644
index 939b0ad..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/agent.js
+++ /dev/null
@@ -1,158 +0,0 @@
-'use strict'
-
-const { InvalidArgumentError, MaxOriginsReachedError } = require('../core/errors')
-const { kClients, kRunning, kClose, kDestroy, kDispatch, kUrl } = require('../core/symbols')
-const DispatcherBase = require('./dispatcher-base')
-const Pool = require('./pool')
-const Client = require('./client')
-const util = require('../core/util')
-
-const kOnConnect = Symbol('onConnect')
-const kOnDisconnect = Symbol('onDisconnect')
-const kOnConnectionError = Symbol('onConnectionError')
-const kOnDrain = Symbol('onDrain')
-const kFactory = Symbol('factory')
-const kOptions = Symbol('options')
-const kOrigins = Symbol('origins')
-
-function defaultFactory (origin, opts) {
- return opts && opts.connections === 1
- ? new Client(origin, opts)
- : new Pool(origin, opts)
-}
-
-class Agent extends DispatcherBase {
- constructor ({ factory = defaultFactory, maxOrigins = Infinity, connect, ...options } = {}) {
- if (typeof factory !== 'function') {
- throw new InvalidArgumentError('factory must be a function.')
- }
-
- if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {
- throw new InvalidArgumentError('connect must be a function or an object')
- }
-
- if (typeof maxOrigins !== 'number' || Number.isNaN(maxOrigins) || maxOrigins <= 0) {
- throw new InvalidArgumentError('maxOrigins must be a number greater than 0')
- }
-
- super()
-
- if (connect && typeof connect !== 'function') {
- connect = { ...connect }
- }
-
- this[kOptions] = { ...util.deepClone(options), maxOrigins, connect }
- this[kFactory] = factory
- this[kClients] = new Map()
- this[kOrigins] = new Set()
-
- this[kOnDrain] = (origin, targets) => {
- this.emit('drain', origin, [this, ...targets])
- }
-
- this[kOnConnect] = (origin, targets) => {
- this.emit('connect', origin, [this, ...targets])
- }
-
- this[kOnDisconnect] = (origin, targets, err) => {
- this.emit('disconnect', origin, [this, ...targets], err)
- }
-
- this[kOnConnectionError] = (origin, targets, err) => {
- this.emit('connectionError', origin, [this, ...targets], err)
- }
- }
-
- get [kRunning] () {
- let ret = 0
- for (const { dispatcher } of this[kClients].values()) {
- ret += dispatcher[kRunning]
- }
- return ret
- }
-
- [kDispatch] (opts, handler) {
- let key
- if (opts.origin && (typeof opts.origin === 'string' || opts.origin instanceof URL)) {
- key = String(opts.origin)
- } else {
- throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.')
- }
-
- if (this[kOrigins].size >= this[kOptions].maxOrigins && !this[kOrigins].has(key)) {
- throw new MaxOriginsReachedError()
- }
-
- const result = this[kClients].get(key)
- let dispatcher = result && result.dispatcher
- if (!dispatcher) {
- const closeClientIfUnused = (connected) => {
- const result = this[kClients].get(key)
- if (result) {
- if (connected) result.count -= 1
- if (result.count <= 0) {
- this[kClients].delete(key)
- if (!result.dispatcher.destroyed) {
- result.dispatcher.close()
- }
- }
- this[kOrigins].delete(key)
- }
- }
- dispatcher = this[kFactory](opts.origin, this[kOptions])
- .on('drain', this[kOnDrain])
- .on('connect', (origin, targets) => {
- const result = this[kClients].get(key)
- if (result) {
- result.count += 1
- }
- this[kOnConnect](origin, targets)
- })
- .on('disconnect', (origin, targets, err) => {
- closeClientIfUnused(true)
- this[kOnDisconnect](origin, targets, err)
- })
- .on('connectionError', (origin, targets, err) => {
- closeClientIfUnused(false)
- this[kOnConnectionError](origin, targets, err)
- })
-
- this[kClients].set(key, { count: 0, dispatcher })
- this[kOrigins].add(key)
- }
-
- return dispatcher.dispatch(opts, handler)
- }
-
- [kClose] () {
- const closePromises = []
- for (const { dispatcher } of this[kClients].values()) {
- closePromises.push(dispatcher.close())
- }
- this[kClients].clear()
-
- return Promise.all(closePromises)
- }
-
- [kDestroy] (err) {
- const destroyPromises = []
- for (const { dispatcher } of this[kClients].values()) {
- destroyPromises.push(dispatcher.destroy(err))
- }
- this[kClients].clear()
-
- return Promise.all(destroyPromises)
- }
-
- get stats () {
- const allClientStats = {}
- for (const { dispatcher } of this[kClients].values()) {
- if (dispatcher.stats) {
- allClientStats[dispatcher[kUrl].origin] = dispatcher.stats
- }
- }
- return allClientStats
- }
-}
-
-module.exports = Agent
diff --git a/vanilla/node_modules/undici/lib/dispatcher/balanced-pool.js b/vanilla/node_modules/undici/lib/dispatcher/balanced-pool.js
deleted file mode 100644
index d53a269..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/balanced-pool.js
+++ /dev/null
@@ -1,216 +0,0 @@
-'use strict'
-
-const {
- BalancedPoolMissingUpstreamError,
- InvalidArgumentError
-} = require('../core/errors')
-const {
- PoolBase,
- kClients,
- kNeedDrain,
- kAddClient,
- kRemoveClient,
- kGetDispatcher
-} = require('./pool-base')
-const Pool = require('./pool')
-const { kUrl } = require('../core/symbols')
-const { parseOrigin } = require('../core/util')
-const kFactory = Symbol('factory')
-
-const kOptions = Symbol('options')
-const kGreatestCommonDivisor = Symbol('kGreatestCommonDivisor')
-const kCurrentWeight = Symbol('kCurrentWeight')
-const kIndex = Symbol('kIndex')
-const kWeight = Symbol('kWeight')
-const kMaxWeightPerServer = Symbol('kMaxWeightPerServer')
-const kErrorPenalty = Symbol('kErrorPenalty')
-
-/**
- * Calculate the greatest common divisor of two numbers by
- * using the Euclidean algorithm.
- *
- * @param {number} a
- * @param {number} b
- * @returns {number}
- */
-function getGreatestCommonDivisor (a, b) {
- if (a === 0) return b
-
- while (b !== 0) {
- const t = b
- b = a % b
- a = t
- }
- return a
-}
-
-function defaultFactory (origin, opts) {
- return new Pool(origin, opts)
-}
-
-class BalancedPool extends PoolBase {
- constructor (upstreams = [], { factory = defaultFactory, ...opts } = {}) {
- if (typeof factory !== 'function') {
- throw new InvalidArgumentError('factory must be a function.')
- }
-
- super()
-
- this[kOptions] = opts
- this[kIndex] = -1
- this[kCurrentWeight] = 0
-
- this[kMaxWeightPerServer] = this[kOptions].maxWeightPerServer || 100
- this[kErrorPenalty] = this[kOptions].errorPenalty || 15
-
- if (!Array.isArray(upstreams)) {
- upstreams = [upstreams]
- }
-
- this[kFactory] = factory
-
- for (const upstream of upstreams) {
- this.addUpstream(upstream)
- }
- this._updateBalancedPoolStats()
- }
-
- addUpstream (upstream) {
- const upstreamOrigin = parseOrigin(upstream).origin
-
- if (this[kClients].find((pool) => (
- pool[kUrl].origin === upstreamOrigin &&
- pool.closed !== true &&
- pool.destroyed !== true
- ))) {
- return this
- }
- const pool = this[kFactory](upstreamOrigin, Object.assign({}, this[kOptions]))
-
- this[kAddClient](pool)
- pool.on('connect', () => {
- pool[kWeight] = Math.min(this[kMaxWeightPerServer], pool[kWeight] + this[kErrorPenalty])
- })
-
- pool.on('connectionError', () => {
- pool[kWeight] = Math.max(1, pool[kWeight] - this[kErrorPenalty])
- this._updateBalancedPoolStats()
- })
-
- pool.on('disconnect', (...args) => {
- const err = args[2]
- if (err && err.code === 'UND_ERR_SOCKET') {
- // decrease the weight of the pool.
- pool[kWeight] = Math.max(1, pool[kWeight] - this[kErrorPenalty])
- this._updateBalancedPoolStats()
- }
- })
-
- for (const client of this[kClients]) {
- client[kWeight] = this[kMaxWeightPerServer]
- }
-
- this._updateBalancedPoolStats()
-
- return this
- }
-
- _updateBalancedPoolStats () {
- let result = 0
- for (let i = 0; i < this[kClients].length; i++) {
- result = getGreatestCommonDivisor(this[kClients][i][kWeight], result)
- }
-
- this[kGreatestCommonDivisor] = result
- }
-
- removeUpstream (upstream) {
- const upstreamOrigin = parseOrigin(upstream).origin
-
- const pool = this[kClients].find((pool) => (
- pool[kUrl].origin === upstreamOrigin &&
- pool.closed !== true &&
- pool.destroyed !== true
- ))
-
- if (pool) {
- this[kRemoveClient](pool)
- }
-
- return this
- }
-
- getUpstream (upstream) {
- const upstreamOrigin = parseOrigin(upstream).origin
-
- return this[kClients].find((pool) => (
- pool[kUrl].origin === upstreamOrigin &&
- pool.closed !== true &&
- pool.destroyed !== true
- ))
- }
-
- get upstreams () {
- return this[kClients]
- .filter(dispatcher => dispatcher.closed !== true && dispatcher.destroyed !== true)
- .map((p) => p[kUrl].origin)
- }
-
- [kGetDispatcher] () {
- // We validate that pools is greater than 0,
- // otherwise we would have to wait until an upstream
- // is added, which might never happen.
- if (this[kClients].length === 0) {
- throw new BalancedPoolMissingUpstreamError()
- }
-
- const dispatcher = this[kClients].find(dispatcher => (
- !dispatcher[kNeedDrain] &&
- dispatcher.closed !== true &&
- dispatcher.destroyed !== true
- ))
-
- if (!dispatcher) {
- return
- }
-
- const allClientsBusy = this[kClients].map(pool => pool[kNeedDrain]).reduce((a, b) => a && b, true)
-
- if (allClientsBusy) {
- return
- }
-
- let counter = 0
-
- let maxWeightIndex = this[kClients].findIndex(pool => !pool[kNeedDrain])
-
- while (counter++ < this[kClients].length) {
- this[kIndex] = (this[kIndex] + 1) % this[kClients].length
- const pool = this[kClients][this[kIndex]]
-
- // find pool index with the largest weight
- if (pool[kWeight] > this[kClients][maxWeightIndex][kWeight] && !pool[kNeedDrain]) {
- maxWeightIndex = this[kIndex]
- }
-
- // decrease the current weight every `this[kClients].length`.
- if (this[kIndex] === 0) {
- // Set the current weight to the next lower weight.
- this[kCurrentWeight] = this[kCurrentWeight] - this[kGreatestCommonDivisor]
-
- if (this[kCurrentWeight] <= 0) {
- this[kCurrentWeight] = this[kMaxWeightPerServer]
- }
- }
- if (pool[kWeight] >= this[kCurrentWeight] && (!pool[kNeedDrain])) {
- return pool
- }
- }
-
- this[kCurrentWeight] = this[kClients][maxWeightIndex][kWeight]
- this[kIndex] = maxWeightIndex
- return this[kClients][maxWeightIndex]
- }
-}
-
-module.exports = BalancedPool
diff --git a/vanilla/node_modules/undici/lib/dispatcher/client-h1.js b/vanilla/node_modules/undici/lib/dispatcher/client-h1.js
deleted file mode 100644
index ce6b4ee..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/client-h1.js
+++ /dev/null
@@ -1,1606 +0,0 @@
-'use strict'
-
-/* global WebAssembly */
-
-const assert = require('node:assert')
-const util = require('../core/util.js')
-const { channels } = require('../core/diagnostics.js')
-const timers = require('../util/timers.js')
-const {
- RequestContentLengthMismatchError,
- ResponseContentLengthMismatchError,
- RequestAbortedError,
- HeadersTimeoutError,
- HeadersOverflowError,
- SocketError,
- InformationalError,
- BodyTimeoutError,
- HTTPParserError,
- ResponseExceededMaxSizeError
-} = require('../core/errors.js')
-const {
- kUrl,
- kReset,
- kClient,
- kParser,
- kBlocking,
- kRunning,
- kPending,
- kSize,
- kWriting,
- kQueue,
- kNoRef,
- kKeepAliveDefaultTimeout,
- kHostHeader,
- kPendingIdx,
- kRunningIdx,
- kError,
- kPipelining,
- kSocket,
- kKeepAliveTimeoutValue,
- kMaxHeadersSize,
- kKeepAliveMaxTimeout,
- kKeepAliveTimeoutThreshold,
- kHeadersTimeout,
- kBodyTimeout,
- kStrictContentLength,
- kMaxRequests,
- kCounter,
- kMaxResponseSize,
- kOnError,
- kResume,
- kHTTPContext,
- kClosed
-} = require('../core/symbols.js')
-
-const constants = require('../llhttp/constants.js')
-const EMPTY_BUF = Buffer.alloc(0)
-const FastBuffer = Buffer[Symbol.species]
-const removeAllListeners = util.removeAllListeners
-
-let extractBody
-
-function lazyllhttp () {
- const llhttpWasmData = process.env.JEST_WORKER_ID ? require('../llhttp/llhttp-wasm.js') : undefined
-
- let mod
-
- // We disable wasm SIMD on ppc64 as it seems to be broken on Power 9 architectures.
- let useWasmSIMD = process.arch !== 'ppc64'
- // The Env Variable UNDICI_NO_WASM_SIMD allows explicitly overriding the default behavior
- if (process.env.UNDICI_NO_WASM_SIMD === '1') {
- useWasmSIMD = true
- } else if (process.env.UNDICI_NO_WASM_SIMD === '0') {
- useWasmSIMD = false
- }
-
- if (useWasmSIMD) {
- try {
- mod = new WebAssembly.Module(require('../llhttp/llhttp_simd-wasm.js'))
- } catch {
- }
- }
-
- if (!mod) {
- // We could check if the error was caused by the simd option not
- // being enabled, but the occurring of this other error
- // * https://github.com/emscripten-core/emscripten/issues/11495
- // got me to remove that check to avoid breaking Node 12.
- mod = new WebAssembly.Module(llhttpWasmData || require('../llhttp/llhttp-wasm.js'))
- }
-
- return new WebAssembly.Instance(mod, {
- env: {
- /**
- * @param {number} p
- * @param {number} at
- * @param {number} len
- * @returns {number}
- */
- wasm_on_url: (p, at, len) => {
- return 0
- },
- /**
- * @param {number} p
- * @param {number} at
- * @param {number} len
- * @returns {number}
- */
- wasm_on_status: (p, at, len) => {
- assert(currentParser.ptr === p)
- const start = at - currentBufferPtr + currentBufferRef.byteOffset
- return currentParser.onStatus(new FastBuffer(currentBufferRef.buffer, start, len))
- },
- /**
- * @param {number} p
- * @returns {number}
- */
- wasm_on_message_begin: (p) => {
- assert(currentParser.ptr === p)
- return currentParser.onMessageBegin()
- },
- /**
- * @param {number} p
- * @param {number} at
- * @param {number} len
- * @returns {number}
- */
- wasm_on_header_field: (p, at, len) => {
- assert(currentParser.ptr === p)
- const start = at - currentBufferPtr + currentBufferRef.byteOffset
- return currentParser.onHeaderField(new FastBuffer(currentBufferRef.buffer, start, len))
- },
- /**
- * @param {number} p
- * @param {number} at
- * @param {number} len
- * @returns {number}
- */
- wasm_on_header_value: (p, at, len) => {
- assert(currentParser.ptr === p)
- const start = at - currentBufferPtr + currentBufferRef.byteOffset
- return currentParser.onHeaderValue(new FastBuffer(currentBufferRef.buffer, start, len))
- },
- /**
- * @param {number} p
- * @param {number} statusCode
- * @param {0|1} upgrade
- * @param {0|1} shouldKeepAlive
- * @returns {number}
- */
- wasm_on_headers_complete: (p, statusCode, upgrade, shouldKeepAlive) => {
- assert(currentParser.ptr === p)
- return currentParser.onHeadersComplete(statusCode, upgrade === 1, shouldKeepAlive === 1)
- },
- /**
- * @param {number} p
- * @param {number} at
- * @param {number} len
- * @returns {number}
- */
- wasm_on_body: (p, at, len) => {
- assert(currentParser.ptr === p)
- const start = at - currentBufferPtr + currentBufferRef.byteOffset
- return currentParser.onBody(new FastBuffer(currentBufferRef.buffer, start, len))
- },
- /**
- * @param {number} p
- * @returns {number}
- */
- wasm_on_message_complete: (p) => {
- assert(currentParser.ptr === p)
- return currentParser.onMessageComplete()
- }
-
- }
- })
-}
-
-let llhttpInstance = null
-
-/**
- * @type {Parser|null}
- */
-let currentParser = null
-let currentBufferRef = null
-/**
- * @type {number}
- */
-let currentBufferSize = 0
-let currentBufferPtr = null
-
-const USE_NATIVE_TIMER = 0
-const USE_FAST_TIMER = 1
-
-// Use fast timers for headers and body to take eventual event loop
-// latency into account.
-const TIMEOUT_HEADERS = 2 | USE_FAST_TIMER
-const TIMEOUT_BODY = 4 | USE_FAST_TIMER
-
-// Use native timers to ignore event loop latency for keep-alive
-// handling.
-const TIMEOUT_KEEP_ALIVE = 8 | USE_NATIVE_TIMER
-
-class Parser {
- /**
- * @param {import('./client.js')} client
- * @param {import('net').Socket} socket
- * @param {*} llhttp
- */
- constructor (client, socket, { exports }) {
- this.llhttp = exports
- this.ptr = this.llhttp.llhttp_alloc(constants.TYPE.RESPONSE)
- this.client = client
- /**
- * @type {import('net').Socket}
- */
- this.socket = socket
- this.timeout = null
- this.timeoutValue = null
- this.timeoutType = null
- this.statusCode = 0
- this.statusText = ''
- this.upgrade = false
- this.headers = []
- this.headersSize = 0
- this.headersMaxSize = client[kMaxHeadersSize]
- this.shouldKeepAlive = false
- this.paused = false
- this.resume = this.resume.bind(this)
-
- this.bytesRead = 0
-
- this.keepAlive = ''
- this.contentLength = ''
- this.connection = ''
- this.maxResponseSize = client[kMaxResponseSize]
- }
-
- setTimeout (delay, type) {
- // If the existing timer and the new timer are of different timer type
- // (fast or native) or have different delay, we need to clear the existing
- // timer and set a new one.
- if (
- delay !== this.timeoutValue ||
- (type & USE_FAST_TIMER) ^ (this.timeoutType & USE_FAST_TIMER)
- ) {
- // If a timeout is already set, clear it with clearTimeout of the fast
- // timer implementation, as it can clear fast and native timers.
- if (this.timeout) {
- timers.clearTimeout(this.timeout)
- this.timeout = null
- }
-
- if (delay) {
- if (type & USE_FAST_TIMER) {
- this.timeout = timers.setFastTimeout(onParserTimeout, delay, new WeakRef(this))
- } else {
- this.timeout = setTimeout(onParserTimeout, delay, new WeakRef(this))
- this.timeout?.unref()
- }
- }
-
- this.timeoutValue = delay
- } else if (this.timeout) {
- if (this.timeout.refresh) {
- this.timeout.refresh()
- }
- }
-
- this.timeoutType = type
- }
-
- resume () {
- if (this.socket.destroyed || !this.paused) {
- return
- }
-
- assert(this.ptr != null)
- assert(currentParser === null)
-
- this.llhttp.llhttp_resume(this.ptr)
-
- assert(this.timeoutType === TIMEOUT_BODY)
- if (this.timeout) {
- if (this.timeout.refresh) {
- this.timeout.refresh()
- }
- }
-
- this.paused = false
- this.execute(this.socket.read() || EMPTY_BUF) // Flush parser.
- this.readMore()
- }
-
- readMore () {
- while (!this.paused && this.ptr) {
- const chunk = this.socket.read()
- if (chunk === null) {
- break
- }
- this.execute(chunk)
- }
- }
-
- /**
- * @param {Buffer} chunk
- */
- execute (chunk) {
- assert(currentParser === null)
- assert(this.ptr != null)
- assert(!this.paused)
-
- const { socket, llhttp } = this
-
- // Allocate a new buffer if the current buffer is too small.
- if (chunk.length > currentBufferSize) {
- if (currentBufferPtr) {
- llhttp.free(currentBufferPtr)
- }
- // Allocate a buffer that is a multiple of 4096 bytes.
- currentBufferSize = Math.ceil(chunk.length / 4096) * 4096
- currentBufferPtr = llhttp.malloc(currentBufferSize)
- }
-
- new Uint8Array(llhttp.memory.buffer, currentBufferPtr, currentBufferSize).set(chunk)
-
- // Call `execute` on the wasm parser.
- // We pass the `llhttp_parser` pointer address, the pointer address of buffer view data,
- // and finally the length of bytes to parse.
- // The return value is an error code or `constants.ERROR.OK`.
- try {
- let ret
-
- try {
- currentBufferRef = chunk
- currentParser = this
- ret = llhttp.llhttp_execute(this.ptr, currentBufferPtr, chunk.length)
- } finally {
- currentParser = null
- currentBufferRef = null
- }
-
- if (ret !== constants.ERROR.OK) {
- const data = chunk.subarray(llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr)
-
- if (ret === constants.ERROR.PAUSED_UPGRADE) {
- this.onUpgrade(data)
- } else if (ret === constants.ERROR.PAUSED) {
- this.paused = true
- socket.unshift(data)
- } else {
- const ptr = llhttp.llhttp_get_error_reason(this.ptr)
- let message = ''
- if (ptr) {
- const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
- message =
- 'Response does not match the HTTP/1.1 protocol (' +
- Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
- ')'
- }
- throw new HTTPParserError(message, constants.ERROR[ret], data)
- }
- }
- } catch (err) {
- util.destroy(socket, err)
- }
- }
-
- destroy () {
- assert(currentParser === null)
- assert(this.ptr != null)
-
- this.llhttp.llhttp_free(this.ptr)
- this.ptr = null
-
- this.timeout && timers.clearTimeout(this.timeout)
- this.timeout = null
- this.timeoutValue = null
- this.timeoutType = null
-
- this.paused = false
- }
-
- /**
- * @param {Buffer} buf
- * @returns {0}
- */
- onStatus (buf) {
- this.statusText = buf.toString()
- return 0
- }
-
- /**
- * @returns {0|-1}
- */
- onMessageBegin () {
- const { socket, client } = this
-
- if (socket.destroyed) {
- return -1
- }
-
- const request = client[kQueue][client[kRunningIdx]]
- if (!request) {
- return -1
- }
- request.onResponseStarted()
-
- return 0
- }
-
- /**
- * @param {Buffer} buf
- * @returns {number}
- */
- onHeaderField (buf) {
- const len = this.headers.length
-
- if ((len & 1) === 0) {
- this.headers.push(buf)
- } else {
- this.headers[len - 1] = Buffer.concat([this.headers[len - 1], buf])
- }
-
- this.trackHeader(buf.length)
-
- return 0
- }
-
- /**
- * @param {Buffer} buf
- * @returns {number}
- */
- onHeaderValue (buf) {
- let len = this.headers.length
-
- if ((len & 1) === 1) {
- this.headers.push(buf)
- len += 1
- } else {
- this.headers[len - 1] = Buffer.concat([this.headers[len - 1], buf])
- }
-
- const key = this.headers[len - 2]
- if (key.length === 10) {
- const headerName = util.bufferToLowerCasedHeaderName(key)
- if (headerName === 'keep-alive') {
- this.keepAlive += buf.toString()
- } else if (headerName === 'connection') {
- this.connection += buf.toString()
- }
- } else if (key.length === 14 && util.bufferToLowerCasedHeaderName(key) === 'content-length') {
- this.contentLength += buf.toString()
- }
-
- this.trackHeader(buf.length)
-
- return 0
- }
-
- /**
- * @param {number} len
- */
- trackHeader (len) {
- this.headersSize += len
- if (this.headersSize >= this.headersMaxSize) {
- util.destroy(this.socket, new HeadersOverflowError())
- }
- }
-
- /**
- * @param {Buffer} head
- */
- onUpgrade (head) {
- const { upgrade, client, socket, headers, statusCode } = this
-
- assert(upgrade)
- assert(client[kSocket] === socket)
- assert(!socket.destroyed)
- assert(!this.paused)
- assert((headers.length & 1) === 0)
-
- const request = client[kQueue][client[kRunningIdx]]
- assert(request)
- assert(request.upgrade || request.method === 'CONNECT')
-
- this.statusCode = 0
- this.statusText = ''
- this.shouldKeepAlive = false
-
- this.headers = []
- this.headersSize = 0
-
- socket.unshift(head)
-
- socket[kParser].destroy()
- socket[kParser] = null
-
- socket[kClient] = null
- socket[kError] = null
-
- removeAllListeners(socket)
-
- client[kSocket] = null
- client[kHTTPContext] = null // TODO (fix): This is hacky...
- client[kQueue][client[kRunningIdx]++] = null
- client.emit('disconnect', client[kUrl], [client], new InformationalError('upgrade'))
-
- try {
- request.onUpgrade(statusCode, headers, socket)
- } catch (err) {
- util.destroy(socket, err)
- }
-
- client[kResume]()
- }
-
- /**
- * @param {number} statusCode
- * @param {boolean} upgrade
- * @param {boolean} shouldKeepAlive
- * @returns {number}
- */
- onHeadersComplete (statusCode, upgrade, shouldKeepAlive) {
- const { client, socket, headers, statusText } = this
-
- if (socket.destroyed) {
- return -1
- }
-
- const request = client[kQueue][client[kRunningIdx]]
-
- if (!request) {
- return -1
- }
-
- assert(!this.upgrade)
- assert(this.statusCode < 200)
-
- if (statusCode === 100) {
- util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
- return -1
- }
-
- /* this can only happen if server is misbehaving */
- if (upgrade && !request.upgrade) {
- util.destroy(socket, new SocketError('bad upgrade', util.getSocketInfo(socket)))
- return -1
- }
-
- assert(this.timeoutType === TIMEOUT_HEADERS)
-
- this.statusCode = statusCode
- this.shouldKeepAlive = (
- shouldKeepAlive ||
- // Override llhttp value which does not allow keepAlive for HEAD.
- (request.method === 'HEAD' && !socket[kReset] && this.connection.toLowerCase() === 'keep-alive')
- )
-
- if (this.statusCode >= 200) {
- const bodyTimeout = request.bodyTimeout != null
- ? request.bodyTimeout
- : client[kBodyTimeout]
- this.setTimeout(bodyTimeout, TIMEOUT_BODY)
- } else if (this.timeout) {
- if (this.timeout.refresh) {
- this.timeout.refresh()
- }
- }
-
- if (request.method === 'CONNECT') {
- assert(client[kRunning] === 1)
- this.upgrade = true
- return 2
- }
-
- if (upgrade) {
- assert(client[kRunning] === 1)
- this.upgrade = true
- return 2
- }
-
- assert((this.headers.length & 1) === 0)
- this.headers = []
- this.headersSize = 0
-
- if (this.shouldKeepAlive && client[kPipelining]) {
- const keepAliveTimeout = this.keepAlive ? util.parseKeepAliveTimeout(this.keepAlive) : null
-
- if (keepAliveTimeout != null) {
- const timeout = Math.min(
- keepAliveTimeout - client[kKeepAliveTimeoutThreshold],
- client[kKeepAliveMaxTimeout]
- )
- if (timeout <= 0) {
- socket[kReset] = true
- } else {
- client[kKeepAliveTimeoutValue] = timeout
- }
- } else {
- client[kKeepAliveTimeoutValue] = client[kKeepAliveDefaultTimeout]
- }
- } else {
- // Stop more requests from being dispatched.
- socket[kReset] = true
- }
-
- const pause = request.onHeaders(statusCode, headers, this.resume, statusText) === false
-
- if (request.aborted) {
- return -1
- }
-
- if (request.method === 'HEAD') {
- return 1
- }
-
- if (statusCode < 200) {
- return 1
- }
-
- if (socket[kBlocking]) {
- socket[kBlocking] = false
- client[kResume]()
- }
-
- return pause ? constants.ERROR.PAUSED : 0
- }
-
- /**
- * @param {Buffer} buf
- * @returns {number}
- */
- onBody (buf) {
- const { client, socket, statusCode, maxResponseSize } = this
-
- if (socket.destroyed) {
- return -1
- }
-
- const request = client[kQueue][client[kRunningIdx]]
- assert(request)
-
- assert(this.timeoutType === TIMEOUT_BODY)
- if (this.timeout) {
- if (this.timeout.refresh) {
- this.timeout.refresh()
- }
- }
-
- assert(statusCode >= 200)
-
- if (maxResponseSize > -1 && this.bytesRead + buf.length > maxResponseSize) {
- util.destroy(socket, new ResponseExceededMaxSizeError())
- return -1
- }
-
- this.bytesRead += buf.length
-
- if (request.onData(buf) === false) {
- return constants.ERROR.PAUSED
- }
-
- return 0
- }
-
- /**
- * @returns {number}
- */
- onMessageComplete () {
- const { client, socket, statusCode, upgrade, headers, contentLength, bytesRead, shouldKeepAlive } = this
-
- if (socket.destroyed && (!statusCode || shouldKeepAlive)) {
- return -1
- }
-
- if (upgrade) {
- return 0
- }
-
- assert(statusCode >= 100)
- assert((this.headers.length & 1) === 0)
-
- const request = client[kQueue][client[kRunningIdx]]
- assert(request)
-
- this.statusCode = 0
- this.statusText = ''
- this.bytesRead = 0
- this.contentLength = ''
- this.keepAlive = ''
- this.connection = ''
-
- this.headers = []
- this.headersSize = 0
-
- if (statusCode < 200) {
- return 0
- }
-
- if (request.method !== 'HEAD' && contentLength && bytesRead !== parseInt(contentLength, 10)) {
- util.destroy(socket, new ResponseContentLengthMismatchError())
- return -1
- }
-
- request.onComplete(headers)
-
- client[kQueue][client[kRunningIdx]++] = null
-
- if (socket[kWriting]) {
- assert(client[kRunning] === 0)
- // Response completed before request.
- util.destroy(socket, new InformationalError('reset'))
- return constants.ERROR.PAUSED
- } else if (!shouldKeepAlive) {
- util.destroy(socket, new InformationalError('reset'))
- return constants.ERROR.PAUSED
- } else if (socket[kReset] && client[kRunning] === 0) {
- // Destroy socket once all requests have completed.
- // The request at the tail of the pipeline is the one
- // that requested reset and no further requests should
- // have been queued since then.
- util.destroy(socket, new InformationalError('reset'))
- return constants.ERROR.PAUSED
- } else if (client[kPipelining] == null || client[kPipelining] === 1) {
- // We must wait a full event loop cycle to reuse this socket to make sure
- // that non-spec compliant servers are not closing the connection even if they
- // said they won't.
- setImmediate(client[kResume])
- } else {
- client[kResume]()
- }
-
- return 0
- }
-}
-
-function onParserTimeout (parserWeakRef) {
- const parser = parserWeakRef.deref()
- if (!parser) {
- return
- }
-
- const { socket, timeoutType, client, paused } = parser
-
- if (timeoutType === TIMEOUT_HEADERS) {
- if (!socket[kWriting] || socket.writableNeedDrain || client[kRunning] > 1) {
- assert(!paused, 'cannot be paused while waiting for headers')
- util.destroy(socket, new HeadersTimeoutError())
- }
- } else if (timeoutType === TIMEOUT_BODY) {
- if (!paused) {
- util.destroy(socket, new BodyTimeoutError())
- }
- } else if (timeoutType === TIMEOUT_KEEP_ALIVE) {
- assert(client[kRunning] === 0 && client[kKeepAliveTimeoutValue])
- util.destroy(socket, new InformationalError('socket idle timeout'))
- }
-}
-
-/**
- * @param {import ('./client.js')} client
- * @param {import('net').Socket} socket
- * @returns
- */
-function connectH1 (client, socket) {
- client[kSocket] = socket
-
- if (!llhttpInstance) {
- llhttpInstance = lazyllhttp()
- }
-
- if (socket.errored) {
- throw socket.errored
- }
-
- if (socket.destroyed) {
- throw new SocketError('destroyed')
- }
-
- socket[kNoRef] = false
- socket[kWriting] = false
- socket[kReset] = false
- socket[kBlocking] = false
- socket[kParser] = new Parser(client, socket, llhttpInstance)
-
- util.addListener(socket, 'error', onHttpSocketError)
- util.addListener(socket, 'readable', onHttpSocketReadable)
- util.addListener(socket, 'end', onHttpSocketEnd)
- util.addListener(socket, 'close', onHttpSocketClose)
-
- socket[kClosed] = false
- socket.on('close', onSocketClose)
-
- return {
- version: 'h1',
- defaultPipelining: 1,
- write (request) {
- return writeH1(client, request)
- },
- resume () {
- resumeH1(client)
- },
- /**
- * @param {Error|undefined} err
- * @param {() => void} callback
- */
- destroy (err, callback) {
- if (socket[kClosed]) {
- queueMicrotask(callback)
- } else {
- socket.on('close', callback)
- socket.destroy(err)
- }
- },
- /**
- * @returns {boolean}
- */
- get destroyed () {
- return socket.destroyed
- },
- /**
- * @param {import('../core/request.js')} request
- * @returns {boolean}
- */
- busy (request) {
- if (socket[kWriting] || socket[kReset] || socket[kBlocking]) {
- return true
- }
-
- if (request) {
- if (client[kRunning] > 0 && !request.idempotent) {
- // Non-idempotent request cannot be retried.
- // Ensure that no other requests are inflight and
- // could cause failure.
- return true
- }
-
- if (client[kRunning] > 0 && (request.upgrade || request.method === 'CONNECT')) {
- // Don't dispatch an upgrade until all preceding requests have completed.
- // A misbehaving server might upgrade the connection before all pipelined
- // request has completed.
- return true
- }
-
- if (client[kRunning] > 0 && util.bodyLength(request.body) !== 0 &&
- (util.isStream(request.body) || util.isAsyncIterable(request.body) || util.isFormDataLike(request.body))) {
- // Request with stream or iterator body can error while other requests
- // are inflight and indirectly error those as well.
- // Ensure this doesn't happen by waiting for inflight
- // to complete before dispatching.
-
- // Request with stream or iterator body cannot be retried.
- // Ensure that no other requests are inflight and
- // could cause failure.
- return true
- }
- }
-
- return false
- }
- }
-}
-
-function onHttpSocketError (err) {
- assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
-
- const parser = this[kParser]
-
- // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
- // to the user.
- if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
- // We treat all incoming data so for as a valid response.
- parser.onMessageComplete()
- return
- }
-
- this[kError] = err
-
- this[kClient][kOnError](err)
-}
-
-function onHttpSocketReadable () {
- this[kParser]?.readMore()
-}
-
-function onHttpSocketEnd () {
- const parser = this[kParser]
-
- if (parser.statusCode && !parser.shouldKeepAlive) {
- // We treat all incoming data so far as a valid response.
- parser.onMessageComplete()
- return
- }
-
- util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))
-}
-
-function onHttpSocketClose () {
- const parser = this[kParser]
-
- if (parser) {
- if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
- // We treat all incoming data so far as a valid response.
- parser.onMessageComplete()
- }
-
- this[kParser].destroy()
- this[kParser] = null
- }
-
- const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))
-
- const client = this[kClient]
-
- client[kSocket] = null
- client[kHTTPContext] = null // TODO (fix): This is hacky...
-
- if (client.destroyed) {
- assert(client[kPending] === 0)
-
- // Fail entire queue.
- const requests = client[kQueue].splice(client[kRunningIdx])
- for (let i = 0; i < requests.length; i++) {
- const request = requests[i]
- util.errorRequest(client, request, err)
- }
- } else if (client[kRunning] > 0 && err.code !== 'UND_ERR_INFO') {
- // Fail head of pipeline.
- const request = client[kQueue][client[kRunningIdx]]
- client[kQueue][client[kRunningIdx]++] = null
-
- util.errorRequest(client, request, err)
- }
-
- client[kPendingIdx] = client[kRunningIdx]
-
- assert(client[kRunning] === 0)
-
- client.emit('disconnect', client[kUrl], [client], err)
-
- client[kResume]()
-}
-
-function onSocketClose () {
- this[kClosed] = true
-}
-
-/**
- * @param {import('./client.js')} client
- */
-function resumeH1 (client) {
- const socket = client[kSocket]
-
- if (socket && !socket.destroyed) {
- if (client[kSize] === 0) {
- if (!socket[kNoRef] && socket.unref) {
- socket.unref()
- socket[kNoRef] = true
- }
- } else if (socket[kNoRef] && socket.ref) {
- socket.ref()
- socket[kNoRef] = false
- }
-
- if (client[kSize] === 0) {
- if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
- socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
- }
- } else if (client[kRunning] > 0 && socket[kParser].statusCode < 200) {
- if (socket[kParser].timeoutType !== TIMEOUT_HEADERS) {
- const request = client[kQueue][client[kRunningIdx]]
- const headersTimeout = request.headersTimeout != null
- ? request.headersTimeout
- : client[kHeadersTimeout]
- socket[kParser].setTimeout(headersTimeout, TIMEOUT_HEADERS)
- }
- }
- }
-}
-
-// https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2
-function shouldSendContentLength (method) {
- return method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS' && method !== 'TRACE' && method !== 'CONNECT'
-}
-
-/**
- * @param {import('./client.js')} client
- * @param {import('../core/request.js')} request
- * @returns
- */
-function writeH1 (client, request) {
- const { method, path, host, upgrade, blocking, reset } = request
-
- let { body, headers, contentLength } = request
-
- // https://tools.ietf.org/html/rfc7231#section-4.3.1
- // https://tools.ietf.org/html/rfc7231#section-4.3.2
- // https://tools.ietf.org/html/rfc7231#section-4.3.5
-
- // Sending a payload body on a request that does not
- // expect it can cause undefined behavior on some
- // servers and corrupt connection state. Do not
- // re-use the connection for further requests.
-
- const expectsPayload = (
- method === 'PUT' ||
- method === 'POST' ||
- method === 'PATCH' ||
- method === 'QUERY' ||
- method === 'PROPFIND' ||
- method === 'PROPPATCH'
- )
-
- if (util.isFormDataLike(body)) {
- if (!extractBody) {
- extractBody = require('../web/fetch/body.js').extractBody
- }
-
- const [bodyStream, contentType] = extractBody(body)
- if (request.contentType == null) {
- headers.push('content-type', contentType)
- }
- body = bodyStream.stream
- contentLength = bodyStream.length
- } else if (util.isBlobLike(body) && request.contentType == null && body.type) {
- headers.push('content-type', body.type)
- }
-
- if (body && typeof body.read === 'function') {
- // Try to read EOF in order to get length.
- body.read(0)
- }
-
- const bodyLength = util.bodyLength(body)
-
- contentLength = bodyLength ?? contentLength
-
- if (contentLength === null) {
- contentLength = request.contentLength
- }
-
- if (contentLength === 0 && !expectsPayload) {
- // https://tools.ietf.org/html/rfc7230#section-3.3.2
- // A user agent SHOULD NOT send a Content-Length header field when
- // the request message does not contain a payload body and the method
- // semantics do not anticipate such a body.
-
- contentLength = null
- }
-
- // https://github.com/nodejs/undici/issues/2046
- // A user agent may send a Content-Length header with 0 value, this should be allowed.
- if (shouldSendContentLength(method) && contentLength > 0 && request.contentLength !== null && request.contentLength !== contentLength) {
- if (client[kStrictContentLength]) {
- util.errorRequest(client, request, new RequestContentLengthMismatchError())
- return false
- }
-
- process.emitWarning(new RequestContentLengthMismatchError())
- }
-
- const socket = client[kSocket]
-
- /**
- * @param {Error} [err]
- * @returns {void}
- */
- const abort = (err) => {
- if (request.aborted || request.completed) {
- return
- }
-
- util.errorRequest(client, request, err || new RequestAbortedError())
-
- util.destroy(body)
- util.destroy(socket, new InformationalError('aborted'))
- }
-
- try {
- request.onConnect(abort)
- } catch (err) {
- util.errorRequest(client, request, err)
- }
-
- if (request.aborted) {
- return false
- }
-
- if (method === 'HEAD') {
- // https://github.com/mcollina/undici/issues/258
- // Close after a HEAD request to interop with misbehaving servers
- // that may send a body in the response.
-
- socket[kReset] = true
- }
-
- if (upgrade || method === 'CONNECT') {
- // On CONNECT or upgrade, block pipeline from dispatching further
- // requests on this connection.
-
- socket[kReset] = true
- }
-
- if (reset != null) {
- socket[kReset] = reset
- }
-
- if (client[kMaxRequests] && socket[kCounter]++ >= client[kMaxRequests]) {
- socket[kReset] = true
- }
-
- if (blocking) {
- socket[kBlocking] = true
- }
-
- let header = `${method} ${path} HTTP/1.1\r\n`
-
- if (typeof host === 'string') {
- header += `host: ${host}\r\n`
- } else {
- header += client[kHostHeader]
- }
-
- if (upgrade) {
- header += `connection: upgrade\r\nupgrade: ${upgrade}\r\n`
- } else if (client[kPipelining] && !socket[kReset]) {
- header += 'connection: keep-alive\r\n'
- } else {
- header += 'connection: close\r\n'
- }
-
- if (Array.isArray(headers)) {
- for (let n = 0; n < headers.length; n += 2) {
- const key = headers[n + 0]
- const val = headers[n + 1]
-
- if (Array.isArray(val)) {
- for (let i = 0; i < val.length; i++) {
- header += `${key}: ${val[i]}\r\n`
- }
- } else {
- header += `${key}: ${val}\r\n`
- }
- }
- }
-
- if (channels.sendHeaders.hasSubscribers) {
- channels.sendHeaders.publish({ request, headers: header, socket })
- }
-
- if (!body || bodyLength === 0) {
- writeBuffer(abort, null, client, request, socket, contentLength, header, expectsPayload)
- } else if (util.isBuffer(body)) {
- writeBuffer(abort, body, client, request, socket, contentLength, header, expectsPayload)
- } else if (util.isBlobLike(body)) {
- if (typeof body.stream === 'function') {
- writeIterable(abort, body.stream(), client, request, socket, contentLength, header, expectsPayload)
- } else {
- writeBlob(abort, body, client, request, socket, contentLength, header, expectsPayload)
- }
- } else if (util.isStream(body)) {
- writeStream(abort, body, client, request, socket, contentLength, header, expectsPayload)
- } else if (util.isIterable(body)) {
- writeIterable(abort, body, client, request, socket, contentLength, header, expectsPayload)
- } else {
- assert(false)
- }
-
- return true
-}
-
-/**
- * @param {AbortCallback} abort
- * @param {import('stream').Stream} body
- * @param {import('./client.js')} client
- * @param {import('../core/request.js')} request
- * @param {import('net').Socket} socket
- * @param {number} contentLength
- * @param {string} header
- * @param {boolean} expectsPayload
- */
-function writeStream (abort, body, client, request, socket, contentLength, header, expectsPayload) {
- assert(contentLength !== 0 || client[kRunning] === 0, 'stream body cannot be pipelined')
-
- let finished = false
-
- const writer = new AsyncWriter({ abort, socket, request, contentLength, client, expectsPayload, header })
-
- /**
- * @param {Buffer} chunk
- * @returns {void}
- */
- const onData = function (chunk) {
- if (finished) {
- return
- }
-
- try {
- if (!writer.write(chunk) && this.pause) {
- this.pause()
- }
- } catch (err) {
- util.destroy(this, err)
- }
- }
-
- /**
- * @returns {void}
- */
- const onDrain = function () {
- if (finished) {
- return
- }
-
- if (body.resume) {
- body.resume()
- }
- }
-
- /**
- * @returns {void}
- */
- const onClose = function () {
- // 'close' might be emitted *before* 'error' for
- // broken streams. Wait a tick to avoid this case.
- queueMicrotask(() => {
- // It's only safe to remove 'error' listener after
- // 'close'.
- body.removeListener('error', onFinished)
- })
-
- if (!finished) {
- const err = new RequestAbortedError()
- queueMicrotask(() => onFinished(err))
- }
- }
-
- /**
- * @param {Error} [err]
- * @returns
- */
- const onFinished = function (err) {
- if (finished) {
- return
- }
-
- finished = true
-
- assert(socket.destroyed || (socket[kWriting] && client[kRunning] <= 1))
-
- socket
- .off('drain', onDrain)
- .off('error', onFinished)
-
- body
- .removeListener('data', onData)
- .removeListener('end', onFinished)
- .removeListener('close', onClose)
-
- if (!err) {
- try {
- writer.end()
- } catch (er) {
- err = er
- }
- }
-
- writer.destroy(err)
-
- if (err && (err.code !== 'UND_ERR_INFO' || err.message !== 'reset')) {
- util.destroy(body, err)
- } else {
- util.destroy(body)
- }
- }
-
- body
- .on('data', onData)
- .on('end', onFinished)
- .on('error', onFinished)
- .on('close', onClose)
-
- if (body.resume) {
- body.resume()
- }
-
- socket
- .on('drain', onDrain)
- .on('error', onFinished)
-
- if (body.errorEmitted ?? body.errored) {
- setImmediate(onFinished, body.errored)
- } else if (body.endEmitted ?? body.readableEnded) {
- setImmediate(onFinished, null)
- }
-
- if (body.closeEmitted ?? body.closed) {
- setImmediate(onClose)
- }
-}
-
-/**
- * @typedef AbortCallback
- * @type {Function}
- * @param {Error} [err]
- * @returns {void}
- */
-
-/**
- * @param {AbortCallback} abort
- * @param {Uint8Array|null} body
- * @param {import('./client.js')} client
- * @param {import('../core/request.js')} request
- * @param {import('net').Socket} socket
- * @param {number} contentLength
- * @param {string} header
- * @param {boolean} expectsPayload
- * @returns {void}
- */
-function writeBuffer (abort, body, client, request, socket, contentLength, header, expectsPayload) {
- try {
- if (!body) {
- if (contentLength === 0) {
- socket.write(`${header}content-length: 0\r\n\r\n`, 'latin1')
- } else {
- assert(contentLength === null, 'no body must not have content length')
- socket.write(`${header}\r\n`, 'latin1')
- }
- } else if (util.isBuffer(body)) {
- assert(contentLength === body.byteLength, 'buffer body must have content length')
-
- socket.cork()
- socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1')
- socket.write(body)
- socket.uncork()
- request.onBodySent(body)
-
- if (!expectsPayload && request.reset !== false) {
- socket[kReset] = true
- }
- }
- request.onRequestSent()
-
- client[kResume]()
- } catch (err) {
- abort(err)
- }
-}
-
-/**
- * @param {AbortCallback} abort
- * @param {Blob} body
- * @param {import('./client.js')} client
- * @param {import('../core/request.js')} request
- * @param {import('net').Socket} socket
- * @param {number} contentLength
- * @param {string} header
- * @param {boolean} expectsPayload
- * @returns {Promise<void>}
- */
-async function writeBlob (abort, body, client, request, socket, contentLength, header, expectsPayload) {
- assert(contentLength === body.size, 'blob body must have content length')
-
- try {
- if (contentLength != null && contentLength !== body.size) {
- throw new RequestContentLengthMismatchError()
- }
-
- const buffer = Buffer.from(await body.arrayBuffer())
-
- socket.cork()
- socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1')
- socket.write(buffer)
- socket.uncork()
-
- request.onBodySent(buffer)
- request.onRequestSent()
-
- if (!expectsPayload && request.reset !== false) {
- socket[kReset] = true
- }
-
- client[kResume]()
- } catch (err) {
- abort(err)
- }
-}
-
-/**
- * @param {AbortCallback} abort
- * @param {Iterable} body
- * @param {import('./client.js')} client
- * @param {import('../core/request.js')} request
- * @param {import('net').Socket} socket
- * @param {number} contentLength
- * @param {string} header
- * @param {boolean} expectsPayload
- * @returns {Promise<void>}
- */
-async function writeIterable (abort, body, client, request, socket, contentLength, header, expectsPayload) {
- assert(contentLength !== 0 || client[kRunning] === 0, 'iterator body cannot be pipelined')
-
- let callback = null
- function onDrain () {
- if (callback) {
- const cb = callback
- callback = null
- cb()
- }
- }
-
- const waitForDrain = () => new Promise((resolve, reject) => {
- assert(callback === null)
-
- if (socket[kError]) {
- reject(socket[kError])
- } else {
- callback = resolve
- }
- })
-
- socket
- .on('close', onDrain)
- .on('drain', onDrain)
-
- const writer = new AsyncWriter({ abort, socket, request, contentLength, client, expectsPayload, header })
- try {
- // It's up to the user to somehow abort the async iterable.
- for await (const chunk of body) {
- if (socket[kError]) {
- throw socket[kError]
- }
-
- if (!writer.write(chunk)) {
- await waitForDrain()
- }
- }
-
- writer.end()
- } catch (err) {
- writer.destroy(err)
- } finally {
- socket
- .off('close', onDrain)
- .off('drain', onDrain)
- }
-}
-
-class AsyncWriter {
- /**
- *
- * @param {object} arg
- * @param {AbortCallback} arg.abort
- * @param {import('net').Socket} arg.socket
- * @param {import('../core/request.js')} arg.request
- * @param {number} arg.contentLength
- * @param {import('./client.js')} arg.client
- * @param {boolean} arg.expectsPayload
- * @param {string} arg.header
- */
- constructor ({ abort, socket, request, contentLength, client, expectsPayload, header }) {
- this.socket = socket
- this.request = request
- this.contentLength = contentLength
- this.client = client
- this.bytesWritten = 0
- this.expectsPayload = expectsPayload
- this.header = header
- this.abort = abort
-
- socket[kWriting] = true
- }
-
- /**
- * @param {Buffer} chunk
- * @returns
- */
- write (chunk) {
- const { socket, request, contentLength, client, bytesWritten, expectsPayload, header } = this
-
- if (socket[kError]) {
- throw socket[kError]
- }
-
- if (socket.destroyed) {
- return false
- }
-
- const len = Buffer.byteLength(chunk)
- if (!len) {
- return true
- }
-
- // We should defer writing chunks.
- if (contentLength !== null && bytesWritten + len > contentLength) {
- if (client[kStrictContentLength]) {
- throw new RequestContentLengthMismatchError()
- }
-
- process.emitWarning(new RequestContentLengthMismatchError())
- }
-
- socket.cork()
-
- if (bytesWritten === 0) {
- if (!expectsPayload && request.reset !== false) {
- socket[kReset] = true
- }
-
- if (contentLength === null) {
- socket.write(`${header}transfer-encoding: chunked\r\n`, 'latin1')
- } else {
- socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1')
- }
- }
-
- if (contentLength === null) {
- socket.write(`\r\n${len.toString(16)}\r\n`, 'latin1')
- }
-
- this.bytesWritten += len
-
- const ret = socket.write(chunk)
-
- socket.uncork()
-
- request.onBodySent(chunk)
-
- if (!ret) {
- if (socket[kParser].timeout && socket[kParser].timeoutType === TIMEOUT_HEADERS) {
- if (socket[kParser].timeout.refresh) {
- socket[kParser].timeout.refresh()
- }
- }
- }
-
- return ret
- }
-
- /**
- * @returns {void}
- */
- end () {
- const { socket, contentLength, client, bytesWritten, expectsPayload, header, request } = this
- request.onRequestSent()
-
- socket[kWriting] = false
-
- if (socket[kError]) {
- throw socket[kError]
- }
-
- if (socket.destroyed) {
- return
- }
-
- if (bytesWritten === 0) {
- if (expectsPayload) {
- // https://tools.ietf.org/html/rfc7230#section-3.3.2
- // A user agent SHOULD send a Content-Length in a request message when
- // no Transfer-Encoding is sent and the request method defines a meaning
- // for an enclosed payload body.
-
- socket.write(`${header}content-length: 0\r\n\r\n`, 'latin1')
- } else {
- socket.write(`${header}\r\n`, 'latin1')
- }
- } else if (contentLength === null) {
- socket.write('\r\n0\r\n\r\n', 'latin1')
- }
-
- if (contentLength !== null && bytesWritten !== contentLength) {
- if (client[kStrictContentLength]) {
- throw new RequestContentLengthMismatchError()
- } else {
- process.emitWarning(new RequestContentLengthMismatchError())
- }
- }
-
- if (socket[kParser].timeout && socket[kParser].timeoutType === TIMEOUT_HEADERS) {
- if (socket[kParser].timeout.refresh) {
- socket[kParser].timeout.refresh()
- }
- }
-
- client[kResume]()
- }
-
- /**
- * @param {Error} [err]
- * @returns {void}
- */
- destroy (err) {
- const { socket, client, abort } = this
-
- socket[kWriting] = false
-
- if (err) {
- assert(client[kRunning] <= 1, 'pipeline should only contain this request')
- abort(err)
- }
- }
-}
-
-module.exports = connectH1
diff --git a/vanilla/node_modules/undici/lib/dispatcher/client-h2.js b/vanilla/node_modules/undici/lib/dispatcher/client-h2.js
deleted file mode 100644
index 0969108..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/client-h2.js
+++ /dev/null
@@ -1,990 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { pipeline } = require('node:stream')
-const util = require('../core/util.js')
-const {
- RequestContentLengthMismatchError,
- RequestAbortedError,
- SocketError,
- InformationalError,
- InvalidArgumentError
-} = require('../core/errors.js')
-const {
- kUrl,
- kReset,
- kClient,
- kRunning,
- kPending,
- kQueue,
- kPendingIdx,
- kRunningIdx,
- kError,
- kSocket,
- kStrictContentLength,
- kOnError,
- kMaxConcurrentStreams,
- kPingInterval,
- kHTTP2Session,
- kHTTP2InitialWindowSize,
- kHTTP2ConnectionWindowSize,
- kResume,
- kSize,
- kHTTPContext,
- kClosed,
- kBodyTimeout,
- kEnableConnectProtocol,
- kRemoteSettings,
- kHTTP2Stream,
- kHTTP2SessionState
-} = require('../core/symbols.js')
-const { channels } = require('../core/diagnostics.js')
-
-const kOpenStreams = Symbol('open streams')
-
-let extractBody
-
-/** @type {import('http2')} */
-let http2
-try {
- http2 = require('node:http2')
-} catch {
- // @ts-ignore
- http2 = { constants: {} }
-}
-
-const {
- constants: {
- HTTP2_HEADER_AUTHORITY,
- HTTP2_HEADER_METHOD,
- HTTP2_HEADER_PATH,
- HTTP2_HEADER_SCHEME,
- HTTP2_HEADER_CONTENT_LENGTH,
- HTTP2_HEADER_EXPECT,
- HTTP2_HEADER_STATUS,
- HTTP2_HEADER_PROTOCOL,
- NGHTTP2_REFUSED_STREAM,
- NGHTTP2_CANCEL
- }
-} = http2
-
-function parseH2Headers (headers) {
- const result = []
-
- for (const [name, value] of Object.entries(headers)) {
- // h2 may concat the header value by array
- // e.g. Set-Cookie
- if (Array.isArray(value)) {
- for (const subvalue of value) {
- // we need to provide each header value of header name
- // because the headers handler expect name-value pair
- result.push(Buffer.from(name), Buffer.from(subvalue))
- }
- } else {
- result.push(Buffer.from(name), Buffer.from(value))
- }
- }
-
- return result
-}
-
-function connectH2 (client, socket) {
- client[kSocket] = socket
-
- const http2InitialWindowSize = client[kHTTP2InitialWindowSize]
- const http2ConnectionWindowSize = client[kHTTP2ConnectionWindowSize]
-
- const session = http2.connect(client[kUrl], {
- createConnection: () => socket,
- peerMaxConcurrentStreams: client[kMaxConcurrentStreams],
- settings: {
- // TODO(metcoder95): add support for PUSH
- enablePush: false,
- ...(http2InitialWindowSize != null ? { initialWindowSize: http2InitialWindowSize } : null)
- }
- })
-
- client[kSocket] = socket
- session[kOpenStreams] = 0
- session[kClient] = client
- session[kSocket] = socket
- session[kHTTP2SessionState] = {
- ping: {
- interval: client[kPingInterval] === 0 ? null : setInterval(onHttp2SendPing, client[kPingInterval], session).unref()
- }
- }
- // We set it to true by default in a best-effort; however once connected to an H2 server
- // we will check if extended CONNECT protocol is supported or not
- // and set this value accordingly.
- session[kEnableConnectProtocol] = false
- // States whether or not we have received the remote settings from the server
- session[kRemoteSettings] = false
-
- // Apply connection-level flow control once connected (if supported).
- if (http2ConnectionWindowSize) {
- util.addListener(session, 'connect', applyConnectionWindowSize.bind(session, http2ConnectionWindowSize))
- }
-
- util.addListener(session, 'error', onHttp2SessionError)
- util.addListener(session, 'frameError', onHttp2FrameError)
- util.addListener(session, 'end', onHttp2SessionEnd)
- util.addListener(session, 'goaway', onHttp2SessionGoAway)
- util.addListener(session, 'close', onHttp2SessionClose)
- util.addListener(session, 'remoteSettings', onHttp2RemoteSettings)
- // TODO (@metcoder95): implement SETTINGS support
- // util.addListener(session, 'localSettings', onHttp2RemoteSettings)
-
- session.unref()
-
- client[kHTTP2Session] = session
- socket[kHTTP2Session] = session
-
- util.addListener(socket, 'error', onHttp2SocketError)
- util.addListener(socket, 'end', onHttp2SocketEnd)
- util.addListener(socket, 'close', onHttp2SocketClose)
-
- socket[kClosed] = false
- socket.on('close', onSocketClose)
-
- return {
- version: 'h2',
- defaultPipelining: Infinity,
- /**
- * @param {import('../core/request.js')} request
- * @returns {boolean}
- */
- write (request) {
- return writeH2(client, request)
- },
- /**
- * @returns {void}
- */
- resume () {
- resumeH2(client)
- },
- /**
- * @param {Error | null} err
- * @param {() => void} callback
- */
- destroy (err, callback) {
- if (socket[kClosed]) {
- queueMicrotask(callback)
- } else {
- socket.destroy(err).on('close', callback)
- }
- },
- /**
- * @type {boolean}
- */
- get destroyed () {
- return socket.destroyed
- },
- /**
- * @param {import('../core/request.js')} request
- * @returns {boolean}
- */
- busy (request) {
- if (request != null) {
- if (client[kRunning] > 0) {
- // We are already processing requests
-
- // Non-idempotent request cannot be retried.
- // Ensure that no other requests are inflight and
- // could cause failure.
- if (request.idempotent === false) return true
- // Don't dispatch an upgrade until all preceding requests have completed.
- // Possibly, we do not have remote settings confirmed yet.
- if ((request.upgrade === 'websocket' || request.method === 'CONNECT') && session[kRemoteSettings] === false) return true
- // Request with stream or iterator body can error while other requests
- // are inflight and indirectly error those as well.
- // Ensure this doesn't happen by waiting for inflight
- // to complete before dispatching.
-
- // Request with stream or iterator body cannot be retried.
- // Ensure that no other requests are inflight and
- // could cause failure.
- if (util.bodyLength(request.body) !== 0 &&
- (util.isStream(request.body) || util.isAsyncIterable(request.body) || util.isFormDataLike(request.body))) return true
- } else {
- return (request.upgrade === 'websocket' || request.method === 'CONNECT') && session[kRemoteSettings] === false
- }
- }
-
- return false
- }
- }
-}
-
-function resumeH2 (client) {
- const socket = client[kSocket]
-
- if (socket?.destroyed === false) {
- if (client[kSize] === 0 || client[kMaxConcurrentStreams] === 0) {
- socket.unref()
- client[kHTTP2Session].unref()
- } else {
- socket.ref()
- client[kHTTP2Session].ref()
- }
- }
-}
-
-function applyConnectionWindowSize (connectionWindowSize) {
- try {
- if (typeof this.setLocalWindowSize === 'function') {
- this.setLocalWindowSize(connectionWindowSize)
- }
- } catch {
- // Best-effort only.
- }
-}
-
-function onHttp2RemoteSettings (settings) {
- // Fallbacks are a safe bet, remote setting will always override
- this[kClient][kMaxConcurrentStreams] = settings.maxConcurrentStreams ?? this[kClient][kMaxConcurrentStreams]
- /**
- * From RFC-8441
- * A sender MUST NOT send a SETTINGS_ENABLE_CONNECT_PROTOCOL parameter
- * with the value of 0 after previously sending a value of 1.
- */
- // Note: Cannot be tested in Node, it does not supports disabling the extended CONNECT protocol once enabled
- if (this[kRemoteSettings] === true && this[kEnableConnectProtocol] === true && settings.enableConnectProtocol === false) {
- const err = new InformationalError('HTTP/2: Server disabled extended CONNECT protocol against RFC-8441')
- this[kSocket][kError] = err
- this[kClient][kOnError](err)
- return
- }
-
- this[kEnableConnectProtocol] = settings.enableConnectProtocol ?? this[kEnableConnectProtocol]
- this[kRemoteSettings] = true
- this[kClient][kResume]()
-}
-
-function onHttp2SendPing (session) {
- const state = session[kHTTP2SessionState]
- if ((session.closed || session.destroyed) && state.ping.interval != null) {
- clearInterval(state.ping.interval)
- state.ping.interval = null
- return
- }
-
- // If no ping sent, do nothing
- session.ping(onPing.bind(session))
-
- function onPing (err, duration) {
- const client = this[kClient]
- const socket = this[kClient]
-
- if (err != null) {
- const error = new InformationalError(`HTTP/2: "PING" errored - type ${err.message}`)
- socket[kError] = error
- client[kOnError](error)
- } else {
- client.emit('ping', duration)
- }
- }
-}
-
-function onHttp2SessionError (err) {
- assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
-
- this[kSocket][kError] = err
- this[kClient][kOnError](err)
-}
-
-function onHttp2FrameError (type, code, id) {
- if (id === 0) {
- const err = new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`)
- this[kSocket][kError] = err
- this[kClient][kOnError](err)
- }
-}
-
-function onHttp2SessionEnd () {
- const err = new SocketError('other side closed', util.getSocketInfo(this[kSocket]))
- this.destroy(err)
- util.destroy(this[kSocket], err)
-}
-
-/**
- * This is the root cause of #3011
- * We need to handle GOAWAY frames properly, and trigger the session close
- * along with the socket right away
- *
- * @this {import('http2').ClientHttp2Session}
- * @param {number} errorCode
- */
-function onHttp2SessionGoAway (errorCode) {
- // TODO(mcollina): Verify if GOAWAY implements the spec correctly:
- // https://datatracker.ietf.org/doc/html/rfc7540#section-6.8
- // Specifically, we do not verify the "valid" stream id.
-
- const err = this[kError] || new SocketError(`HTTP/2: "GOAWAY" frame received with code ${errorCode}`, util.getSocketInfo(this[kSocket]))
- const client = this[kClient]
-
- client[kSocket] = null
- client[kHTTPContext] = null
-
- // this is an HTTP2 session
- this.close()
- this[kHTTP2Session] = null
-
- util.destroy(this[kSocket], err)
-
- // Fail head of pipeline.
- if (client[kRunningIdx] < client[kQueue].length) {
- const request = client[kQueue][client[kRunningIdx]]
- client[kQueue][client[kRunningIdx]++] = null
- util.errorRequest(client, request, err)
- client[kPendingIdx] = client[kRunningIdx]
- }
-
- assert(client[kRunning] === 0)
-
- client.emit('disconnect', client[kUrl], [client], err)
- client.emit('connectionError', client[kUrl], [client], err)
-
- client[kResume]()
-}
-
-function onHttp2SessionClose () {
- const { [kClient]: client, [kHTTP2SessionState]: state } = this
- const { [kSocket]: socket } = client
-
- const err = this[kSocket][kError] || this[kError] || new SocketError('closed', util.getSocketInfo(socket))
-
- client[kSocket] = null
- client[kHTTPContext] = null
-
- if (state.ping.interval != null) {
- clearInterval(state.ping.interval)
- state.ping.interval = null
- }
-
- if (client.destroyed) {
- assert(client[kPending] === 0)
-
- // Fail entire queue.
- const requests = client[kQueue].splice(client[kRunningIdx])
- for (let i = 0; i < requests.length; i++) {
- const request = requests[i]
- util.errorRequest(client, request, err)
- }
- }
-}
-
-function onHttp2SocketClose () {
- const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))
-
- const client = this[kHTTP2Session][kClient]
-
- client[kSocket] = null
- client[kHTTPContext] = null
-
- if (this[kHTTP2Session] !== null) {
- this[kHTTP2Session].destroy(err)
- }
-
- client[kPendingIdx] = client[kRunningIdx]
-
- assert(client[kRunning] === 0)
-
- client.emit('disconnect', client[kUrl], [client], err)
-
- client[kResume]()
-}
-
-function onHttp2SocketError (err) {
- assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
-
- this[kError] = err
-
- this[kClient][kOnError](err)
-}
-
-function onHttp2SocketEnd () {
- util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))
-}
-
-function onSocketClose () {
- this[kClosed] = true
-}
-
-// https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2
-function shouldSendContentLength (method) {
- return method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS' && method !== 'TRACE' && method !== 'CONNECT'
-}
-
-function writeH2 (client, request) {
- const requestTimeout = request.bodyTimeout ?? client[kBodyTimeout]
- const session = client[kHTTP2Session]
- const { method, path, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request
- let { body } = request
-
- if (upgrade != null && upgrade !== 'websocket') {
- util.errorRequest(client, request, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`))
- return false
- }
-
- const headers = {}
- for (let n = 0; n < reqHeaders.length; n += 2) {
- const key = reqHeaders[n + 0]
- const val = reqHeaders[n + 1]
-
- if (key === 'cookie') {
- if (headers[key] != null) {
- headers[key] = Array.isArray(headers[key]) ? (headers[key].push(val), headers[key]) : [headers[key], val]
- } else {
- headers[key] = val
- }
-
- continue
- }
-
- if (Array.isArray(val)) {
- for (let i = 0; i < val.length; i++) {
- if (headers[key]) {
- headers[key] += `, ${val[i]}`
- } else {
- headers[key] = val[i]
- }
- }
- } else if (headers[key]) {
- headers[key] += `, ${val}`
- } else {
- headers[key] = val
- }
- }
-
- /** @type {import('node:http2').ClientHttp2Stream} */
- let stream = null
-
- const { hostname, port } = client[kUrl]
-
- headers[HTTP2_HEADER_AUTHORITY] = host || `${hostname}${port ? `:${port}` : ''}`
- headers[HTTP2_HEADER_METHOD] = method
-
- const abort = (err) => {
- if (request.aborted || request.completed) {
- return
- }
-
- err = err || new RequestAbortedError()
-
- util.errorRequest(client, request, err)
-
- if (stream != null) {
- // Some chunks might still come after abort,
- // let's ignore them
- stream.removeAllListeners('data')
-
- // On Abort, we close the stream to send RST_STREAM frame
- stream.close()
-
- // We move the running index to the next request
- client[kOnError](err)
- client[kResume]()
- }
-
- // We do not destroy the socket as we can continue using the session
- // the stream gets destroyed and the session remains to create new streams
- util.destroy(body, err)
- }
-
- try {
- // We are already connected, streams are pending.
- // We can call on connect, and wait for abort
- request.onConnect(abort)
- } catch (err) {
- util.errorRequest(client, request, err)
- }
-
- if (request.aborted) {
- return false
- }
-
- if (upgrade || method === 'CONNECT') {
- session.ref()
-
- if (upgrade === 'websocket') {
- // We cannot upgrade to websocket if extended CONNECT protocol is not supported
- if (session[kEnableConnectProtocol] === false) {
- util.errorRequest(client, request, new InformationalError('HTTP/2: Extended CONNECT protocol not supported by server'))
- session.unref()
- return false
- }
-
- // We force the method to CONNECT
- // as per RFC-8441
- // https://datatracker.ietf.org/doc/html/rfc8441#section-4
- headers[HTTP2_HEADER_METHOD] = 'CONNECT'
- headers[HTTP2_HEADER_PROTOCOL] = 'websocket'
- // :path and :scheme headers must be omitted when sending CONNECT but set if extended-CONNECT
- headers[HTTP2_HEADER_PATH] = path
-
- if (protocol === 'ws:' || protocol === 'wss:') {
- headers[HTTP2_HEADER_SCHEME] = protocol === 'ws:' ? 'http' : 'https'
- } else {
- headers[HTTP2_HEADER_SCHEME] = protocol === 'http:' ? 'http' : 'https'
- }
-
- stream = session.request(headers, { endStream: false, signal })
- stream[kHTTP2Stream] = true
-
- stream.once('response', (headers, _flags) => {
- const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers
-
- request.onUpgrade(statusCode, parseH2Headers(realHeaders), stream)
-
- ++session[kOpenStreams]
- client[kQueue][client[kRunningIdx]++] = null
- })
-
- stream.on('error', () => {
- if (stream.rstCode === NGHTTP2_REFUSED_STREAM || stream.rstCode === NGHTTP2_CANCEL) {
- // NGHTTP2_REFUSED_STREAM (7) or NGHTTP2_CANCEL (8)
- // We do not treat those as errors as the server might
- // not support websockets and refuse the stream
- abort(new InformationalError(`HTTP/2: "stream error" received - code ${stream.rstCode}`))
- }
- })
-
- stream.once('close', () => {
- session[kOpenStreams] -= 1
- if (session[kOpenStreams] === 0) session.unref()
- })
-
- stream.setTimeout(requestTimeout)
- return true
- }
-
- // TODO: consolidate once we support CONNECT properly
- // NOTE: We are already connected, streams are pending, first request
- // will create a new stream. We trigger a request to create the stream and wait until
- // `ready` event is triggered
- // We disabled endStream to allow the user to write to the stream
- stream = session.request(headers, { endStream: false, signal })
- stream[kHTTP2Stream] = true
- stream.on('response', headers => {
- const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers
-
- request.onUpgrade(statusCode, parseH2Headers(realHeaders), stream)
- ++session[kOpenStreams]
- client[kQueue][client[kRunningIdx]++] = null
- })
- stream.once('close', () => {
- session[kOpenStreams] -= 1
- if (session[kOpenStreams] === 0) session.unref()
- })
- stream.setTimeout(requestTimeout)
-
- return true
- }
-
- // https://tools.ietf.org/html/rfc7540#section-8.3
- // :path and :scheme headers must be omitted when sending CONNECT
- headers[HTTP2_HEADER_PATH] = path
- headers[HTTP2_HEADER_SCHEME] = protocol === 'http:' ? 'http' : 'https'
-
- // https://tools.ietf.org/html/rfc7231#section-4.3.1
- // https://tools.ietf.org/html/rfc7231#section-4.3.2
- // https://tools.ietf.org/html/rfc7231#section-4.3.5
-
- // Sending a payload body on a request that does not
- // expect it can cause undefined behavior on some
- // servers and corrupt connection state. Do not
- // re-use the connection for further requests.
-
- const expectsPayload = (
- method === 'PUT' ||
- method === 'POST' ||
- method === 'PATCH'
- )
-
- if (body && typeof body.read === 'function') {
- // Try to read EOF in order to get length.
- body.read(0)
- }
-
- let contentLength = util.bodyLength(body)
-
- if (util.isFormDataLike(body)) {
- extractBody ??= require('../web/fetch/body.js').extractBody
-
- const [bodyStream, contentType] = extractBody(body)
- headers['content-type'] = contentType
-
- body = bodyStream.stream
- contentLength = bodyStream.length
- }
-
- if (contentLength == null) {
- contentLength = request.contentLength
- }
-
- if (!expectsPayload) {
- // https://tools.ietf.org/html/rfc7230#section-3.3.2
- // A user agent SHOULD NOT send a Content-Length header field when
- // the request message does not contain a payload body and the method
- // semantics do not anticipate such a body.
- // And for methods that don't expect a payload, omit Content-Length.
- contentLength = null
- }
-
- // https://github.com/nodejs/undici/issues/2046
- // A user agent may send a Content-Length header with 0 value, this should be allowed.
- if (shouldSendContentLength(method) && contentLength > 0 && request.contentLength != null && request.contentLength !== contentLength) {
- if (client[kStrictContentLength]) {
- util.errorRequest(client, request, new RequestContentLengthMismatchError())
- return false
- }
-
- process.emitWarning(new RequestContentLengthMismatchError())
- }
-
- if (contentLength != null) {
- assert(body || contentLength === 0, 'no body must not have content length')
- headers[HTTP2_HEADER_CONTENT_LENGTH] = `${contentLength}`
- }
-
- session.ref()
-
- if (channels.sendHeaders.hasSubscribers) {
- let header = ''
- for (const key in headers) {
- header += `${key}: ${headers[key]}\r\n`
- }
- channels.sendHeaders.publish({ request, headers: header, socket: session[kSocket] })
- }
-
- // TODO(metcoder95): add support for sending trailers
- const shouldEndStream = method === 'GET' || method === 'HEAD' || body === null
- if (expectContinue) {
- headers[HTTP2_HEADER_EXPECT] = '100-continue'
- stream = session.request(headers, { endStream: shouldEndStream, signal })
- stream[kHTTP2Stream] = true
-
- stream.once('continue', writeBodyH2)
- } else {
- stream = session.request(headers, {
- endStream: shouldEndStream,
- signal
- })
- stream[kHTTP2Stream] = true
-
- writeBodyH2()
- }
-
- // Increment counter as we have new streams open
- ++session[kOpenStreams]
- stream.setTimeout(requestTimeout)
-
- // Track whether we received a response (headers)
- let responseReceived = false
-
- stream.once('response', headers => {
- const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers
- request.onResponseStarted()
- responseReceived = true
-
- // Due to the stream nature, it is possible we face a race condition
- // where the stream has been assigned, but the request has been aborted
- // the request remains in-flight and headers hasn't been received yet
- // for those scenarios, best effort is to destroy the stream immediately
- // as there's no value to keep it open.
- if (request.aborted) {
- stream.removeAllListeners('data')
- return
- }
-
- if (request.onHeaders(Number(statusCode), parseH2Headers(realHeaders), stream.resume.bind(stream), '') === false) {
- stream.pause()
- }
- })
-
- stream.on('data', (chunk) => {
- if (request.onData(chunk) === false) {
- stream.pause()
- }
- })
-
- stream.once('end', () => {
- stream.removeAllListeners('data')
- // If we received a response, this is a normal completion
- if (responseReceived) {
- if (!request.aborted && !request.completed) {
- request.onComplete({})
- }
-
- client[kQueue][client[kRunningIdx]++] = null
- client[kResume]()
- } else {
- // Stream ended without receiving a response - this is an error
- // (e.g., server destroyed the stream before sending headers)
- abort(new InformationalError('HTTP/2: stream half-closed (remote)'))
- client[kQueue][client[kRunningIdx]++] = null
- client[kPendingIdx] = client[kRunningIdx]
- client[kResume]()
- }
- })
-
- stream.once('close', () => {
- stream.removeAllListeners('data')
- session[kOpenStreams] -= 1
- if (session[kOpenStreams] === 0) {
- session.unref()
- }
- })
-
- stream.once('error', function (err) {
- stream.removeAllListeners('data')
- abort(err)
- })
-
- stream.once('frameError', (type, code) => {
- stream.removeAllListeners('data')
- abort(new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`))
- })
-
- stream.on('aborted', () => {
- stream.removeAllListeners('data')
- })
-
- stream.on('timeout', () => {
- const err = new InformationalError(`HTTP/2: "stream timeout after ${requestTimeout}"`)
- stream.removeAllListeners('data')
- session[kOpenStreams] -= 1
-
- if (session[kOpenStreams] === 0) {
- session.unref()
- }
-
- abort(err)
- })
-
- stream.once('trailers', trailers => {
- if (request.aborted || request.completed) {
- return
- }
-
- request.onComplete(trailers)
- })
-
- return true
-
- function writeBodyH2 () {
- if (!body || contentLength === 0) {
- writeBuffer(
- abort,
- stream,
- null,
- client,
- request,
- client[kSocket],
- contentLength,
- expectsPayload
- )
- } else if (util.isBuffer(body)) {
- writeBuffer(
- abort,
- stream,
- body,
- client,
- request,
- client[kSocket],
- contentLength,
- expectsPayload
- )
- } else if (util.isBlobLike(body)) {
- if (typeof body.stream === 'function') {
- writeIterable(
- abort,
- stream,
- body.stream(),
- client,
- request,
- client[kSocket],
- contentLength,
- expectsPayload
- )
- } else {
- writeBlob(
- abort,
- stream,
- body,
- client,
- request,
- client[kSocket],
- contentLength,
- expectsPayload
- )
- }
- } else if (util.isStream(body)) {
- writeStream(
- abort,
- client[kSocket],
- expectsPayload,
- stream,
- body,
- client,
- request,
- contentLength
- )
- } else if (util.isIterable(body)) {
- writeIterable(
- abort,
- stream,
- body,
- client,
- request,
- client[kSocket],
- contentLength,
- expectsPayload
- )
- } else {
- assert(false)
- }
- }
-}
-
-function writeBuffer (abort, h2stream, body, client, request, socket, contentLength, expectsPayload) {
- try {
- if (body != null && util.isBuffer(body)) {
- assert(contentLength === body.byteLength, 'buffer body must have content length')
- h2stream.cork()
- h2stream.write(body)
- h2stream.uncork()
- h2stream.end()
-
- request.onBodySent(body)
- }
-
- if (!expectsPayload) {
- socket[kReset] = true
- }
-
- request.onRequestSent()
- client[kResume]()
- } catch (error) {
- abort(error)
- }
-}
-
-function writeStream (abort, socket, expectsPayload, h2stream, body, client, request, contentLength) {
- assert(contentLength !== 0 || client[kRunning] === 0, 'stream body cannot be pipelined')
-
- // For HTTP/2, is enough to pipe the stream
- const pipe = pipeline(
- body,
- h2stream,
- (err) => {
- if (err) {
- util.destroy(pipe, err)
- abort(err)
- } else {
- util.removeAllListeners(pipe)
- request.onRequestSent()
-
- if (!expectsPayload) {
- socket[kReset] = true
- }
-
- client[kResume]()
- }
- }
- )
-
- util.addListener(pipe, 'data', onPipeData)
-
- function onPipeData (chunk) {
- request.onBodySent(chunk)
- }
-}
-
-async function writeBlob (abort, h2stream, body, client, request, socket, contentLength, expectsPayload) {
- assert(contentLength === body.size, 'blob body must have content length')
-
- try {
- if (contentLength != null && contentLength !== body.size) {
- throw new RequestContentLengthMismatchError()
- }
-
- const buffer = Buffer.from(await body.arrayBuffer())
-
- h2stream.cork()
- h2stream.write(buffer)
- h2stream.uncork()
- h2stream.end()
-
- request.onBodySent(buffer)
- request.onRequestSent()
-
- if (!expectsPayload) {
- socket[kReset] = true
- }
-
- client[kResume]()
- } catch (err) {
- abort(err)
- }
-}
-
-async function writeIterable (abort, h2stream, body, client, request, socket, contentLength, expectsPayload) {
- assert(contentLength !== 0 || client[kRunning] === 0, 'iterator body cannot be pipelined')
-
- let callback = null
- function onDrain () {
- if (callback) {
- const cb = callback
- callback = null
- cb()
- }
- }
-
- const waitForDrain = () => new Promise((resolve, reject) => {
- assert(callback === null)
-
- if (socket[kError]) {
- reject(socket[kError])
- } else {
- callback = resolve
- }
- })
-
- h2stream
- .on('close', onDrain)
- .on('drain', onDrain)
-
- try {
- // It's up to the user to somehow abort the async iterable.
- for await (const chunk of body) {
- if (socket[kError]) {
- throw socket[kError]
- }
-
- const res = h2stream.write(chunk)
- request.onBodySent(chunk)
- if (!res) {
- await waitForDrain()
- }
- }
-
- h2stream.end()
-
- request.onRequestSent()
-
- if (!expectsPayload) {
- socket[kReset] = true
- }
-
- client[kResume]()
- } catch (err) {
- abort(err)
- } finally {
- h2stream
- .off('close', onDrain)
- .off('drain', onDrain)
- }
-}
-
-module.exports = connectH2
diff --git a/vanilla/node_modules/undici/lib/dispatcher/client.js b/vanilla/node_modules/undici/lib/dispatcher/client.js
deleted file mode 100644
index 101acb6..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/client.js
+++ /dev/null
@@ -1,647 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const net = require('node:net')
-const http = require('node:http')
-const util = require('../core/util.js')
-const { ClientStats } = require('../util/stats.js')
-const { channels } = require('../core/diagnostics.js')
-const Request = require('../core/request.js')
-const DispatcherBase = require('./dispatcher-base')
-const {
- InvalidArgumentError,
- InformationalError,
- ClientDestroyedError
-} = require('../core/errors.js')
-const buildConnector = require('../core/connect.js')
-const {
- kUrl,
- kServerName,
- kClient,
- kBusy,
- kConnect,
- kResuming,
- kRunning,
- kPending,
- kSize,
- kQueue,
- kConnected,
- kConnecting,
- kNeedDrain,
- kKeepAliveDefaultTimeout,
- kHostHeader,
- kPendingIdx,
- kRunningIdx,
- kError,
- kPipelining,
- kKeepAliveTimeoutValue,
- kMaxHeadersSize,
- kKeepAliveMaxTimeout,
- kKeepAliveTimeoutThreshold,
- kHeadersTimeout,
- kBodyTimeout,
- kStrictContentLength,
- kConnector,
- kMaxRequests,
- kCounter,
- kClose,
- kDestroy,
- kDispatch,
- kLocalAddress,
- kMaxResponseSize,
- kOnError,
- kHTTPContext,
- kMaxConcurrentStreams,
- kHTTP2InitialWindowSize,
- kHTTP2ConnectionWindowSize,
- kResume,
- kPingInterval
-} = require('../core/symbols.js')
-const connectH1 = require('./client-h1.js')
-const connectH2 = require('./client-h2.js')
-
-const kClosedResolve = Symbol('kClosedResolve')
-
-const getDefaultNodeMaxHeaderSize = http &&
- http.maxHeaderSize &&
- Number.isInteger(http.maxHeaderSize) &&
- http.maxHeaderSize > 0
- ? () => http.maxHeaderSize
- : () => { throw new InvalidArgumentError('http module not available or http.maxHeaderSize invalid') }
-
-const noop = () => {}
-
-function getPipelining (client) {
- return client[kPipelining] ?? client[kHTTPContext]?.defaultPipelining ?? 1
-}
-
-/**
- * @type {import('../../types/client.js').default}
- */
-class Client extends DispatcherBase {
- /**
- *
- * @param {string|URL} url
- * @param {import('../../types/client.js').Client.Options} options
- */
- constructor (url, {
- maxHeaderSize,
- headersTimeout,
- socketTimeout,
- requestTimeout,
- connectTimeout,
- bodyTimeout,
- idleTimeout,
- keepAlive,
- keepAliveTimeout,
- maxKeepAliveTimeout,
- keepAliveMaxTimeout,
- keepAliveTimeoutThreshold,
- socketPath,
- pipelining,
- tls,
- strictContentLength,
- maxCachedSessions,
- connect,
- maxRequestsPerClient,
- localAddress,
- maxResponseSize,
- autoSelectFamily,
- autoSelectFamilyAttemptTimeout,
- // h2
- maxConcurrentStreams,
- allowH2,
- useH2c,
- initialWindowSize,
- connectionWindowSize,
- pingInterval
- } = {}) {
- if (keepAlive !== undefined) {
- throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')
- }
-
- if (socketTimeout !== undefined) {
- throw new InvalidArgumentError('unsupported socketTimeout, use headersTimeout & bodyTimeout instead')
- }
-
- if (requestTimeout !== undefined) {
- throw new InvalidArgumentError('unsupported requestTimeout, use headersTimeout & bodyTimeout instead')
- }
-
- if (idleTimeout !== undefined) {
- throw new InvalidArgumentError('unsupported idleTimeout, use keepAliveTimeout instead')
- }
-
- if (maxKeepAliveTimeout !== undefined) {
- throw new InvalidArgumentError('unsupported maxKeepAliveTimeout, use keepAliveMaxTimeout instead')
- }
-
- if (maxHeaderSize != null) {
- if (!Number.isInteger(maxHeaderSize) || maxHeaderSize < 1) {
- throw new InvalidArgumentError('invalid maxHeaderSize')
- }
- } else {
- // If maxHeaderSize is not provided, use the default value from the http module
- // or if that is not available, throw an error.
- maxHeaderSize = getDefaultNodeMaxHeaderSize()
- }
-
- if (socketPath != null && typeof socketPath !== 'string') {
- throw new InvalidArgumentError('invalid socketPath')
- }
-
- if (connectTimeout != null && (!Number.isFinite(connectTimeout) || connectTimeout < 0)) {
- throw new InvalidArgumentError('invalid connectTimeout')
- }
-
- if (keepAliveTimeout != null && (!Number.isFinite(keepAliveTimeout) || keepAliveTimeout <= 0)) {
- throw new InvalidArgumentError('invalid keepAliveTimeout')
- }
-
- if (keepAliveMaxTimeout != null && (!Number.isFinite(keepAliveMaxTimeout) || keepAliveMaxTimeout <= 0)) {
- throw new InvalidArgumentError('invalid keepAliveMaxTimeout')
- }
-
- if (keepAliveTimeoutThreshold != null && !Number.isFinite(keepAliveTimeoutThreshold)) {
- throw new InvalidArgumentError('invalid keepAliveTimeoutThreshold')
- }
-
- if (headersTimeout != null && (!Number.isInteger(headersTimeout) || headersTimeout < 0)) {
- throw new InvalidArgumentError('headersTimeout must be a positive integer or zero')
- }
-
- if (bodyTimeout != null && (!Number.isInteger(bodyTimeout) || bodyTimeout < 0)) {
- throw new InvalidArgumentError('bodyTimeout must be a positive integer or zero')
- }
-
- if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {
- throw new InvalidArgumentError('connect must be a function or an object')
- }
-
- if (maxRequestsPerClient != null && (!Number.isInteger(maxRequestsPerClient) || maxRequestsPerClient < 0)) {
- throw new InvalidArgumentError('maxRequestsPerClient must be a positive number')
- }
-
- if (localAddress != null && (typeof localAddress !== 'string' || net.isIP(localAddress) === 0)) {
- throw new InvalidArgumentError('localAddress must be valid string IP address')
- }
-
- if (maxResponseSize != null && (!Number.isInteger(maxResponseSize) || maxResponseSize < -1)) {
- throw new InvalidArgumentError('maxResponseSize must be a positive number')
- }
-
- if (
- autoSelectFamilyAttemptTimeout != null &&
- (!Number.isInteger(autoSelectFamilyAttemptTimeout) || autoSelectFamilyAttemptTimeout < -1)
- ) {
- throw new InvalidArgumentError('autoSelectFamilyAttemptTimeout must be a positive number')
- }
-
- // h2
- if (allowH2 != null && typeof allowH2 !== 'boolean') {
- throw new InvalidArgumentError('allowH2 must be a valid boolean value')
- }
-
- if (maxConcurrentStreams != null && (typeof maxConcurrentStreams !== 'number' || maxConcurrentStreams < 1)) {
- throw new InvalidArgumentError('maxConcurrentStreams must be a positive integer, greater than 0')
- }
-
- if (useH2c != null && typeof useH2c !== 'boolean') {
- throw new InvalidArgumentError('useH2c must be a valid boolean value')
- }
-
- if (initialWindowSize != null && (!Number.isInteger(initialWindowSize) || initialWindowSize < 1)) {
- throw new InvalidArgumentError('initialWindowSize must be a positive integer, greater than 0')
- }
-
- if (connectionWindowSize != null && (!Number.isInteger(connectionWindowSize) || connectionWindowSize < 1)) {
- throw new InvalidArgumentError('connectionWindowSize must be a positive integer, greater than 0')
- }
-
- if (pingInterval != null && (typeof pingInterval !== 'number' || !Number.isInteger(pingInterval) || pingInterval < 0)) {
- throw new InvalidArgumentError('pingInterval must be a positive integer, greater or equal to 0')
- }
-
- super()
-
- if (typeof connect !== 'function') {
- connect = buildConnector({
- ...tls,
- maxCachedSessions,
- allowH2,
- useH2c,
- socketPath,
- timeout: connectTimeout,
- ...(typeof autoSelectFamily === 'boolean' ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : undefined),
- ...connect
- })
- }
-
- this[kUrl] = util.parseOrigin(url)
- this[kConnector] = connect
- this[kPipelining] = pipelining != null ? pipelining : 1
- this[kMaxHeadersSize] = maxHeaderSize
- this[kKeepAliveDefaultTimeout] = keepAliveTimeout == null ? 4e3 : keepAliveTimeout
- this[kKeepAliveMaxTimeout] = keepAliveMaxTimeout == null ? 600e3 : keepAliveMaxTimeout
- this[kKeepAliveTimeoutThreshold] = keepAliveTimeoutThreshold == null ? 2e3 : keepAliveTimeoutThreshold
- this[kKeepAliveTimeoutValue] = this[kKeepAliveDefaultTimeout]
- this[kServerName] = null
- this[kLocalAddress] = localAddress != null ? localAddress : null
- this[kResuming] = 0 // 0, idle, 1, scheduled, 2 resuming
- this[kNeedDrain] = 0 // 0, idle, 1, scheduled, 2 resuming
- this[kHostHeader] = `host: ${this[kUrl].hostname}${this[kUrl].port ? `:${this[kUrl].port}` : ''}\r\n`
- this[kBodyTimeout] = bodyTimeout != null ? bodyTimeout : 300e3
- this[kHeadersTimeout] = headersTimeout != null ? headersTimeout : 300e3
- this[kStrictContentLength] = strictContentLength == null ? true : strictContentLength
- this[kMaxRequests] = maxRequestsPerClient
- this[kClosedResolve] = null
- this[kMaxResponseSize] = maxResponseSize > -1 ? maxResponseSize : -1
- this[kHTTPContext] = null
- // h2
- this[kMaxConcurrentStreams] = maxConcurrentStreams != null ? maxConcurrentStreams : 100 // Max peerConcurrentStreams for a Node h2 server
- // HTTP/2 window sizes are set to higher defaults than Node.js core for better performance:
- // - initialWindowSize: 262144 (256KB) vs Node.js default 65535 (64KB - 1)
- // Allows more data to be sent before requiring acknowledgment, improving throughput
- // especially on high-latency networks. This matches common production HTTP/2 servers.
- // - connectionWindowSize: 524288 (512KB) vs Node.js default (none set)
- // Provides better flow control for the entire connection across multiple streams.
- this[kHTTP2InitialWindowSize] = initialWindowSize != null ? initialWindowSize : 262144
- this[kHTTP2ConnectionWindowSize] = connectionWindowSize != null ? connectionWindowSize : 524288
- this[kPingInterval] = pingInterval != null ? pingInterval : 60e3 // Default ping interval for h2 - 1 minute
-
- // kQueue is built up of 3 sections separated by
- // the kRunningIdx and kPendingIdx indices.
- // | complete | running | pending |
- // ^ kRunningIdx ^ kPendingIdx ^ kQueue.length
- // kRunningIdx points to the first running element.
- // kPendingIdx points to the first pending element.
- // This implements a fast queue with an amortized
- // time of O(1).
-
- this[kQueue] = []
- this[kRunningIdx] = 0
- this[kPendingIdx] = 0
-
- this[kResume] = (sync) => resume(this, sync)
- this[kOnError] = (err) => onError(this, err)
- }
-
- get pipelining () {
- return this[kPipelining]
- }
-
- set pipelining (value) {
- this[kPipelining] = value
- this[kResume](true)
- }
-
- get stats () {
- return new ClientStats(this)
- }
-
- get [kPending] () {
- return this[kQueue].length - this[kPendingIdx]
- }
-
- get [kRunning] () {
- return this[kPendingIdx] - this[kRunningIdx]
- }
-
- get [kSize] () {
- return this[kQueue].length - this[kRunningIdx]
- }
-
- get [kConnected] () {
- return !!this[kHTTPContext] && !this[kConnecting] && !this[kHTTPContext].destroyed
- }
-
- get [kBusy] () {
- return Boolean(
- this[kHTTPContext]?.busy(null) ||
- (this[kSize] >= (getPipelining(this) || 1)) ||
- this[kPending] > 0
- )
- }
-
- [kConnect] (cb) {
- connect(this)
- this.once('connect', cb)
- }
-
- [kDispatch] (opts, handler) {
- const request = new Request(this[kUrl].origin, opts, handler)
-
- this[kQueue].push(request)
- if (this[kResuming]) {
- // Do nothing.
- } else if (util.bodyLength(request.body) == null && util.isIterable(request.body)) {
- // Wait a tick in case stream/iterator is ended in the same tick.
- this[kResuming] = 1
- queueMicrotask(() => resume(this))
- } else {
- this[kResume](true)
- }
-
- if (this[kResuming] && this[kNeedDrain] !== 2 && this[kBusy]) {
- this[kNeedDrain] = 2
- }
-
- return this[kNeedDrain] < 2
- }
-
- [kClose] () {
- // TODO: for H2 we need to gracefully flush the remaining enqueued
- // request and close each stream.
- return new Promise((resolve) => {
- if (this[kSize]) {
- this[kClosedResolve] = resolve
- } else {
- resolve(null)
- }
- })
- }
-
- [kDestroy] (err) {
- return new Promise((resolve) => {
- const requests = this[kQueue].splice(this[kPendingIdx])
- for (let i = 0; i < requests.length; i++) {
- const request = requests[i]
- util.errorRequest(this, request, err)
- }
-
- const callback = () => {
- if (this[kClosedResolve]) {
- // TODO (fix): Should we error here with ClientDestroyedError?
- this[kClosedResolve]()
- this[kClosedResolve] = null
- }
- resolve(null)
- }
-
- if (this[kHTTPContext]) {
- this[kHTTPContext].destroy(err, callback)
- this[kHTTPContext] = null
- } else {
- queueMicrotask(callback)
- }
-
- this[kResume]()
- })
- }
-}
-
-function onError (client, err) {
- if (
- client[kRunning] === 0 &&
- err.code !== 'UND_ERR_INFO' &&
- err.code !== 'UND_ERR_SOCKET'
- ) {
- // Error is not caused by running request and not a recoverable
- // socket error.
-
- assert(client[kPendingIdx] === client[kRunningIdx])
-
- const requests = client[kQueue].splice(client[kRunningIdx])
-
- for (let i = 0; i < requests.length; i++) {
- const request = requests[i]
- util.errorRequest(client, request, err)
- }
- assert(client[kSize] === 0)
- }
-}
-
-/**
- * @param {Client} client
- * @returns {void}
- */
-function connect (client) {
- assert(!client[kConnecting])
- assert(!client[kHTTPContext])
-
- let { host, hostname, protocol, port } = client[kUrl]
-
- // Resolve ipv6
- if (hostname[0] === '[') {
- const idx = hostname.indexOf(']')
-
- assert(idx !== -1)
- const ip = hostname.substring(1, idx)
-
- assert(net.isIPv6(ip))
- hostname = ip
- }
-
- client[kConnecting] = true
-
- if (channels.beforeConnect.hasSubscribers) {
- channels.beforeConnect.publish({
- connectParams: {
- host,
- hostname,
- protocol,
- port,
- version: client[kHTTPContext]?.version,
- servername: client[kServerName],
- localAddress: client[kLocalAddress]
- },
- connector: client[kConnector]
- })
- }
-
- client[kConnector]({
- host,
- hostname,
- protocol,
- port,
- servername: client[kServerName],
- localAddress: client[kLocalAddress]
- }, (err, socket) => {
- if (err) {
- handleConnectError(client, err, { host, hostname, protocol, port })
- client[kResume]()
- return
- }
-
- if (client.destroyed) {
- util.destroy(socket.on('error', noop), new ClientDestroyedError())
- client[kResume]()
- return
- }
-
- assert(socket)
-
- try {
- client[kHTTPContext] = socket.alpnProtocol === 'h2'
- ? connectH2(client, socket)
- : connectH1(client, socket)
- } catch (err) {
- socket.destroy().on('error', noop)
- handleConnectError(client, err, { host, hostname, protocol, port })
- client[kResume]()
- return
- }
-
- client[kConnecting] = false
-
- socket[kCounter] = 0
- socket[kMaxRequests] = client[kMaxRequests]
- socket[kClient] = client
- socket[kError] = null
-
- if (channels.connected.hasSubscribers) {
- channels.connected.publish({
- connectParams: {
- host,
- hostname,
- protocol,
- port,
- version: client[kHTTPContext]?.version,
- servername: client[kServerName],
- localAddress: client[kLocalAddress]
- },
- connector: client[kConnector],
- socket
- })
- }
-
- client.emit('connect', client[kUrl], [client])
- client[kResume]()
- })
-}
-
-function handleConnectError (client, err, { host, hostname, protocol, port }) {
- if (client.destroyed) {
- return
- }
-
- client[kConnecting] = false
-
- if (channels.connectError.hasSubscribers) {
- channels.connectError.publish({
- connectParams: {
- host,
- hostname,
- protocol,
- port,
- version: client[kHTTPContext]?.version,
- servername: client[kServerName],
- localAddress: client[kLocalAddress]
- },
- connector: client[kConnector],
- error: err
- })
- }
-
- if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {
- assert(client[kRunning] === 0)
- while (client[kPending] > 0 && client[kQueue][client[kPendingIdx]].servername === client[kServerName]) {
- const request = client[kQueue][client[kPendingIdx]++]
- util.errorRequest(client, request, err)
- }
- } else {
- onError(client, err)
- }
-
- client.emit('connectionError', client[kUrl], [client], err)
-}
-
-function emitDrain (client) {
- client[kNeedDrain] = 0
- client.emit('drain', client[kUrl], [client])
-}
-
-function resume (client, sync) {
- if (client[kResuming] === 2) {
- return
- }
-
- client[kResuming] = 2
-
- _resume(client, sync)
- client[kResuming] = 0
-
- if (client[kRunningIdx] > 256) {
- client[kQueue].splice(0, client[kRunningIdx])
- client[kPendingIdx] -= client[kRunningIdx]
- client[kRunningIdx] = 0
- }
-}
-
-function _resume (client, sync) {
- while (true) {
- if (client.destroyed) {
- assert(client[kPending] === 0)
- return
- }
-
- if (client[kClosedResolve] && !client[kSize]) {
- client[kClosedResolve]()
- client[kClosedResolve] = null
- return
- }
-
- if (client[kHTTPContext]) {
- client[kHTTPContext].resume()
- }
-
- if (client[kBusy]) {
- client[kNeedDrain] = 2
- } else if (client[kNeedDrain] === 2) {
- if (sync) {
- client[kNeedDrain] = 1
- queueMicrotask(() => emitDrain(client))
- } else {
- emitDrain(client)
- }
- continue
- }
-
- if (client[kPending] === 0) {
- return
- }
-
- if (client[kRunning] >= (getPipelining(client) || 1)) {
- return
- }
-
- const request = client[kQueue][client[kPendingIdx]]
-
- if (client[kUrl].protocol === 'https:' && client[kServerName] !== request.servername) {
- if (client[kRunning] > 0) {
- return
- }
-
- client[kServerName] = request.servername
- client[kHTTPContext]?.destroy(new InformationalError('servername changed'), () => {
- client[kHTTPContext] = null
- resume(client)
- })
- }
-
- if (client[kConnecting]) {
- return
- }
-
- if (!client[kHTTPContext]) {
- connect(client)
- return
- }
-
- if (client[kHTTPContext].destroyed) {
- return
- }
-
- if (client[kHTTPContext].busy(request)) {
- return
- }
-
- if (!request.aborted && client[kHTTPContext].write(request)) {
- client[kPendingIdx]++
- } else {
- client[kQueue].splice(client[kPendingIdx], 1)
- }
- }
-}
-
-module.exports = Client
diff --git a/vanilla/node_modules/undici/lib/dispatcher/dispatcher-base.js b/vanilla/node_modules/undici/lib/dispatcher/dispatcher-base.js
deleted file mode 100644
index a6f4710..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/dispatcher-base.js
+++ /dev/null
@@ -1,165 +0,0 @@
-'use strict'
-
-const Dispatcher = require('./dispatcher')
-const UnwrapHandler = require('../handler/unwrap-handler')
-const {
- ClientDestroyedError,
- ClientClosedError,
- InvalidArgumentError
-} = require('../core/errors')
-const { kDestroy, kClose, kClosed, kDestroyed, kDispatch } = require('../core/symbols')
-
-const kOnDestroyed = Symbol('onDestroyed')
-const kOnClosed = Symbol('onClosed')
-
-class DispatcherBase extends Dispatcher {
- /** @type {boolean} */
- [kDestroyed] = false;
-
- /** @type {Array<Function|null} */
- [kOnDestroyed] = null;
-
- /** @type {boolean} */
- [kClosed] = false;
-
- /** @type {Array<Function>|null} */
- [kOnClosed] = null
-
- /** @returns {boolean} */
- get destroyed () {
- return this[kDestroyed]
- }
-
- /** @returns {boolean} */
- get closed () {
- return this[kClosed]
- }
-
- close (callback) {
- if (callback === undefined) {
- return new Promise((resolve, reject) => {
- this.close((err, data) => {
- return err ? reject(err) : resolve(data)
- })
- })
- }
-
- if (typeof callback !== 'function') {
- throw new InvalidArgumentError('invalid callback')
- }
-
- if (this[kDestroyed]) {
- const err = new ClientDestroyedError()
- queueMicrotask(() => callback(err, null))
- return
- }
-
- if (this[kClosed]) {
- if (this[kOnClosed]) {
- this[kOnClosed].push(callback)
- } else {
- queueMicrotask(() => callback(null, null))
- }
- return
- }
-
- this[kClosed] = true
- this[kOnClosed] ??= []
- this[kOnClosed].push(callback)
-
- const onClosed = () => {
- const callbacks = this[kOnClosed]
- this[kOnClosed] = null
- for (let i = 0; i < callbacks.length; i++) {
- callbacks[i](null, null)
- }
- }
-
- // Should not error.
- this[kClose]()
- .then(() => this.destroy())
- .then(() => queueMicrotask(onClosed))
- }
-
- destroy (err, callback) {
- if (typeof err === 'function') {
- callback = err
- err = null
- }
-
- if (callback === undefined) {
- return new Promise((resolve, reject) => {
- this.destroy(err, (err, data) => {
- return err ? reject(err) : resolve(data)
- })
- })
- }
-
- if (typeof callback !== 'function') {
- throw new InvalidArgumentError('invalid callback')
- }
-
- if (this[kDestroyed]) {
- if (this[kOnDestroyed]) {
- this[kOnDestroyed].push(callback)
- } else {
- queueMicrotask(() => callback(null, null))
- }
- return
- }
-
- if (!err) {
- err = new ClientDestroyedError()
- }
-
- this[kDestroyed] = true
- this[kOnDestroyed] ??= []
- this[kOnDestroyed].push(callback)
-
- const onDestroyed = () => {
- const callbacks = this[kOnDestroyed]
- this[kOnDestroyed] = null
- for (let i = 0; i < callbacks.length; i++) {
- callbacks[i](null, null)
- }
- }
-
- // Should not error.
- this[kDestroy](err)
- .then(() => queueMicrotask(onDestroyed))
- }
-
- dispatch (opts, handler) {
- if (!handler || typeof handler !== 'object') {
- throw new InvalidArgumentError('handler must be an object')
- }
-
- handler = UnwrapHandler.unwrap(handler)
-
- try {
- if (!opts || typeof opts !== 'object') {
- throw new InvalidArgumentError('opts must be an object.')
- }
-
- if (this[kDestroyed] || this[kOnDestroyed]) {
- throw new ClientDestroyedError()
- }
-
- if (this[kClosed]) {
- throw new ClientClosedError()
- }
-
- return this[kDispatch](opts, handler)
- } catch (err) {
- if (typeof handler.onError !== 'function') {
- throw err
- }
-
- handler.onError(err)
-
- return false
- }
- }
-}
-
-module.exports = DispatcherBase
diff --git a/vanilla/node_modules/undici/lib/dispatcher/dispatcher.js b/vanilla/node_modules/undici/lib/dispatcher/dispatcher.js
deleted file mode 100644
index 824dfb6..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/dispatcher.js
+++ /dev/null
@@ -1,48 +0,0 @@
-'use strict'
-const EventEmitter = require('node:events')
-const WrapHandler = require('../handler/wrap-handler')
-
-const wrapInterceptor = (dispatch) => (opts, handler) => dispatch(opts, WrapHandler.wrap(handler))
-
-class Dispatcher extends EventEmitter {
- dispatch () {
- throw new Error('not implemented')
- }
-
- close () {
- throw new Error('not implemented')
- }
-
- destroy () {
- throw new Error('not implemented')
- }
-
- compose (...args) {
- // So we handle [interceptor1, interceptor2] or interceptor1, interceptor2, ...
- const interceptors = Array.isArray(args[0]) ? args[0] : args
- let dispatch = this.dispatch.bind(this)
-
- for (const interceptor of interceptors) {
- if (interceptor == null) {
- continue
- }
-
- if (typeof interceptor !== 'function') {
- throw new TypeError(`invalid interceptor, expected function received ${typeof interceptor}`)
- }
-
- dispatch = interceptor(dispatch)
- dispatch = wrapInterceptor(dispatch)
-
- if (dispatch == null || typeof dispatch !== 'function' || dispatch.length !== 2) {
- throw new TypeError('invalid interceptor')
- }
- }
-
- return new Proxy(this, {
- get: (target, key) => key === 'dispatch' ? dispatch : target[key]
- })
- }
-}
-
-module.exports = Dispatcher
diff --git a/vanilla/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js b/vanilla/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js
deleted file mode 100644
index f88437f..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js
+++ /dev/null
@@ -1,146 +0,0 @@
-'use strict'
-
-const DispatcherBase = require('./dispatcher-base')
-const { kClose, kDestroy, kClosed, kDestroyed, kDispatch, kNoProxyAgent, kHttpProxyAgent, kHttpsProxyAgent } = require('../core/symbols')
-const ProxyAgent = require('./proxy-agent')
-const Agent = require('./agent')
-
-const DEFAULT_PORTS = {
- 'http:': 80,
- 'https:': 443
-}
-
-class EnvHttpProxyAgent extends DispatcherBase {
- #noProxyValue = null
- #noProxyEntries = null
- #opts = null
-
- constructor (opts = {}) {
- super()
- this.#opts = opts
-
- const { httpProxy, httpsProxy, noProxy, ...agentOpts } = opts
-
- this[kNoProxyAgent] = new Agent(agentOpts)
-
- const HTTP_PROXY = httpProxy ?? process.env.http_proxy ?? process.env.HTTP_PROXY
- if (HTTP_PROXY) {
- this[kHttpProxyAgent] = new ProxyAgent({ ...agentOpts, uri: HTTP_PROXY })
- } else {
- this[kHttpProxyAgent] = this[kNoProxyAgent]
- }
-
- const HTTPS_PROXY = httpsProxy ?? process.env.https_proxy ?? process.env.HTTPS_PROXY
- if (HTTPS_PROXY) {
- this[kHttpsProxyAgent] = new ProxyAgent({ ...agentOpts, uri: HTTPS_PROXY })
- } else {
- this[kHttpsProxyAgent] = this[kHttpProxyAgent]
- }
-
- this.#parseNoProxy()
- }
-
- [kDispatch] (opts, handler) {
- const url = new URL(opts.origin)
- const agent = this.#getProxyAgentForUrl(url)
- return agent.dispatch(opts, handler)
- }
-
- [kClose] () {
- return Promise.all([
- this[kNoProxyAgent].close(),
- !this[kHttpProxyAgent][kClosed] && this[kHttpProxyAgent].close(),
- !this[kHttpsProxyAgent][kClosed] && this[kHttpsProxyAgent].close()
- ])
- }
-
- [kDestroy] (err) {
- return Promise.all([
- this[kNoProxyAgent].destroy(err),
- !this[kHttpProxyAgent][kDestroyed] && this[kHttpProxyAgent].destroy(err),
- !this[kHttpsProxyAgent][kDestroyed] && this[kHttpsProxyAgent].destroy(err)
- ])
- }
-
- #getProxyAgentForUrl (url) {
- let { protocol, host: hostname, port } = url
-
- // Stripping ports in this way instead of using parsedUrl.hostname to make
- // sure that the brackets around IPv6 addresses are kept.
- hostname = hostname.replace(/:\d*$/, '').toLowerCase()
- port = Number.parseInt(port, 10) || DEFAULT_PORTS[protocol] || 0
- if (!this.#shouldProxy(hostname, port)) {
- return this[kNoProxyAgent]
- }
- if (protocol === 'https:') {
- return this[kHttpsProxyAgent]
- }
- return this[kHttpProxyAgent]
- }
-
- #shouldProxy (hostname, port) {
- if (this.#noProxyChanged) {
- this.#parseNoProxy()
- }
-
- if (this.#noProxyEntries.length === 0) {
- return true // Always proxy if NO_PROXY is not set or empty.
- }
- if (this.#noProxyValue === '*') {
- return false // Never proxy if wildcard is set.
- }
-
- for (let i = 0; i < this.#noProxyEntries.length; i++) {
- const entry = this.#noProxyEntries[i]
- if (entry.port && entry.port !== port) {
- continue // Skip if ports don't match.
- }
- // Don't proxy if the hostname is equal with the no_proxy host.
- if (hostname === entry.hostname) {
- return false
- }
- // Don't proxy if the hostname is the subdomain of the no_proxy host.
- // Reference - https://github.com/denoland/deno/blob/6fbce91e40cc07fc6da74068e5cc56fdd40f7b4c/ext/fetch/proxy.rs#L485
- if (hostname.slice(-(entry.hostname.length + 1)) === `.${entry.hostname}`) {
- return false
- }
- }
-
- return true
- }
-
- #parseNoProxy () {
- const noProxyValue = this.#opts.noProxy ?? this.#noProxyEnv
- const noProxySplit = noProxyValue.split(/[,\s]/)
- const noProxyEntries = []
-
- for (let i = 0; i < noProxySplit.length; i++) {
- const entry = noProxySplit[i]
- if (!entry) {
- continue
- }
- const parsed = entry.match(/^(.+):(\d+)$/)
- noProxyEntries.push({
- // strip leading dot or asterisk with dot
- hostname: (parsed ? parsed[1] : entry).replace(/^\*?\./, '').toLowerCase(),
- port: parsed ? Number.parseInt(parsed[2], 10) : 0
- })
- }
-
- this.#noProxyValue = noProxyValue
- this.#noProxyEntries = noProxyEntries
- }
-
- get #noProxyChanged () {
- if (this.#opts.noProxy !== undefined) {
- return false
- }
- return this.#noProxyValue !== this.#noProxyEnv
- }
-
- get #noProxyEnv () {
- return process.env.no_proxy ?? process.env.NO_PROXY ?? ''
- }
-}
-
-module.exports = EnvHttpProxyAgent
diff --git a/vanilla/node_modules/undici/lib/dispatcher/fixed-queue.js b/vanilla/node_modules/undici/lib/dispatcher/fixed-queue.js
deleted file mode 100644
index f918849..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/fixed-queue.js
+++ /dev/null
@@ -1,135 +0,0 @@
-'use strict'
-
-// Extracted from node/lib/internal/fixed_queue.js
-
-// Currently optimal queue size, tested on V8 6.0 - 6.6. Must be power of two.
-const kSize = 2048
-const kMask = kSize - 1
-
-// The FixedQueue is implemented as a singly-linked list of fixed-size
-// circular buffers. It looks something like this:
-//
-// head tail
-// | |
-// v v
-// +-----------+ <-----\ +-----------+ <------\ +-----------+
-// | [null] | \----- | next | \------- | next |
-// +-----------+ +-----------+ +-----------+
-// | item | <-- bottom | item | <-- bottom | undefined |
-// | item | | item | | undefined |
-// | item | | item | | undefined |
-// | item | | item | | undefined |
-// | item | | item | bottom --> | item |
-// | item | | item | | item |
-// | ... | | ... | | ... |
-// | item | | item | | item |
-// | item | | item | | item |
-// | undefined | <-- top | item | | item |
-// | undefined | | item | | item |
-// | undefined | | undefined | <-- top top --> | undefined |
-// +-----------+ +-----------+ +-----------+
-//
-// Or, if there is only one circular buffer, it looks something
-// like either of these:
-//
-// head tail head tail
-// | | | |
-// v v v v
-// +-----------+ +-----------+
-// | [null] | | [null] |
-// +-----------+ +-----------+
-// | undefined | | item |
-// | undefined | | item |
-// | item | <-- bottom top --> | undefined |
-// | item | | undefined |
-// | undefined | <-- top bottom --> | item |
-// | undefined | | item |
-// +-----------+ +-----------+
-//
-// Adding a value means moving `top` forward by one, removing means
-// moving `bottom` forward by one. After reaching the end, the queue
-// wraps around.
-//
-// When `top === bottom` the current queue is empty and when
-// `top + 1 === bottom` it's full. This wastes a single space of storage
-// but allows much quicker checks.
-
-/**
- * @type {FixedCircularBuffer}
- * @template T
- */
-class FixedCircularBuffer {
- /** @type {number} */
- bottom = 0
- /** @type {number} */
- top = 0
- /** @type {Array<T|undefined>} */
- list = new Array(kSize).fill(undefined)
- /** @type {T|null} */
- next = null
-
- /** @returns {boolean} */
- isEmpty () {
- return this.top === this.bottom
- }
-
- /** @returns {boolean} */
- isFull () {
- return ((this.top + 1) & kMask) === this.bottom
- }
-
- /**
- * @param {T} data
- * @returns {void}
- */
- push (data) {
- this.list[this.top] = data
- this.top = (this.top + 1) & kMask
- }
-
- /** @returns {T|null} */
- shift () {
- const nextItem = this.list[this.bottom]
- if (nextItem === undefined) { return null }
- this.list[this.bottom] = undefined
- this.bottom = (this.bottom + 1) & kMask
- return nextItem
- }
-}
-
-/**
- * @template T
- */
-module.exports = class FixedQueue {
- constructor () {
- /** @type {FixedCircularBuffer<T>} */
- this.head = this.tail = new FixedCircularBuffer()
- }
-
- /** @returns {boolean} */
- isEmpty () {
- return this.head.isEmpty()
- }
-
- /** @param {T} data */
- push (data) {
- if (this.head.isFull()) {
- // Head is full: Creates a new queue, sets the old queue's `.next` to it,
- // and sets it as the new main queue.
- this.head = this.head.next = new FixedCircularBuffer()
- }
- this.head.push(data)
- }
-
- /** @returns {T|null} */
- shift () {
- const tail = this.tail
- const next = tail.shift()
- if (tail.isEmpty() && tail.next !== null) {
- // If there is another queue, it forms the new tail.
- this.tail = tail.next
- tail.next = null
- }
- return next
- }
-}
diff --git a/vanilla/node_modules/undici/lib/dispatcher/h2c-client.js b/vanilla/node_modules/undici/lib/dispatcher/h2c-client.js
deleted file mode 100644
index bd38522..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/h2c-client.js
+++ /dev/null
@@ -1,51 +0,0 @@
-'use strict'
-
-const { InvalidArgumentError } = require('../core/errors')
-const Client = require('./client')
-
-class H2CClient extends Client {
- constructor (origin, clientOpts) {
- if (typeof origin === 'string') {
- origin = new URL(origin)
- }
-
- if (origin.protocol !== 'http:') {
- throw new InvalidArgumentError(
- 'h2c-client: Only h2c protocol is supported'
- )
- }
-
- const { connect, maxConcurrentStreams, pipelining, ...opts } =
- clientOpts ?? {}
- let defaultMaxConcurrentStreams = 100
- let defaultPipelining = 100
-
- if (
- maxConcurrentStreams != null &&
- Number.isInteger(maxConcurrentStreams) &&
- maxConcurrentStreams > 0
- ) {
- defaultMaxConcurrentStreams = maxConcurrentStreams
- }
-
- if (pipelining != null && Number.isInteger(pipelining) && pipelining > 0) {
- defaultPipelining = pipelining
- }
-
- if (defaultPipelining > defaultMaxConcurrentStreams) {
- throw new InvalidArgumentError(
- 'h2c-client: pipelining cannot be greater than maxConcurrentStreams'
- )
- }
-
- super(origin, {
- ...opts,
- maxConcurrentStreams: defaultMaxConcurrentStreams,
- pipelining: defaultPipelining,
- allowH2: true,
- useH2c: true
- })
- }
-}
-
-module.exports = H2CClient
diff --git a/vanilla/node_modules/undici/lib/dispatcher/pool-base.js b/vanilla/node_modules/undici/lib/dispatcher/pool-base.js
deleted file mode 100644
index 6c1f238..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/pool-base.js
+++ /dev/null
@@ -1,214 +0,0 @@
-'use strict'
-
-const { PoolStats } = require('../util/stats.js')
-const DispatcherBase = require('./dispatcher-base')
-const FixedQueue = require('./fixed-queue')
-const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = require('../core/symbols')
-
-const kClients = Symbol('clients')
-const kNeedDrain = Symbol('needDrain')
-const kQueue = Symbol('queue')
-const kClosedResolve = Symbol('closed resolve')
-const kOnDrain = Symbol('onDrain')
-const kOnConnect = Symbol('onConnect')
-const kOnDisconnect = Symbol('onDisconnect')
-const kOnConnectionError = Symbol('onConnectionError')
-const kGetDispatcher = Symbol('get dispatcher')
-const kAddClient = Symbol('add client')
-const kRemoveClient = Symbol('remove client')
-
-class PoolBase extends DispatcherBase {
- [kQueue] = new FixedQueue();
-
- [kQueued] = 0;
-
- [kClients] = [];
-
- [kNeedDrain] = false;
-
- [kOnDrain] (client, origin, targets) {
- const queue = this[kQueue]
-
- let needDrain = false
-
- while (!needDrain) {
- const item = queue.shift()
- if (!item) {
- break
- }
- this[kQueued]--
- needDrain = !client.dispatch(item.opts, item.handler)
- }
-
- client[kNeedDrain] = needDrain
-
- if (!needDrain && this[kNeedDrain]) {
- this[kNeedDrain] = false
- this.emit('drain', origin, [this, ...targets])
- }
-
- if (this[kClosedResolve] && queue.isEmpty()) {
- const closeAll = []
- for (let i = 0; i < this[kClients].length; i++) {
- const client = this[kClients][i]
- if (!client.destroyed) {
- closeAll.push(client.close())
- }
- }
- return Promise.all(closeAll)
- .then(this[kClosedResolve])
- }
- }
-
- [kOnConnect] = (origin, targets) => {
- this.emit('connect', origin, [this, ...targets])
- };
-
- [kOnDisconnect] = (origin, targets, err) => {
- this.emit('disconnect', origin, [this, ...targets], err)
- };
-
- [kOnConnectionError] = (origin, targets, err) => {
- this.emit('connectionError', origin, [this, ...targets], err)
- }
-
- get [kBusy] () {
- return this[kNeedDrain]
- }
-
- get [kConnected] () {
- let ret = 0
- for (const { [kConnected]: connected } of this[kClients]) {
- ret += connected
- }
- return ret
- }
-
- get [kFree] () {
- let ret = 0
- for (const { [kConnected]: connected, [kNeedDrain]: needDrain } of this[kClients]) {
- ret += connected && !needDrain
- }
- return ret
- }
-
- get [kPending] () {
- let ret = this[kQueued]
- for (const { [kPending]: pending } of this[kClients]) {
- ret += pending
- }
- return ret
- }
-
- get [kRunning] () {
- let ret = 0
- for (const { [kRunning]: running } of this[kClients]) {
- ret += running
- }
- return ret
- }
-
- get [kSize] () {
- let ret = this[kQueued]
- for (const { [kSize]: size } of this[kClients]) {
- ret += size
- }
- return ret
- }
-
- get stats () {
- return new PoolStats(this)
- }
-
- [kClose] () {
- if (this[kQueue].isEmpty()) {
- const closeAll = []
- for (let i = 0; i < this[kClients].length; i++) {
- const client = this[kClients][i]
- if (!client.destroyed) {
- closeAll.push(client.close())
- }
- }
- return Promise.all(closeAll)
- } else {
- return new Promise((resolve) => {
- this[kClosedResolve] = resolve
- })
- }
- }
-
- [kDestroy] (err) {
- while (true) {
- const item = this[kQueue].shift()
- if (!item) {
- break
- }
- item.handler.onError(err)
- }
-
- const destroyAll = new Array(this[kClients].length)
- for (let i = 0; i < this[kClients].length; i++) {
- destroyAll[i] = this[kClients][i].destroy(err)
- }
- return Promise.all(destroyAll)
- }
-
- [kDispatch] (opts, handler) {
- const dispatcher = this[kGetDispatcher]()
-
- if (!dispatcher) {
- this[kNeedDrain] = true
- this[kQueue].push({ opts, handler })
- this[kQueued]++
- } else if (!dispatcher.dispatch(opts, handler)) {
- dispatcher[kNeedDrain] = true
- this[kNeedDrain] = !this[kGetDispatcher]()
- }
-
- return !this[kNeedDrain]
- }
-
- [kAddClient] (client) {
- client
- .on('drain', this[kOnDrain].bind(this, client))
- .on('connect', this[kOnConnect])
- .on('disconnect', this[kOnDisconnect])
- .on('connectionError', this[kOnConnectionError])
-
- this[kClients].push(client)
-
- if (this[kNeedDrain]) {
- queueMicrotask(() => {
- if (this[kNeedDrain]) {
- this[kOnDrain](client, client[kUrl], [client, this])
- }
- })
- }
-
- return this
- }
-
- [kRemoveClient] (client) {
- client.close(() => {
- const idx = this[kClients].indexOf(client)
- if (idx !== -1) {
- this[kClients].splice(idx, 1)
- }
- })
-
- this[kNeedDrain] = this[kClients].some(dispatcher => (
- !dispatcher[kNeedDrain] &&
- dispatcher.closed !== true &&
- dispatcher.destroyed !== true
- ))
- }
-}
-
-module.exports = {
- PoolBase,
- kClients,
- kNeedDrain,
- kAddClient,
- kRemoveClient,
- kGetDispatcher
-}
diff --git a/vanilla/node_modules/undici/lib/dispatcher/pool.js b/vanilla/node_modules/undici/lib/dispatcher/pool.js
deleted file mode 100644
index 77fd532..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/pool.js
+++ /dev/null
@@ -1,118 +0,0 @@
-'use strict'
-
-const {
- PoolBase,
- kClients,
- kNeedDrain,
- kAddClient,
- kGetDispatcher,
- kRemoveClient
-} = require('./pool-base')
-const Client = require('./client')
-const {
- InvalidArgumentError
-} = require('../core/errors')
-const util = require('../core/util')
-const { kUrl } = require('../core/symbols')
-const buildConnector = require('../core/connect')
-
-const kOptions = Symbol('options')
-const kConnections = Symbol('connections')
-const kFactory = Symbol('factory')
-
-function defaultFactory (origin, opts) {
- return new Client(origin, opts)
-}
-
-class Pool extends PoolBase {
- constructor (origin, {
- connections,
- factory = defaultFactory,
- connect,
- connectTimeout,
- tls,
- maxCachedSessions,
- socketPath,
- autoSelectFamily,
- autoSelectFamilyAttemptTimeout,
- allowH2,
- clientTtl,
- ...options
- } = {}) {
- if (connections != null && (!Number.isFinite(connections) || connections < 0)) {
- throw new InvalidArgumentError('invalid connections')
- }
-
- if (typeof factory !== 'function') {
- throw new InvalidArgumentError('factory must be a function.')
- }
-
- if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {
- throw new InvalidArgumentError('connect must be a function or an object')
- }
-
- if (typeof connect !== 'function') {
- connect = buildConnector({
- ...tls,
- maxCachedSessions,
- allowH2,
- socketPath,
- timeout: connectTimeout,
- ...(typeof autoSelectFamily === 'boolean' ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : undefined),
- ...connect
- })
- }
-
- super()
-
- this[kConnections] = connections || null
- this[kUrl] = util.parseOrigin(origin)
- this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl }
- this[kOptions].interceptors = options.interceptors
- ? { ...options.interceptors }
- : undefined
- this[kFactory] = factory
-
- this.on('connect', (origin, targets) => {
- if (clientTtl != null && clientTtl > 0) {
- for (const target of targets) {
- Object.assign(target, { ttl: Date.now() })
- }
- }
- })
-
- this.on('connectionError', (origin, targets, error) => {
- // If a connection error occurs, we remove the client from the pool,
- // and emit a connectionError event. They will not be re-used.
- // Fixes https://github.com/nodejs/undici/issues/3895
- for (const target of targets) {
- // Do not use kRemoveClient here, as it will close the client,
- // but the client cannot be closed in this state.
- const idx = this[kClients].indexOf(target)
- if (idx !== -1) {
- this[kClients].splice(idx, 1)
- }
- }
- })
- }
-
- [kGetDispatcher] () {
- const clientTtlOption = this[kOptions].clientTtl
- for (const client of this[kClients]) {
- // check ttl of client and if it's stale, remove it from the pool
- if (clientTtlOption != null && clientTtlOption > 0 && client.ttl && ((Date.now() - client.ttl) > clientTtlOption)) {
- this[kRemoveClient](client)
- } else if (!client[kNeedDrain]) {
- return client
- }
- }
-
- if (!this[kConnections] || this[kClients].length < this[kConnections]) {
- const dispatcher = this[kFactory](this[kUrl], this[kOptions])
- this[kAddClient](dispatcher)
- return dispatcher
- }
- }
-}
-
-module.exports = Pool
diff --git a/vanilla/node_modules/undici/lib/dispatcher/proxy-agent.js b/vanilla/node_modules/undici/lib/dispatcher/proxy-agent.js
deleted file mode 100644
index 4403b8d..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/proxy-agent.js
+++ /dev/null
@@ -1,287 +0,0 @@
-'use strict'
-
-const { kProxy, kClose, kDestroy, kDispatch } = require('../core/symbols')
-const Agent = require('./agent')
-const Pool = require('./pool')
-const DispatcherBase = require('./dispatcher-base')
-const { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = require('../core/errors')
-const buildConnector = require('../core/connect')
-const Client = require('./client')
-const { channels } = require('../core/diagnostics')
-
-const kAgent = Symbol('proxy agent')
-const kClient = Symbol('proxy client')
-const kProxyHeaders = Symbol('proxy headers')
-const kRequestTls = Symbol('request tls settings')
-const kProxyTls = Symbol('proxy tls settings')
-const kConnectEndpoint = Symbol('connect endpoint function')
-const kTunnelProxy = Symbol('tunnel proxy')
-
-function defaultProtocolPort (protocol) {
- return protocol === 'https:' ? 443 : 80
-}
-
-function defaultFactory (origin, opts) {
- return new Pool(origin, opts)
-}
-
-const noop = () => {}
-
-function defaultAgentFactory (origin, opts) {
- if (opts.connections === 1) {
- return new Client(origin, opts)
- }
- return new Pool(origin, opts)
-}
-
-class Http1ProxyWrapper extends DispatcherBase {
- #client
-
- constructor (proxyUrl, { headers = {}, connect, factory }) {
- if (!proxyUrl) {
- throw new InvalidArgumentError('Proxy URL is mandatory')
- }
-
- super()
-
- this[kProxyHeaders] = headers
- if (factory) {
- this.#client = factory(proxyUrl, { connect })
- } else {
- this.#client = new Client(proxyUrl, { connect })
- }
- }
-
- [kDispatch] (opts, handler) {
- const onHeaders = handler.onHeaders
- handler.onHeaders = function (statusCode, data, resume) {
- if (statusCode === 407) {
- if (typeof handler.onError === 'function') {
- handler.onError(new InvalidArgumentError('Proxy Authentication Required (407)'))
- }
- return
- }
- if (onHeaders) onHeaders.call(this, statusCode, data, resume)
- }
-
- // Rewrite request as an HTTP1 Proxy request, without tunneling.
- const {
- origin,
- path = '/',
- headers = {}
- } = opts
-
- opts.path = origin + path
-
- if (!('host' in headers) && !('Host' in headers)) {
- const { host } = new URL(origin)
- headers.host = host
- }
- opts.headers = { ...this[kProxyHeaders], ...headers }
-
- return this.#client[kDispatch](opts, handler)
- }
-
- [kClose] () {
- return this.#client.close()
- }
-
- [kDestroy] (err) {
- return this.#client.destroy(err)
- }
-}
-
-class ProxyAgent extends DispatcherBase {
- constructor (opts) {
- if (!opts || (typeof opts === 'object' && !(opts instanceof URL) && !opts.uri)) {
- throw new InvalidArgumentError('Proxy uri is mandatory')
- }
-
- const { clientFactory = defaultFactory } = opts
- if (typeof clientFactory !== 'function') {
- throw new InvalidArgumentError('Proxy opts.clientFactory must be a function.')
- }
-
- const { proxyTunnel = true } = opts
-
- super()
-
- const url = this.#getUrl(opts)
- const { href, origin, port, protocol, username, password, hostname: proxyHostname } = url
-
- this[kProxy] = { uri: href, protocol }
- this[kRequestTls] = opts.requestTls
- this[kProxyTls] = opts.proxyTls
- this[kProxyHeaders] = opts.headers || {}
- this[kTunnelProxy] = proxyTunnel
-
- if (opts.auth && opts.token) {
- throw new InvalidArgumentError('opts.auth cannot be used in combination with opts.token')
- } else if (opts.auth) {
- /* @deprecated in favour of opts.token */
- this[kProxyHeaders]['proxy-authorization'] = `Basic ${opts.auth}`
- } else if (opts.token) {
- this[kProxyHeaders]['proxy-authorization'] = opts.token
- } else if (username && password) {
- this[kProxyHeaders]['proxy-authorization'] = `Basic ${Buffer.from(`${decodeURIComponent(username)}:${decodeURIComponent(password)}`).toString('base64')}`
- }
-
- const connect = buildConnector({ ...opts.proxyTls })
- this[kConnectEndpoint] = buildConnector({ ...opts.requestTls })
-
- const agentFactory = opts.factory || defaultAgentFactory
- const factory = (origin, options) => {
- const { protocol } = new URL(origin)
- if (!this[kTunnelProxy] && protocol === 'http:' && this[kProxy].protocol === 'http:') {
- return new Http1ProxyWrapper(this[kProxy].uri, {
- headers: this[kProxyHeaders],
- connect,
- factory: agentFactory
- })
- }
- return agentFactory(origin, options)
- }
- this[kClient] = clientFactory(url, { connect })
- this[kAgent] = new Agent({
- ...opts,
- factory,
- connect: async (opts, callback) => {
- let requestedPath = opts.host
- if (!opts.port) {
- requestedPath += `:${defaultProtocolPort(opts.protocol)}`
- }
- try {
- const connectParams = {
- origin,
- port,
- path: requestedPath,
- signal: opts.signal,
- headers: {
- ...this[kProxyHeaders],
- host: opts.host,
- ...(opts.connections == null || opts.connections > 0 ? { 'proxy-connection': 'keep-alive' } : {})
- },
- servername: this[kProxyTls]?.servername || proxyHostname
- }
- const { socket, statusCode } = await this[kClient].connect(connectParams)
- if (statusCode !== 200) {
- socket.on('error', noop).destroy()
- callback(new RequestAbortedError(`Proxy response (${statusCode}) !== 200 when HTTP Tunneling`))
- return
- }
-
- if (channels.proxyConnected.hasSubscribers) {
- channels.proxyConnected.publish({
- socket,
- connectParams
- })
- }
-
- if (opts.protocol !== 'https:') {
- callback(null, socket)
- return
- }
- let servername
- if (this[kRequestTls]) {
- servername = this[kRequestTls].servername
- } else {
- servername = opts.servername
- }
- this[kConnectEndpoint]({ ...opts, servername, httpSocket: socket }, callback)
- } catch (err) {
- if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {
- // Throw a custom error to avoid loop in client.js#connect
- callback(new SecureProxyConnectionError(err))
- } else {
- callback(err)
- }
- }
- }
- })
- }
-
- dispatch (opts, handler) {
- const headers = buildHeaders(opts.headers)
- throwIfProxyAuthIsSent(headers)
-
- if (headers && !('host' in headers) && !('Host' in headers)) {
- const { host } = new URL(opts.origin)
- headers.host = host
- }
-
- return this[kAgent].dispatch(
- {
- ...opts,
- headers
- },
- handler
- )
- }
-
- /**
- * @param {import('../../types/proxy-agent').ProxyAgent.Options | string | URL} opts
- * @returns {URL}
- */
- #getUrl (opts) {
- if (typeof opts === 'string') {
- return new URL(opts)
- } else if (opts instanceof URL) {
- return opts
- } else {
- return new URL(opts.uri)
- }
- }
-
- [kClose] () {
- return Promise.all([
- this[kAgent].close(),
- this[kClient].close()
- ])
- }
-
- [kDestroy] () {
- return Promise.all([
- this[kAgent].destroy(),
- this[kClient].destroy()
- ])
- }
-}
-
-/**
- * @param {string[] | Record<string, string>} headers
- * @returns {Record<string, string>}
- */
-function buildHeaders (headers) {
- // When using undici.fetch, the headers list is stored
- // as an array.
- if (Array.isArray(headers)) {
- /** @type {Record<string, string>} */
- const headersPair = {}
-
- for (let i = 0; i < headers.length; i += 2) {
- headersPair[headers[i]] = headers[i + 1]
- }
-
- return headersPair
- }
-
- return headers
-}
-
-/**
- * @param {Record<string, string>} headers
- *
- * Previous versions of ProxyAgent suggests the Proxy-Authorization in request headers
- * Nevertheless, it was changed and to avoid a security vulnerability by end users
- * this check was created.
- * It should be removed in the next major version for performance reasons
- */
-function throwIfProxyAuthIsSent (headers) {
- const existProxyAuth = headers && Object.keys(headers)
- .find((key) => key.toLowerCase() === 'proxy-authorization')
- if (existProxyAuth) {
- throw new InvalidArgumentError('Proxy-Authorization should be sent in ProxyAgent constructor')
- }
-}
-
-module.exports = ProxyAgent
diff --git a/vanilla/node_modules/undici/lib/dispatcher/retry-agent.js b/vanilla/node_modules/undici/lib/dispatcher/retry-agent.js
deleted file mode 100644
index 0c2120d..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/retry-agent.js
+++ /dev/null
@@ -1,35 +0,0 @@
-'use strict'
-
-const Dispatcher = require('./dispatcher')
-const RetryHandler = require('../handler/retry-handler')
-
-class RetryAgent extends Dispatcher {
- #agent = null
- #options = null
- constructor (agent, options = {}) {
- super(options)
- this.#agent = agent
- this.#options = options
- }
-
- dispatch (opts, handler) {
- const retry = new RetryHandler({
- ...opts,
- retryOptions: this.#options
- }, {
- dispatch: this.#agent.dispatch.bind(this.#agent),
- handler
- })
- return this.#agent.dispatch(opts, retry)
- }
-
- close () {
- return this.#agent.close()
- }
-
- destroy () {
- return this.#agent.destroy()
- }
-}
-
-module.exports = RetryAgent
diff --git a/vanilla/node_modules/undici/lib/dispatcher/round-robin-pool.js b/vanilla/node_modules/undici/lib/dispatcher/round-robin-pool.js
deleted file mode 100644
index b113aa9..0000000
--- a/vanilla/node_modules/undici/lib/dispatcher/round-robin-pool.js
+++ /dev/null
@@ -1,137 +0,0 @@
-'use strict'
-
-const {
- PoolBase,
- kClients,
- kNeedDrain,
- kAddClient,
- kGetDispatcher,
- kRemoveClient
-} = require('./pool-base')
-const Client = require('./client')
-const {
- InvalidArgumentError
-} = require('../core/errors')
-const util = require('../core/util')
-const { kUrl } = require('../core/symbols')
-const buildConnector = require('../core/connect')
-
-const kOptions = Symbol('options')
-const kConnections = Symbol('connections')
-const kFactory = Symbol('factory')
-const kIndex = Symbol('index')
-
-function defaultFactory (origin, opts) {
- return new Client(origin, opts)
-}
-
-class RoundRobinPool extends PoolBase {
- constructor (origin, {
- connections,
- factory = defaultFactory,
- connect,
- connectTimeout,
- tls,
- maxCachedSessions,
- socketPath,
- autoSelectFamily,
- autoSelectFamilyAttemptTimeout,
- allowH2,
- clientTtl,
- ...options
- } = {}) {
- if (connections != null && (!Number.isFinite(connections) || connections < 0)) {
- throw new InvalidArgumentError('invalid connections')
- }
-
- if (typeof factory !== 'function') {
- throw new InvalidArgumentError('factory must be a function.')
- }
-
- if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {
- throw new InvalidArgumentError('connect must be a function or an object')
- }
-
- if (typeof connect !== 'function') {
- connect = buildConnector({
- ...tls,
- maxCachedSessions,
- allowH2,
- socketPath,
- timeout: connectTimeout,
- ...(typeof autoSelectFamily === 'boolean' ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : undefined),
- ...connect
- })
- }
-
- super()
-
- this[kConnections] = connections || null
- this[kUrl] = util.parseOrigin(origin)
- this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl }
- this[kOptions].interceptors = options.interceptors
- ? { ...options.interceptors }
- : undefined
- this[kFactory] = factory
- this[kIndex] = -1
-
- this.on('connect', (origin, targets) => {
- if (clientTtl != null && clientTtl > 0) {
- for (const target of targets) {
- Object.assign(target, { ttl: Date.now() })
- }
- }
- })
-
- this.on('connectionError', (origin, targets, error) => {
- for (const target of targets) {
- const idx = this[kClients].indexOf(target)
- if (idx !== -1) {
- this[kClients].splice(idx, 1)
- }
- }
- })
- }
-
- [kGetDispatcher] () {
- const clientTtlOption = this[kOptions].clientTtl
- const clientsLength = this[kClients].length
-
- // If we have no clients yet, create one
- if (clientsLength === 0) {
- const dispatcher = this[kFactory](this[kUrl], this[kOptions])
- this[kAddClient](dispatcher)
- return dispatcher
- }
-
- // Round-robin through existing clients
- let checked = 0
- while (checked < clientsLength) {
- this[kIndex] = (this[kIndex] + 1) % clientsLength
- const client = this[kClients][this[kIndex]]
-
- // Check if client is stale (TTL expired)
- if (clientTtlOption != null && clientTtlOption > 0 && client.ttl && ((Date.now() - client.ttl) > clientTtlOption)) {
- this[kRemoveClient](client)
- checked++
- continue
- }
-
- // Return client if it's not draining
- if (!client[kNeedDrain]) {
- return client
- }
-
- checked++
- }
-
- // All clients are busy, create a new one if we haven't reached the limit
- if (!this[kConnections] || clientsLength < this[kConnections]) {
- const dispatcher = this[kFactory](this[kUrl], this[kOptions])
- this[kAddClient](dispatcher)
- return dispatcher
- }
- }
-}
-
-module.exports = RoundRobinPool
diff --git a/vanilla/node_modules/undici/lib/encoding/index.js b/vanilla/node_modules/undici/lib/encoding/index.js
deleted file mode 100644
index 41f7794..0000000
--- a/vanilla/node_modules/undici/lib/encoding/index.js
+++ /dev/null
@@ -1,33 +0,0 @@
-'use strict'
-
-const textDecoder = new TextDecoder()
-
-/**
- * @see https://encoding.spec.whatwg.org/#utf-8-decode
- * @param {Uint8Array} 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-8’s
- // decoder, ioQueue, output, and "replacement".
- const output = textDecoder.decode(buffer)
-
- // 4. Return output.
- return output
-}
-
-module.exports = {
- utf8DecodeBytes
-}
diff --git a/vanilla/node_modules/undici/lib/global.js b/vanilla/node_modules/undici/lib/global.js
deleted file mode 100644
index b61d779..0000000
--- a/vanilla/node_modules/undici/lib/global.js
+++ /dev/null
@@ -1,50 +0,0 @@
-'use strict'
-
-// We include a version number for the Dispatcher API. In case of breaking changes,
-// this version number must be increased to avoid conflicts.
-const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
-const { InvalidArgumentError } = require('./core/errors')
-const Agent = require('./dispatcher/agent')
-
-if (getGlobalDispatcher() === undefined) {
- setGlobalDispatcher(new Agent())
-}
-
-function setGlobalDispatcher (agent) {
- if (!agent || typeof agent.dispatch !== 'function') {
- throw new InvalidArgumentError('Argument agent must implement Agent')
- }
- Object.defineProperty(globalThis, globalDispatcher, {
- value: agent,
- writable: true,
- enumerable: false,
- configurable: false
- })
-}
-
-function getGlobalDispatcher () {
- return globalThis[globalDispatcher]
-}
-
-// These are the globals that can be installed by undici.install().
-// Not exported by index.js to avoid use outside of this module.
-const installedExports = /** @type {const} */ (
- [
- 'fetch',
- 'Headers',
- 'Response',
- 'Request',
- 'FormData',
- 'WebSocket',
- 'CloseEvent',
- 'ErrorEvent',
- 'MessageEvent',
- 'EventSource'
- ]
-)
-
-module.exports = {
- setGlobalDispatcher,
- getGlobalDispatcher,
- installedExports
-}
diff --git a/vanilla/node_modules/undici/lib/handler/cache-handler.js b/vanilla/node_modules/undici/lib/handler/cache-handler.js
deleted file mode 100644
index 93a70e8..0000000
--- a/vanilla/node_modules/undici/lib/handler/cache-handler.js
+++ /dev/null
@@ -1,561 +0,0 @@
-'use strict'
-
-const util = require('../core/util')
-const {
- parseCacheControlHeader,
- parseVaryHeader,
- isEtagUsable
-} = require('../util/cache')
-const { parseHttpDate } = require('../util/date.js')
-
-function noop () {}
-
-// Status codes that we can use some heuristics on to cache
-const HEURISTICALLY_CACHEABLE_STATUS_CODES = [
- 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, 501
-]
-
-// Status codes which semantic is not handled by the cache
-// https://datatracker.ietf.org/doc/html/rfc9111#section-3
-// This list should not grow beyond 206 unless the RFC is updated
-// by a newer one including more. Please introduce another list if
-// implementing caching of responses with the 'must-understand' directive.
-const NOT_UNDERSTOOD_STATUS_CODES = [
- 206
-]
-
-const MAX_RESPONSE_AGE = 2147483647000
-
-/**
- * @typedef {import('../../types/dispatcher.d.ts').default.DispatchHandler} DispatchHandler
- *
- * @implements {DispatchHandler}
- */
-class CacheHandler {
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}
- */
- #cacheKey
-
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions['type']}
- */
- #cacheType
-
- /**
- * @type {number | undefined}
- */
- #cacheByDefault
-
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheStore}
- */
- #store
-
- /**
- * @type {import('../../types/dispatcher.d.ts').default.DispatchHandler}
- */
- #handler
-
- /**
- * @type {import('node:stream').Writable | undefined}
- */
- #writeStream
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions} opts
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey
- * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler
- */
- constructor ({ store, type, cacheByDefault }, cacheKey, handler) {
- this.#store = store
- this.#cacheType = type
- this.#cacheByDefault = cacheByDefault
- this.#cacheKey = cacheKey
- this.#handler = handler
- }
-
- onRequestStart (controller, context) {
- this.#writeStream?.destroy()
- this.#writeStream = undefined
- this.#handler.onRequestStart?.(controller, context)
- }
-
- onRequestUpgrade (controller, statusCode, headers, socket) {
- this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
- }
-
- /**
- * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller
- * @param {number} statusCode
- * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
- * @param {string} statusMessage
- */
- onResponseStart (
- controller,
- statusCode,
- resHeaders,
- statusMessage
- ) {
- const downstreamOnHeaders = () =>
- this.#handler.onResponseStart?.(
- controller,
- statusCode,
- resHeaders,
- statusMessage
- )
- const handler = this
-
- if (
- !util.safeHTTPMethods.includes(this.#cacheKey.method) &&
- statusCode >= 200 &&
- statusCode <= 399
- ) {
- // Successful response to an unsafe method, delete it from cache
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-invalidating-stored-response
- try {
- this.#store.delete(this.#cacheKey)?.catch?.(noop)
- } catch {
- // Fail silently
- }
- return downstreamOnHeaders()
- }
-
- const cacheControlHeader = resHeaders['cache-control']
- const heuristicallyCacheable = resHeaders['last-modified'] && HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode)
- if (
- !cacheControlHeader &&
- !resHeaders['expires'] &&
- !heuristicallyCacheable &&
- !this.#cacheByDefault
- ) {
- // Don't have anything to tell us this response is cachable and we're not
- // caching by default
- return downstreamOnHeaders()
- }
-
- const cacheControlDirectives = cacheControlHeader ? parseCacheControlHeader(cacheControlHeader) : {}
- if (!canCacheResponse(this.#cacheType, statusCode, resHeaders, cacheControlDirectives)) {
- return downstreamOnHeaders()
- }
-
- const now = Date.now()
- const resAge = resHeaders.age ? getAge(resHeaders.age) : undefined
- if (resAge && resAge >= MAX_RESPONSE_AGE) {
- // Response considered stale
- return downstreamOnHeaders()
- }
-
- const resDate = typeof resHeaders.date === 'string'
- ? parseHttpDate(resHeaders.date)
- : undefined
-
- const staleAt =
- determineStaleAt(this.#cacheType, now, resAge, resHeaders, resDate, cacheControlDirectives) ??
- this.#cacheByDefault
- if (staleAt === undefined || (resAge && resAge > staleAt)) {
- return downstreamOnHeaders()
- }
-
- const baseTime = resDate ? resDate.getTime() : now
- const absoluteStaleAt = staleAt + baseTime
- if (now >= absoluteStaleAt) {
- // Response is already stale
- return downstreamOnHeaders()
- }
-
- let varyDirectives
- if (this.#cacheKey.headers && resHeaders.vary) {
- varyDirectives = parseVaryHeader(resHeaders.vary, this.#cacheKey.headers)
- if (!varyDirectives) {
- // Parse error
- return downstreamOnHeaders()
- }
- }
-
- const deleteAt = determineDeleteAt(baseTime, cacheControlDirectives, absoluteStaleAt)
- const strippedHeaders = stripNecessaryHeaders(resHeaders, cacheControlDirectives)
-
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}
- */
- const value = {
- statusCode,
- statusMessage,
- headers: strippedHeaders,
- vary: varyDirectives,
- cacheControlDirectives,
- cachedAt: resAge ? now - resAge : now,
- staleAt: absoluteStaleAt,
- deleteAt
- }
-
- // Not modified, re-use the cached value
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-handling-304-not-modified
- if (statusCode === 304) {
- const handle304 = (cachedValue) => {
- if (!cachedValue) {
- // Do not create a new cache entry, as a 304 won't have a body - so cannot be cached.
- return downstreamOnHeaders()
- }
-
- // Re-use the cached value: statuscode, statusmessage, headers and body
- value.statusCode = cachedValue.statusCode
- value.statusMessage = cachedValue.statusMessage
- value.etag = cachedValue.etag
- value.headers = { ...cachedValue.headers, ...strippedHeaders }
-
- downstreamOnHeaders()
-
- this.#writeStream = this.#store.createWriteStream(this.#cacheKey, value)
-
- if (!this.#writeStream || !cachedValue?.body) {
- return
- }
-
- if (typeof cachedValue.body.values === 'function') {
- const bodyIterator = cachedValue.body.values()
-
- const streamCachedBody = () => {
- for (const chunk of bodyIterator) {
- const full = this.#writeStream.write(chunk) === false
- this.#handler.onResponseData?.(controller, chunk)
- // when stream is full stop writing until we get a 'drain' event
- if (full) {
- break
- }
- }
- }
-
- this.#writeStream
- .on('error', function () {
- handler.#writeStream = undefined
- handler.#store.delete(handler.#cacheKey)
- })
- .on('drain', () => {
- streamCachedBody()
- })
- .on('close', function () {
- if (handler.#writeStream === this) {
- handler.#writeStream = undefined
- }
- })
-
- streamCachedBody()
- } else if (typeof cachedValue.body.on === 'function') {
- // Readable stream body (e.g. from async/remote cache stores)
- cachedValue.body
- .on('data', (chunk) => {
- this.#writeStream.write(chunk)
- this.#handler.onResponseData?.(controller, chunk)
- })
- .on('end', () => {
- this.#writeStream.end()
- })
- .on('error', () => {
- this.#writeStream = undefined
- this.#store.delete(this.#cacheKey)
- })
-
- this.#writeStream
- .on('error', function () {
- handler.#writeStream = undefined
- handler.#store.delete(handler.#cacheKey)
- })
- .on('close', function () {
- if (handler.#writeStream === this) {
- handler.#writeStream = undefined
- }
- })
- }
- }
-
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}
- */
- const result = this.#store.get(this.#cacheKey)
- if (result && typeof result.then === 'function') {
- result.then(handle304)
- } else {
- handle304(result)
- }
- } else {
- if (typeof resHeaders.etag === 'string' && isEtagUsable(resHeaders.etag)) {
- value.etag = resHeaders.etag
- }
-
- this.#writeStream = this.#store.createWriteStream(this.#cacheKey, value)
-
- if (!this.#writeStream) {
- return downstreamOnHeaders()
- }
-
- this.#writeStream
- .on('drain', () => controller.resume())
- .on('error', function () {
- // TODO (fix): Make error somehow observable?
- handler.#writeStream = undefined
-
- // Delete the value in case the cache store is holding onto state from
- // the call to createWriteStream
- handler.#store.delete(handler.#cacheKey)
- })
- .on('close', function () {
- if (handler.#writeStream === this) {
- handler.#writeStream = undefined
- }
-
- // TODO (fix): Should we resume even if was paused downstream?
- controller.resume()
- })
-
- downstreamOnHeaders()
- }
- }
-
- onResponseData (controller, chunk) {
- if (this.#writeStream?.write(chunk) === false) {
- controller.pause()
- }
-
- this.#handler.onResponseData?.(controller, chunk)
- }
-
- onResponseEnd (controller, trailers) {
- this.#writeStream?.end()
- this.#handler.onResponseEnd?.(controller, trailers)
- }
-
- onResponseError (controller, err) {
- this.#writeStream?.destroy(err)
- this.#writeStream = undefined
- this.#handler.onResponseError?.(controller, err)
- }
-}
-
-/**
- * @see https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen
- *
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType
- * @param {number} statusCode
- * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
- */
-function canCacheResponse (cacheType, statusCode, resHeaders, cacheControlDirectives) {
- // Status code must be final and understood.
- if (statusCode < 200 || NOT_UNDERSTOOD_STATUS_CODES.includes(statusCode)) {
- return false
- }
- // Responses with neither status codes that are heuristically cacheable, nor "explicit enough" caching
- // directives, are not cacheable. "Explicit enough": see https://www.rfc-editor.org/rfc/rfc9111.html#section-3
- if (!HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode) && !resHeaders['expires'] &&
- !cacheControlDirectives.public &&
- cacheControlDirectives['max-age'] === undefined &&
- // RFC 9111: a private response directive, if the cache is not shared
- !(cacheControlDirectives.private && cacheType === 'private') &&
- !(cacheControlDirectives['s-maxage'] !== undefined && cacheType === 'shared')
- ) {
- return false
- }
-
- if (cacheControlDirectives['no-store']) {
- return false
- }
-
- if (cacheType === 'shared' && cacheControlDirectives.private === true) {
- return false
- }
-
- // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.1-5
- if (resHeaders.vary?.includes('*')) {
- return false
- }
-
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen
- if (resHeaders.authorization) {
- if (!cacheControlDirectives.public || typeof resHeaders.authorization !== 'string') {
- return false
- }
-
- if (
- Array.isArray(cacheControlDirectives['no-cache']) &&
- cacheControlDirectives['no-cache'].includes('authorization')
- ) {
- return false
- }
-
- if (
- Array.isArray(cacheControlDirectives['private']) &&
- cacheControlDirectives['private'].includes('authorization')
- ) {
- return false
- }
- }
-
- return true
-}
-
-/**
- * @param {string | string[]} ageHeader
- * @returns {number | undefined}
- */
-function getAge (ageHeader) {
- const age = parseInt(Array.isArray(ageHeader) ? ageHeader[0] : ageHeader)
-
- return isNaN(age) ? undefined : age * 1000
-}
-
-/**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType
- * @param {number} now
- * @param {number | undefined} age
- * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
- * @param {Date | undefined} responseDate
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
- *
- * @returns {number | undefined} time that the value is stale at in seconds or undefined if it shouldn't be cached
- */
-function determineStaleAt (cacheType, now, age, resHeaders, responseDate, cacheControlDirectives) {
- if (cacheType === 'shared') {
- // Prioritize s-maxage since we're a shared cache
- // s-maxage > max-age > Expire
- // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10-3
- const sMaxAge = cacheControlDirectives['s-maxage']
- if (sMaxAge !== undefined) {
- return sMaxAge > 0 ? sMaxAge * 1000 : undefined
- }
- }
-
- const maxAge = cacheControlDirectives['max-age']
- if (maxAge !== undefined) {
- return maxAge > 0 ? maxAge * 1000 : undefined
- }
-
- if (typeof resHeaders.expires === 'string') {
- // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3
- const expiresDate = parseHttpDate(resHeaders.expires)
- if (expiresDate) {
- if (now >= expiresDate.getTime()) {
- return undefined
- }
-
- if (responseDate) {
- if (responseDate >= expiresDate) {
- return undefined
- }
-
- if (age !== undefined && age > (expiresDate - responseDate)) {
- return undefined
- }
- }
-
- return expiresDate.getTime() - now
- }
- }
-
- if (typeof resHeaders['last-modified'] === 'string') {
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-calculating-heuristic-fresh
- const lastModified = new Date(resHeaders['last-modified'])
- if (isValidDate(lastModified)) {
- if (lastModified.getTime() >= now) {
- return undefined
- }
-
- const responseAge = now - lastModified.getTime()
-
- return responseAge * 0.1
- }
- }
-
- if (cacheControlDirectives.immutable) {
- // https://www.rfc-editor.org/rfc/rfc8246.html#section-2.2
- return 31536000
- }
-
- return undefined
-}
-
-/**
- * @param {number} now
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
- * @param {number} staleAt
- */
-function determineDeleteAt (now, cacheControlDirectives, staleAt) {
- let staleWhileRevalidate = -Infinity
- let staleIfError = -Infinity
- let immutable = -Infinity
-
- if (cacheControlDirectives['stale-while-revalidate']) {
- staleWhileRevalidate = staleAt + (cacheControlDirectives['stale-while-revalidate'] * 1000)
- }
-
- if (cacheControlDirectives['stale-if-error']) {
- staleIfError = staleAt + (cacheControlDirectives['stale-if-error'] * 1000)
- }
-
- if (staleWhileRevalidate === -Infinity && staleIfError === -Infinity) {
- immutable = now + 31536000000
- }
-
- return Math.max(staleAt, staleWhileRevalidate, staleIfError, immutable)
-}
-
-/**
- * Strips headers required to be removed in cached responses
- * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
- * @returns {Record<string, string | string []>}
- */
-function stripNecessaryHeaders (resHeaders, cacheControlDirectives) {
- const headersToRemove = [
- 'connection',
- 'proxy-authenticate',
- 'proxy-authentication-info',
- 'proxy-authorization',
- 'proxy-connection',
- 'te',
- 'transfer-encoding',
- 'upgrade',
- // We'll add age back when serving it
- 'age'
- ]
-
- if (resHeaders['connection']) {
- if (Array.isArray(resHeaders['connection'])) {
- // connection: a
- // connection: b
- headersToRemove.push(...resHeaders['connection'].map(header => header.trim()))
- } else {
- // connection: a, b
- headersToRemove.push(...resHeaders['connection'].split(',').map(header => header.trim()))
- }
- }
-
- if (Array.isArray(cacheControlDirectives['no-cache'])) {
- headersToRemove.push(...cacheControlDirectives['no-cache'])
- }
-
- if (Array.isArray(cacheControlDirectives['private'])) {
- headersToRemove.push(...cacheControlDirectives['private'])
- }
-
- let strippedHeaders
- for (const headerName of headersToRemove) {
- if (resHeaders[headerName]) {
- strippedHeaders ??= { ...resHeaders }
- delete strippedHeaders[headerName]
- }
- }
-
- return strippedHeaders ?? resHeaders
-}
-
-/**
- * @param {Date} date
- * @returns {boolean}
- */
-function isValidDate (date) {
- return date instanceof Date && Number.isFinite(date.valueOf())
-}
-
-module.exports = CacheHandler
diff --git a/vanilla/node_modules/undici/lib/handler/cache-revalidation-handler.js b/vanilla/node_modules/undici/lib/handler/cache-revalidation-handler.js
deleted file mode 100644
index 393d16d..0000000
--- a/vanilla/node_modules/undici/lib/handler/cache-revalidation-handler.js
+++ /dev/null
@@ -1,124 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-
-/**
- * This takes care of revalidation requests we send to the origin. If we get
- * a response indicating that what we have is cached (via a HTTP 304), we can
- * continue using the cached value. Otherwise, we'll receive the new response
- * here, which we then just pass on to the next handler (most likely a
- * CacheHandler). Note that this assumes the proper headers were already
- * included in the request to tell the origin that we want to revalidate the
- * response (i.e. if-modified-since or if-none-match).
- *
- * @see https://www.rfc-editor.org/rfc/rfc9111.html#name-validation
- *
- * @implements {import('../../types/dispatcher.d.ts').default.DispatchHandler}
- */
-class CacheRevalidationHandler {
- #successful = false
-
- /**
- * @type {((boolean, any) => void) | null}
- */
- #callback
-
- /**
- * @type {(import('../../types/dispatcher.d.ts').default.DispatchHandler)}
- */
- #handler
-
- #context
-
- /**
- * @type {boolean}
- */
- #allowErrorStatusCodes
-
- /**
- * @param {(boolean) => void} callback Function to call if the cached value is valid
- * @param {import('../../types/dispatcher.d.ts').default.DispatchHandlers} handler
- * @param {boolean} allowErrorStatusCodes
- */
- constructor (callback, handler, allowErrorStatusCodes) {
- if (typeof callback !== 'function') {
- throw new TypeError('callback must be a function')
- }
-
- this.#callback = callback
- this.#handler = handler
- this.#allowErrorStatusCodes = allowErrorStatusCodes
- }
-
- onRequestStart (_, context) {
- this.#successful = false
- this.#context = context
- }
-
- onRequestUpgrade (controller, statusCode, headers, socket) {
- this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
- }
-
- onResponseStart (
- controller,
- statusCode,
- headers,
- statusMessage
- ) {
- assert(this.#callback != null)
-
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-handling-a-validation-respo
- // https://datatracker.ietf.org/doc/html/rfc5861#section-4
- this.#successful = statusCode === 304 ||
- (this.#allowErrorStatusCodes && statusCode >= 500 && statusCode <= 504)
- this.#callback(this.#successful, this.#context)
- this.#callback = null
-
- if (this.#successful) {
- return true
- }
-
- this.#handler.onRequestStart?.(controller, this.#context)
- this.#handler.onResponseStart?.(
- controller,
- statusCode,
- headers,
- statusMessage
- )
- }
-
- onResponseData (controller, chunk) {
- if (this.#successful) {
- return
- }
-
- return this.#handler.onResponseData?.(controller, chunk)
- }
-
- onResponseEnd (controller, trailers) {
- if (this.#successful) {
- return
- }
-
- this.#handler.onResponseEnd?.(controller, trailers)
- }
-
- onResponseError (controller, err) {
- if (this.#successful) {
- return
- }
-
- if (this.#callback) {
- this.#callback(false)
- this.#callback = null
- }
-
- if (typeof this.#handler.onResponseError === 'function') {
- this.#handler.onResponseError(controller, err)
- } else {
- throw err
- }
- }
-}
-
-module.exports = CacheRevalidationHandler
diff --git a/vanilla/node_modules/undici/lib/handler/decorator-handler.js b/vanilla/node_modules/undici/lib/handler/decorator-handler.js
deleted file mode 100644
index 50fbb0c..0000000
--- a/vanilla/node_modules/undici/lib/handler/decorator-handler.js
+++ /dev/null
@@ -1,67 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const WrapHandler = require('./wrap-handler')
-
-/**
- * @deprecated
- */
-module.exports = class DecoratorHandler {
- #handler
- #onCompleteCalled = false
- #onErrorCalled = false
- #onResponseStartCalled = false
-
- constructor (handler) {
- if (typeof handler !== 'object' || handler === null) {
- throw new TypeError('handler must be an object')
- }
- this.#handler = WrapHandler.wrap(handler)
- }
-
- onRequestStart (...args) {
- this.#handler.onRequestStart?.(...args)
- }
-
- onRequestUpgrade (...args) {
- assert(!this.#onCompleteCalled)
- assert(!this.#onErrorCalled)
-
- return this.#handler.onRequestUpgrade?.(...args)
- }
-
- onResponseStart (...args) {
- assert(!this.#onCompleteCalled)
- assert(!this.#onErrorCalled)
- assert(!this.#onResponseStartCalled)
-
- this.#onResponseStartCalled = true
-
- return this.#handler.onResponseStart?.(...args)
- }
-
- onResponseData (...args) {
- assert(!this.#onCompleteCalled)
- assert(!this.#onErrorCalled)
-
- return this.#handler.onResponseData?.(...args)
- }
-
- onResponseEnd (...args) {
- assert(!this.#onCompleteCalled)
- assert(!this.#onErrorCalled)
-
- this.#onCompleteCalled = true
- return this.#handler.onResponseEnd?.(...args)
- }
-
- onResponseError (...args) {
- this.#onErrorCalled = true
- return this.#handler.onResponseError?.(...args)
- }
-
- /**
- * @deprecated
- */
- onBodySent () {}
-}
diff --git a/vanilla/node_modules/undici/lib/handler/deduplication-handler.js b/vanilla/node_modules/undici/lib/handler/deduplication-handler.js
deleted file mode 100644
index 33a2c4f..0000000
--- a/vanilla/node_modules/undici/lib/handler/deduplication-handler.js
+++ /dev/null
@@ -1,216 +0,0 @@
-'use strict'
-
-/**
- * @typedef {import('../../types/dispatcher.d.ts').default.DispatchHandler} DispatchHandler
- */
-
-/**
- * Handler that buffers response data and notifies multiple waiting handlers.
- * Used for request deduplication.
- *
- * @implements {DispatchHandler}
- */
-class DeduplicationHandler {
- /**
- * @type {DispatchHandler}
- */
- #primaryHandler
-
- /**
- * @type {DispatchHandler[]}
- */
- #waitingHandlers = []
-
- /**
- * @type {Buffer[]}
- */
- #chunks = []
-
- /**
- * @type {number}
- */
- #statusCode = 0
-
- /**
- * @type {Record<string, string | string[]>}
- */
- #headers = {}
-
- /**
- * @type {string}
- */
- #statusMessage = ''
-
- /**
- * @type {boolean}
- */
- #aborted = false
-
- /**
- * @type {import('../../types/dispatcher.d.ts').default.DispatchController | null}
- */
- #controller = null
-
- /**
- * @type {(() => void) | null}
- */
- #onComplete = null
-
- /**
- * @param {DispatchHandler} primaryHandler The primary handler
- * @param {() => void} onComplete Callback when request completes
- */
- constructor (primaryHandler, onComplete) {
- this.#primaryHandler = primaryHandler
- this.#onComplete = onComplete
- }
-
- /**
- * Add a waiting handler that will receive the buffered response
- * @param {DispatchHandler} handler
- */
- addWaitingHandler (handler) {
- this.#waitingHandlers.push(handler)
- }
-
- /**
- * @param {() => void} abort
- * @param {any} context
- */
- onRequestStart (controller, context) {
- this.#controller = controller
- this.#primaryHandler.onRequestStart?.(controller, context)
- }
-
- /**
- * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller
- * @param {number} statusCode
- * @param {import('../../types/header.d.ts').IncomingHttpHeaders} headers
- * @param {Socket} socket
- */
- onRequestUpgrade (controller, statusCode, headers, socket) {
- this.#primaryHandler.onRequestUpgrade?.(controller, statusCode, headers, socket)
- }
-
- /**
- * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller
- * @param {number} statusCode
- * @param {Record<string, string | string[]>} headers
- * @param {string} statusMessage
- */
- onResponseStart (controller, statusCode, headers, statusMessage) {
- this.#statusCode = statusCode
- this.#headers = headers
- this.#statusMessage = statusMessage
- this.#primaryHandler.onResponseStart?.(controller, statusCode, headers, statusMessage)
- }
-
- /**
- * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller
- * @param {Buffer} chunk
- */
- onResponseData (controller, chunk) {
- // Buffer the chunk for waiting handlers
- this.#chunks.push(Buffer.from(chunk))
- this.#primaryHandler.onResponseData?.(controller, chunk)
- }
-
- /**
- * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller
- * @param {object} trailers
- */
- onResponseEnd (controller, trailers) {
- this.#primaryHandler.onResponseEnd?.(controller, trailers)
- this.#notifyWaitingHandlers()
- this.#onComplete?.()
- }
-
- /**
- * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller
- * @param {Error} err
- */
- onResponseError (controller, err) {
- this.#aborted = true
- this.#primaryHandler.onResponseError?.(controller, err)
- this.#notifyWaitingHandlersError(err)
- this.#onComplete?.()
- }
-
- /**
- * Notify all waiting handlers with the buffered response
- */
- #notifyWaitingHandlers () {
- const body = Buffer.concat(this.#chunks)
-
- for (const handler of this.#waitingHandlers) {
- // Create a simple controller for each waiting handler
- const waitingController = {
- resume () {},
- pause () {},
- get paused () { return false },
- get aborted () { return false },
- get reason () { return null },
- abort () {}
- }
-
- try {
- handler.onRequestStart?.(waitingController, null)
-
- if (waitingController.aborted) {
- continue
- }
-
- handler.onResponseStart?.(
- waitingController,
- this.#statusCode,
- this.#headers,
- this.#statusMessage
- )
-
- if (waitingController.aborted) {
- continue
- }
-
- if (body.length > 0) {
- handler.onResponseData?.(waitingController, body)
- }
-
- handler.onResponseEnd?.(waitingController, {})
- } catch {
- // Ignore errors from waiting handlers
- }
- }
-
- this.#waitingHandlers = []
- this.#chunks = []
- }
-
- /**
- * Notify all waiting handlers of an error
- * @param {Error} err
- */
- #notifyWaitingHandlersError (err) {
- for (const handler of this.#waitingHandlers) {
- const waitingController = {
- resume () {},
- pause () {},
- get paused () { return false },
- get aborted () { return true },
- get reason () { return err },
- abort () {}
- }
-
- try {
- handler.onRequestStart?.(waitingController, null)
- handler.onResponseError?.(waitingController, err)
- } catch {
- // Ignore errors from waiting handlers
- }
- }
-
- this.#waitingHandlers = []
- this.#chunks = []
- }
-}
-
-module.exports = DeduplicationHandler
diff --git a/vanilla/node_modules/undici/lib/handler/redirect-handler.js b/vanilla/node_modules/undici/lib/handler/redirect-handler.js
deleted file mode 100644
index dd0f471..0000000
--- a/vanilla/node_modules/undici/lib/handler/redirect-handler.js
+++ /dev/null
@@ -1,237 +0,0 @@
-'use strict'
-
-const util = require('../core/util')
-const { kBodyUsed } = require('../core/symbols')
-const assert = require('node:assert')
-const { InvalidArgumentError } = require('../core/errors')
-const EE = require('node:events')
-
-const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
-
-const kBody = Symbol('body')
-
-const noop = () => {}
-
-class BodyAsyncIterable {
- constructor (body) {
- this[kBody] = body
- this[kBodyUsed] = false
- }
-
- async * [Symbol.asyncIterator] () {
- assert(!this[kBodyUsed], 'disturbed')
- this[kBodyUsed] = true
- yield * this[kBody]
- }
-}
-
-class RedirectHandler {
- static buildDispatch (dispatcher, maxRedirections) {
- if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {
- throw new InvalidArgumentError('maxRedirections must be a positive number')
- }
-
- const dispatch = dispatcher.dispatch.bind(dispatcher)
- return (opts, originalHandler) => dispatch(opts, new RedirectHandler(dispatch, maxRedirections, opts, originalHandler))
- }
-
- constructor (dispatch, maxRedirections, opts, handler) {
- if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {
- throw new InvalidArgumentError('maxRedirections must be a positive number')
- }
-
- this.dispatch = dispatch
- this.location = null
- const { maxRedirections: _, ...cleanOpts } = opts
- this.opts = cleanOpts // opts must be a copy, exclude maxRedirections
- this.maxRedirections = maxRedirections
- this.handler = handler
- this.history = []
-
- if (util.isStream(this.opts.body)) {
- // TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
- // so that it can be dispatched again?
- // TODO (fix): Do we need 100-expect support to provide a way to do this properly?
- if (util.bodyLength(this.opts.body) === 0) {
- this.opts.body
- .on('data', function () {
- assert(false)
- })
- }
-
- if (typeof this.opts.body.readableDidRead !== 'boolean') {
- this.opts.body[kBodyUsed] = false
- EE.prototype.on.call(this.opts.body, 'data', function () {
- this[kBodyUsed] = true
- })
- }
- } else if (this.opts.body && typeof this.opts.body.pipeTo === 'function') {
- // TODO (fix): We can't access ReadableStream internal state
- // to determine whether or not it has been disturbed. This is just
- // a workaround.
- this.opts.body = new BodyAsyncIterable(this.opts.body)
- } else if (
- this.opts.body &&
- typeof this.opts.body !== 'string' &&
- !ArrayBuffer.isView(this.opts.body) &&
- util.isIterable(this.opts.body) &&
- !util.isFormDataLike(this.opts.body)
- ) {
- // TODO: Should we allow re-using iterable if !this.opts.idempotent
- // or through some other flag?
- this.opts.body = new BodyAsyncIterable(this.opts.body)
- }
- }
-
- onRequestStart (controller, context) {
- this.handler.onRequestStart?.(controller, { ...context, history: this.history })
- }
-
- onRequestUpgrade (controller, statusCode, headers, socket) {
- this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
- }
-
- onResponseStart (controller, statusCode, headers, statusMessage) {
- if (this.opts.throwOnMaxRedirect && this.history.length >= this.maxRedirections) {
- throw new Error('max redirects')
- }
-
- // https://tools.ietf.org/html/rfc7231#section-6.4.2
- // https://fetch.spec.whatwg.org/#http-redirect-fetch
- // In case of HTTP 301 or 302 with POST, change the method to GET
- if ((statusCode === 301 || statusCode === 302) && this.opts.method === 'POST') {
- this.opts.method = 'GET'
- if (util.isStream(this.opts.body)) {
- util.destroy(this.opts.body.on('error', noop))
- }
- this.opts.body = null
- }
-
- // https://tools.ietf.org/html/rfc7231#section-6.4.4
- // In case of HTTP 303, always replace method to be either HEAD or GET
- if (statusCode === 303 && this.opts.method !== 'HEAD') {
- this.opts.method = 'GET'
- if (util.isStream(this.opts.body)) {
- util.destroy(this.opts.body.on('error', noop))
- }
- this.opts.body = null
- }
-
- this.location = this.history.length >= this.maxRedirections || util.isDisturbed(this.opts.body) || redirectableStatusCodes.indexOf(statusCode) === -1
- ? null
- : headers.location
-
- if (this.opts.origin) {
- this.history.push(new URL(this.opts.path, this.opts.origin))
- }
-
- if (!this.location) {
- this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)
- return
- }
-
- const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)))
- const path = search ? `${pathname}${search}` : pathname
-
- // Check for redirect loops by seeing if we've already visited this URL in our history
- // This catches the case where Client/Pool try to handle cross-origin redirects but fail
- // and keep redirecting to the same URL in an infinite loop
- const redirectUrlString = `${origin}${path}`
- for (const historyUrl of this.history) {
- if (historyUrl.toString() === redirectUrlString) {
- throw new InvalidArgumentError(`Redirect loop detected. Cannot redirect to ${origin}. This typically happens when using a Client or Pool with cross-origin redirects. Use an Agent for cross-origin redirects.`)
- }
- }
-
- // Remove headers referring to the original URL.
- // By default it is Host only, unless it's a 303 (see below), which removes also all Content-* headers.
- // https://tools.ietf.org/html/rfc7231#section-6.4
- this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin)
- this.opts.path = path
- this.opts.origin = origin
- this.opts.query = null
- }
-
- onResponseData (controller, chunk) {
- if (this.location) {
- /*
- https://tools.ietf.org/html/rfc7231#section-6.4
-
- TLDR: undici always ignores 3xx response bodies.
-
- Redirection is used to serve the requested resource from another URL, so it assumes that
- no body is generated (and thus can be ignored). Even though generating a body is not prohibited.
-
- For status 301, 302, 303, 307 and 308 (the latter from RFC 7238), the specs mention that the body usually
- (which means it's optional and not mandated) contain just an hyperlink to the value of
- the Location response header, so the body can be ignored safely.
-
- For status 300, which is "Multiple Choices", the spec mentions both generating a Location
- response header AND a response body with the other possible location to follow.
- Since the spec explicitly chooses not to specify a format for such body and leave it to
- servers and browsers implementors, we ignore the body as there is no specified way to eventually parse it.
- */
- } else {
- this.handler.onResponseData?.(controller, chunk)
- }
- }
-
- onResponseEnd (controller, trailers) {
- if (this.location) {
- /*
- https://tools.ietf.org/html/rfc7231#section-6.4
-
- TLDR: undici always ignores 3xx response trailers as they are not expected in case of redirections
- and neither are useful if present.
-
- See comment on onData method above for more detailed information.
- */
- this.dispatch(this.opts, this)
- } else {
- this.handler.onResponseEnd(controller, trailers)
- }
- }
-
- onResponseError (controller, error) {
- this.handler.onResponseError?.(controller, error)
- }
-}
-
-// https://tools.ietf.org/html/rfc7231#section-6.4.4
-function shouldRemoveHeader (header, removeContent, unknownOrigin) {
- if (header.length === 4) {
- return util.headerNameToString(header) === 'host'
- }
- if (removeContent && util.headerNameToString(header).startsWith('content-')) {
- return true
- }
- if (unknownOrigin && (header.length === 13 || header.length === 6 || header.length === 19)) {
- const name = util.headerNameToString(header)
- return name === 'authorization' || name === 'cookie' || name === 'proxy-authorization'
- }
- return false
-}
-
-// https://tools.ietf.org/html/rfc7231#section-6.4
-function cleanRequestHeaders (headers, removeContent, unknownOrigin) {
- const ret = []
- if (Array.isArray(headers)) {
- for (let i = 0; i < headers.length; i += 2) {
- if (!shouldRemoveHeader(headers[i], removeContent, unknownOrigin)) {
- ret.push(headers[i], headers[i + 1])
- }
- }
- } else if (headers && typeof headers === 'object') {
- const entries = typeof headers[Symbol.iterator] === 'function' ? headers : Object.entries(headers)
- for (const [key, value] of entries) {
- if (!shouldRemoveHeader(key, removeContent, unknownOrigin)) {
- ret.push(key, value)
- }
- }
- } else {
- assert(headers == null, 'headers must be an object or an array')
- }
- return ret
-}
-
-module.exports = RedirectHandler
diff --git a/vanilla/node_modules/undici/lib/handler/retry-handler.js b/vanilla/node_modules/undici/lib/handler/retry-handler.js
deleted file mode 100644
index 1cbc789..0000000
--- a/vanilla/node_modules/undici/lib/handler/retry-handler.js
+++ /dev/null
@@ -1,394 +0,0 @@
-'use strict'
-const assert = require('node:assert')
-
-const { kRetryHandlerDefaultRetry } = require('../core/symbols')
-const { RequestRetryError } = require('../core/errors')
-const WrapHandler = require('./wrap-handler')
-const {
- isDisturbed,
- parseRangeHeader,
- wrapRequestBody
-} = require('../core/util')
-
-function calculateRetryAfterHeader (retryAfter) {
- const retryTime = new Date(retryAfter).getTime()
- return isNaN(retryTime) ? 0 : retryTime - Date.now()
-}
-
-class RetryHandler {
- constructor (opts, { dispatch, handler }) {
- const { retryOptions, ...dispatchOpts } = opts
- const {
- // Retry scoped
- retry: retryFn,
- maxRetries,
- maxTimeout,
- minTimeout,
- timeoutFactor,
- // Response scoped
- methods,
- errorCodes,
- retryAfter,
- statusCodes,
- throwOnError
- } = retryOptions ?? {}
-
- this.error = null
- this.dispatch = dispatch
- this.handler = WrapHandler.wrap(handler)
- this.opts = { ...dispatchOpts, body: wrapRequestBody(opts.body) }
- this.retryOpts = {
- throwOnError: throwOnError ?? true,
- retry: retryFn ?? RetryHandler[kRetryHandlerDefaultRetry],
- retryAfter: retryAfter ?? true,
- maxTimeout: maxTimeout ?? 30 * 1000, // 30s,
- minTimeout: minTimeout ?? 500, // .5s
- timeoutFactor: timeoutFactor ?? 2,
- maxRetries: maxRetries ?? 5,
- // What errors we should retry
- methods: methods ?? ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE'],
- // Indicates which errors to retry
- statusCodes: statusCodes ?? [500, 502, 503, 504, 429],
- // List of errors to retry
- errorCodes: errorCodes ?? [
- 'ECONNRESET',
- 'ECONNREFUSED',
- 'ENOTFOUND',
- 'ENETDOWN',
- 'ENETUNREACH',
- 'EHOSTDOWN',
- 'EHOSTUNREACH',
- 'EPIPE',
- 'UND_ERR_SOCKET'
- ]
- }
-
- this.retryCount = 0
- this.retryCountCheckpoint = 0
- this.headersSent = false
- this.start = 0
- this.end = null
- this.etag = null
- }
-
- onResponseStartWithRetry (controller, statusCode, headers, statusMessage, err) {
- if (this.retryOpts.throwOnError) {
- // Preserve old behavior for status codes that are not eligible for retry
- if (this.retryOpts.statusCodes.includes(statusCode) === false) {
- this.headersSent = true
- this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)
- } else {
- this.error = err
- }
-
- return
- }
-
- if (isDisturbed(this.opts.body)) {
- this.headersSent = true
- this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)
- return
- }
-
- function shouldRetry (passedErr) {
- if (passedErr) {
- this.headersSent = true
- this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)
- controller.resume()
- return
- }
-
- this.error = err
- controller.resume()
- }
-
- controller.pause()
- this.retryOpts.retry(
- err,
- {
- state: { counter: this.retryCount },
- opts: { retryOptions: this.retryOpts, ...this.opts }
- },
- shouldRetry.bind(this)
- )
- }
-
- onRequestStart (controller, context) {
- if (!this.headersSent) {
- this.handler.onRequestStart?.(controller, context)
- }
- }
-
- onRequestUpgrade (controller, statusCode, headers, socket) {
- this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
- }
-
- static [kRetryHandlerDefaultRetry] (err, { state, opts }, cb) {
- const { statusCode, code, headers } = err
- const { method, retryOptions } = opts
- const {
- maxRetries,
- minTimeout,
- maxTimeout,
- timeoutFactor,
- statusCodes,
- errorCodes,
- methods
- } = retryOptions
- const { counter } = state
-
- // Any code that is not a Undici's originated and allowed to retry
- if (code && code !== 'UND_ERR_REQ_RETRY' && !errorCodes.includes(code)) {
- cb(err)
- return
- }
-
- // If a set of method are provided and the current method is not in the list
- if (Array.isArray(methods) && !methods.includes(method)) {
- cb(err)
- return
- }
-
- // If a set of status code are provided and the current status code is not in the list
- if (
- statusCode != null &&
- Array.isArray(statusCodes) &&
- !statusCodes.includes(statusCode)
- ) {
- cb(err)
- return
- }
-
- // If we reached the max number of retries
- if (counter > maxRetries) {
- cb(err)
- return
- }
-
- let retryAfterHeader = headers?.['retry-after']
- if (retryAfterHeader) {
- retryAfterHeader = Number(retryAfterHeader)
- retryAfterHeader = Number.isNaN(retryAfterHeader)
- ? calculateRetryAfterHeader(headers['retry-after'])
- : retryAfterHeader * 1e3 // Retry-After is in seconds
- }
-
- const retryTimeout =
- retryAfterHeader > 0
- ? Math.min(retryAfterHeader, maxTimeout)
- : Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout)
-
- setTimeout(() => cb(null), retryTimeout)
- }
-
- onResponseStart (controller, statusCode, headers, statusMessage) {
- this.error = null
- this.retryCount += 1
-
- if (statusCode >= 300) {
- const err = new RequestRetryError('Request failed', statusCode, {
- headers,
- data: {
- count: this.retryCount
- }
- })
-
- this.onResponseStartWithRetry(controller, statusCode, headers, statusMessage, err)
- return
- }
-
- // Checkpoint for resume from where we left it
- if (this.headersSent) {
- // Only Partial Content 206 supposed to provide Content-Range,
- // any other status code that partially consumed the payload
- // should not be retried because it would result in downstream
- // wrongly concatenate multiple responses.
- if (statusCode !== 206 && (this.start > 0 || statusCode !== 200)) {
- throw new RequestRetryError('server does not support the range header and the payload was partially consumed', statusCode, {
- headers,
- data: { count: this.retryCount }
- })
- }
-
- const contentRange = parseRangeHeader(headers['content-range'])
- // If no content range
- if (!contentRange) {
- // We always throw here as we want to indicate that we entred unexpected path
- throw new RequestRetryError('Content-Range mismatch', statusCode, {
- headers,
- data: { count: this.retryCount }
- })
- }
-
- // Let's start with a weak etag check
- if (this.etag != null && this.etag !== headers.etag) {
- // We always throw here as we want to indicate that we entred unexpected path
- throw new RequestRetryError('ETag mismatch', statusCode, {
- headers,
- data: { count: this.retryCount }
- })
- }
-
- const { start, size, end = size ? size - 1 : null } = contentRange
-
- assert(this.start === start, 'content-range mismatch')
- assert(this.end == null || this.end === end, 'content-range mismatch')
-
- return
- }
-
- if (this.end == null) {
- if (statusCode === 206) {
- // First time we receive 206
- const range = parseRangeHeader(headers['content-range'])
-
- if (range == null) {
- this.headersSent = true
- this.handler.onResponseStart?.(
- controller,
- statusCode,
- headers,
- statusMessage
- )
- return
- }
-
- const { start, size, end = size ? size - 1 : null } = range
- assert(
- start != null && Number.isFinite(start),
- 'content-range mismatch'
- )
- assert(end != null && Number.isFinite(end), 'invalid content-length')
-
- this.start = start
- this.end = end
- }
-
- // We make our best to checkpoint the body for further range headers
- if (this.end == null) {
- const contentLength = headers['content-length']
- this.end = contentLength != null ? Number(contentLength) - 1 : null
- }
-
- assert(Number.isFinite(this.start))
- assert(
- this.end == null || Number.isFinite(this.end),
- 'invalid content-length'
- )
-
- this.resume = true
- this.etag = headers.etag != null ? headers.etag : null
-
- // Weak etags are not useful for comparison nor cache
- // for instance not safe to assume if the response is byte-per-byte
- // equal
- if (
- this.etag != null &&
- this.etag[0] === 'W' &&
- this.etag[1] === '/'
- ) {
- this.etag = null
- }
-
- this.headersSent = true
- this.handler.onResponseStart?.(
- controller,
- statusCode,
- headers,
- statusMessage
- )
- } else {
- throw new RequestRetryError('Request failed', statusCode, {
- headers,
- data: { count: this.retryCount }
- })
- }
- }
-
- onResponseData (controller, chunk) {
- if (this.error) {
- return
- }
-
- this.start += chunk.length
-
- this.handler.onResponseData?.(controller, chunk)
- }
-
- onResponseEnd (controller, trailers) {
- if (this.error && this.retryOpts.throwOnError) {
- throw this.error
- }
-
- if (!this.error) {
- this.retryCount = 0
- return this.handler.onResponseEnd?.(controller, trailers)
- }
-
- this.retry(controller)
- }
-
- retry (controller) {
- if (this.start !== 0) {
- const headers = { range: `bytes=${this.start}-${this.end ?? ''}` }
-
- // Weak etag check - weak etags will make comparison algorithms never match
- if (this.etag != null) {
- headers['if-match'] = this.etag
- }
-
- this.opts = {
- ...this.opts,
- headers: {
- ...this.opts.headers,
- ...headers
- }
- }
- }
-
- try {
- this.retryCountCheckpoint = this.retryCount
- this.dispatch(this.opts, this)
- } catch (err) {
- this.handler.onResponseError?.(controller, err)
- }
- }
-
- onResponseError (controller, err) {
- if (controller?.aborted || isDisturbed(this.opts.body)) {
- this.handler.onResponseError?.(controller, err)
- return
- }
-
- function shouldRetry (returnedErr) {
- if (!returnedErr) {
- this.retry(controller)
- return
- }
-
- this.handler?.onResponseError?.(controller, returnedErr)
- }
-
- // We reconcile in case of a mix between network errors
- // and server error response
- if (this.retryCount - this.retryCountCheckpoint > 0) {
- // We count the difference between the last checkpoint and the current retry count
- this.retryCount =
- this.retryCountCheckpoint +
- (this.retryCount - this.retryCountCheckpoint)
- } else {
- this.retryCount += 1
- }
-
- this.retryOpts.retry(
- err,
- {
- state: { counter: this.retryCount },
- opts: { retryOptions: this.retryOpts, ...this.opts }
- },
- shouldRetry.bind(this)
- )
- }
-}
-
-module.exports = RetryHandler
diff --git a/vanilla/node_modules/undici/lib/handler/unwrap-handler.js b/vanilla/node_modules/undici/lib/handler/unwrap-handler.js
deleted file mode 100644
index 865593a..0000000
--- a/vanilla/node_modules/undici/lib/handler/unwrap-handler.js
+++ /dev/null
@@ -1,96 +0,0 @@
-'use strict'
-
-const { parseHeaders } = require('../core/util')
-const { InvalidArgumentError } = require('../core/errors')
-
-const kResume = Symbol('resume')
-
-class UnwrapController {
- #paused = false
- #reason = null
- #aborted = false
- #abort
-
- [kResume] = null
-
- constructor (abort) {
- this.#abort = abort
- }
-
- pause () {
- this.#paused = true
- }
-
- resume () {
- if (this.#paused) {
- this.#paused = false
- this[kResume]?.()
- }
- }
-
- abort (reason) {
- if (!this.#aborted) {
- this.#aborted = true
- this.#reason = reason
- this.#abort(reason)
- }
- }
-
- get aborted () {
- return this.#aborted
- }
-
- get reason () {
- return this.#reason
- }
-
- get paused () {
- return this.#paused
- }
-}
-
-module.exports = class UnwrapHandler {
- #handler
- #controller
-
- constructor (handler) {
- this.#handler = handler
- }
-
- static unwrap (handler) {
- // TODO (fix): More checks...
- return !handler.onRequestStart ? handler : new UnwrapHandler(handler)
- }
-
- onConnect (abort, context) {
- this.#controller = new UnwrapController(abort)
- this.#handler.onRequestStart?.(this.#controller, context)
- }
-
- onUpgrade (statusCode, rawHeaders, socket) {
- this.#handler.onRequestUpgrade?.(this.#controller, statusCode, parseHeaders(rawHeaders), socket)
- }
-
- onHeaders (statusCode, rawHeaders, resume, statusMessage) {
- this.#controller[kResume] = resume
- this.#handler.onResponseStart?.(this.#controller, statusCode, parseHeaders(rawHeaders), statusMessage)
- return !this.#controller.paused
- }
-
- onData (data) {
- this.#handler.onResponseData?.(this.#controller, data)
- return !this.#controller.paused
- }
-
- onComplete (rawTrailers) {
- this.#handler.onResponseEnd?.(this.#controller, parseHeaders(rawTrailers))
- }
-
- onError (err) {
- if (!this.#handler.onResponseError) {
- throw new InvalidArgumentError('invalid onError method')
- }
-
- this.#handler.onResponseError?.(this.#controller, err)
- }
-}
diff --git a/vanilla/node_modules/undici/lib/handler/wrap-handler.js b/vanilla/node_modules/undici/lib/handler/wrap-handler.js
deleted file mode 100644
index 47caa5f..0000000
--- a/vanilla/node_modules/undici/lib/handler/wrap-handler.js
+++ /dev/null
@@ -1,95 +0,0 @@
-'use strict'
-
-const { InvalidArgumentError } = require('../core/errors')
-
-module.exports = class WrapHandler {
- #handler
-
- constructor (handler) {
- this.#handler = handler
- }
-
- static wrap (handler) {
- // TODO (fix): More checks...
- return handler.onRequestStart ? handler : new WrapHandler(handler)
- }
-
- // Unwrap Interface
-
- onConnect (abort, context) {
- return this.#handler.onConnect?.(abort, context)
- }
-
- onHeaders (statusCode, rawHeaders, resume, statusMessage) {
- return this.#handler.onHeaders?.(statusCode, rawHeaders, resume, statusMessage)
- }
-
- onUpgrade (statusCode, rawHeaders, socket) {
- return this.#handler.onUpgrade?.(statusCode, rawHeaders, socket)
- }
-
- onData (data) {
- return this.#handler.onData?.(data)
- }
-
- onComplete (trailers) {
- return this.#handler.onComplete?.(trailers)
- }
-
- onError (err) {
- if (!this.#handler.onError) {
- throw err
- }
-
- return this.#handler.onError?.(err)
- }
-
- // Wrap Interface
-
- onRequestStart (controller, context) {
- this.#handler.onConnect?.((reason) => controller.abort(reason), context)
- }
-
- onRequestUpgrade (controller, statusCode, headers, socket) {
- const rawHeaders = []
- for (const [key, val] of Object.entries(headers)) {
- rawHeaders.push(Buffer.from(key), Array.isArray(val) ? val.map(v => Buffer.from(v)) : Buffer.from(val))
- }
-
- this.#handler.onUpgrade?.(statusCode, rawHeaders, socket)
- }
-
- onResponseStart (controller, statusCode, headers, statusMessage) {
- const rawHeaders = []
- for (const [key, val] of Object.entries(headers)) {
- rawHeaders.push(Buffer.from(key), Array.isArray(val) ? val.map(v => Buffer.from(v)) : Buffer.from(val))
- }
-
- if (this.#handler.onHeaders?.(statusCode, rawHeaders, () => controller.resume(), statusMessage) === false) {
- controller.pause()
- }
- }
-
- onResponseData (controller, data) {
- if (this.#handler.onData?.(data) === false) {
- controller.pause()
- }
- }
-
- onResponseEnd (controller, trailers) {
- const rawTrailers = []
- for (const [key, val] of Object.entries(trailers)) {
- rawTrailers.push(Buffer.from(key), Array.isArray(val) ? val.map(v => Buffer.from(v)) : Buffer.from(val))
- }
-
- this.#handler.onComplete?.(rawTrailers)
- }
-
- onResponseError (controller, err) {
- if (!this.#handler.onError) {
- throw new InvalidArgumentError('invalid onError method')
- }
-
- this.#handler.onError?.(err)
- }
-}
diff --git a/vanilla/node_modules/undici/lib/interceptor/cache.js b/vanilla/node_modules/undici/lib/interceptor/cache.js
deleted file mode 100644
index 81d7cb1..0000000
--- a/vanilla/node_modules/undici/lib/interceptor/cache.js
+++ /dev/null
@@ -1,495 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { Readable } = require('node:stream')
-const util = require('../core/util')
-const CacheHandler = require('../handler/cache-handler')
-const MemoryCacheStore = require('../cache/memory-cache-store')
-const CacheRevalidationHandler = require('../handler/cache-revalidation-handler')
-const { assertCacheStore, assertCacheMethods, makeCacheKey, normalizeHeaders, parseCacheControlHeader } = require('../util/cache.js')
-const { AbortError } = require('../core/errors.js')
-
-/**
- * @param {(string | RegExp)[] | undefined} origins
- * @param {string} name
- */
-function assertCacheOrigins (origins, name) {
- if (origins === undefined) return
- if (!Array.isArray(origins)) {
- throw new TypeError(`expected ${name} to be an array or undefined, got ${typeof origins}`)
- }
- for (let i = 0; i < origins.length; i++) {
- const origin = origins[i]
- if (typeof origin !== 'string' && !(origin instanceof RegExp)) {
- throw new TypeError(`expected ${name}[${i}] to be a string or RegExp, got ${typeof origin}`)
- }
- }
-}
-
-const nop = () => {}
-
-/**
- * @typedef {(options: import('../../types/dispatcher.d.ts').default.DispatchOptions, handler: import('../../types/dispatcher.d.ts').default.DispatchHandler) => void} DispatchFn
- */
-
-/**
- * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives | undefined} cacheControlDirectives
- * @param {import('../../types/dispatcher.d.ts').default.RequestOptions} opts
- * @returns {boolean}
- */
-function needsRevalidation (result, cacheControlDirectives, { headers = {} }) {
- // Always revalidate requests with the no-cache request directive.
- if (cacheControlDirectives?.['no-cache']) {
- return true
- }
-
- // Always revalidate requests with unqualified no-cache response directive.
- if (result.cacheControlDirectives?.['no-cache'] && !Array.isArray(result.cacheControlDirectives['no-cache'])) {
- return true
- }
-
- // Always revalidate requests with conditional headers.
- if (headers['if-modified-since'] || headers['if-none-match']) {
- return true
- }
-
- return false
-}
-
-/**
- * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives | undefined} cacheControlDirectives
- * @returns {boolean}
- */
-function isStale (result, cacheControlDirectives) {
- const now = Date.now()
- if (now > result.staleAt) {
- // Response is stale
- if (cacheControlDirectives?.['max-stale']) {
- // There's a threshold where we can serve stale responses, let's see if
- // we're in it
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-max-stale
- const gracePeriod = result.staleAt + (cacheControlDirectives['max-stale'] * 1000)
- return now > gracePeriod
- }
-
- return true
- }
-
- if (cacheControlDirectives?.['min-fresh']) {
- // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.3
-
- // At this point, staleAt is always > now
- const timeLeftTillStale = result.staleAt - now
- const threshold = cacheControlDirectives['min-fresh'] * 1000
-
- return timeLeftTillStale <= threshold
- }
-
- return false
-}
-
-/**
- * Check if we're within the stale-while-revalidate window for a stale response
- * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result
- * @returns {boolean}
- */
-function withinStaleWhileRevalidateWindow (result) {
- const staleWhileRevalidate = result.cacheControlDirectives?.['stale-while-revalidate']
- if (!staleWhileRevalidate) {
- return false
- }
-
- const now = Date.now()
- const staleWhileRevalidateExpiry = result.staleAt + (staleWhileRevalidate * 1000)
- return now <= staleWhileRevalidateExpiry
-}
-
-/**
- * @param {DispatchFn} dispatch
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions} globalOpts
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey
- * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler
- * @param {import('../../types/dispatcher.d.ts').default.RequestOptions} opts
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives | undefined} reqCacheControl
- */
-function handleUncachedResponse (
- dispatch,
- globalOpts,
- cacheKey,
- handler,
- opts,
- reqCacheControl
-) {
- if (reqCacheControl?.['only-if-cached']) {
- let aborted = false
- try {
- if (typeof handler.onConnect === 'function') {
- handler.onConnect(() => {
- aborted = true
- })
-
- if (aborted) {
- return
- }
- }
-
- if (typeof handler.onHeaders === 'function') {
- handler.onHeaders(504, [], nop, 'Gateway Timeout')
- if (aborted) {
- return
- }
- }
-
- if (typeof handler.onComplete === 'function') {
- handler.onComplete([])
- }
- } catch (err) {
- if (typeof handler.onError === 'function') {
- handler.onError(err)
- }
- }
-
- return true
- }
-
- return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler))
-}
-
-/**
- * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler
- * @param {import('../../types/dispatcher.d.ts').default.RequestOptions} opts
- * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result
- * @param {number} age
- * @param {any} context
- * @param {boolean} isStale
- */
-function sendCachedValue (handler, opts, result, age, context, isStale) {
- // TODO (perf): Readable.from path can be optimized...
- const stream = util.isStream(result.body)
- ? result.body
- : Readable.from(result.body ?? [])
-
- assert(!stream.destroyed, 'stream should not be destroyed')
- assert(!stream.readableDidRead, 'stream should not be readableDidRead')
-
- const controller = {
- resume () {
- stream.resume()
- },
- pause () {
- stream.pause()
- },
- get paused () {
- return stream.isPaused()
- },
- get aborted () {
- return stream.destroyed
- },
- get reason () {
- return stream.errored
- },
- abort (reason) {
- stream.destroy(reason ?? new AbortError())
- }
- }
-
- stream
- .on('error', function (err) {
- if (!this.readableEnded) {
- if (typeof handler.onResponseError === 'function') {
- handler.onResponseError(controller, err)
- } else {
- throw err
- }
- }
- })
- .on('close', function () {
- if (!this.errored) {
- handler.onResponseEnd?.(controller, {})
- }
- })
-
- handler.onRequestStart?.(controller, context)
-
- if (stream.destroyed) {
- return
- }
-
- // Add the age header
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-age
- const headers = { ...result.headers, age: String(age) }
-
- if (isStale) {
- // Add warning header
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Warning
- headers.warning = '110 - "response is stale"'
- }
-
- handler.onResponseStart?.(controller, result.statusCode, headers, result.statusMessage)
-
- if (opts.method === 'HEAD') {
- stream.destroy()
- } else {
- stream.on('data', function (chunk) {
- handler.onResponseData?.(controller, chunk)
- })
- }
-}
-
-/**
- * @param {DispatchFn} dispatch
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions} globalOpts
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey
- * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler
- * @param {import('../../types/dispatcher.d.ts').default.RequestOptions} opts
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives | undefined} reqCacheControl
- * @param {import('../../types/cache-interceptor.d.ts').default.GetResult | undefined} result
- */
-function handleResult (
- dispatch,
- globalOpts,
- cacheKey,
- handler,
- opts,
- reqCacheControl,
- result
-) {
- if (!result) {
- return handleUncachedResponse(dispatch, globalOpts, cacheKey, handler, opts, reqCacheControl)
- }
-
- const now = Date.now()
- if (now > result.deleteAt) {
- // Response is expired, cache store shouldn't have given this to us
- return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler))
- }
-
- const age = Math.round((now - result.cachedAt) / 1000)
- if (reqCacheControl?.['max-age'] && age >= reqCacheControl['max-age']) {
- // Response is considered expired for this specific request
- // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.1
- return dispatch(opts, handler)
- }
-
- const stale = isStale(result, reqCacheControl)
- const revalidate = needsRevalidation(result, reqCacheControl, opts)
-
- // Check if the response is stale
- if (stale || revalidate) {
- if (util.isStream(opts.body) && util.bodyLength(opts.body) !== 0) {
- // If body is a stream we can't revalidate...
- // TODO (fix): This could be less strict...
- return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler))
- }
-
- // RFC 5861: If we're within stale-while-revalidate window, serve stale immediately
- // and revalidate in background, unless immediate revalidation is necessary
- if (!revalidate && withinStaleWhileRevalidateWindow(result)) {
- // Serve stale response immediately
- sendCachedValue(handler, opts, result, age, null, true)
-
- // Start background revalidation (fire-and-forget)
- queueMicrotask(() => {
- const headers = {
- ...opts.headers,
- 'if-modified-since': new Date(result.cachedAt).toUTCString()
- }
-
- if (result.etag) {
- headers['if-none-match'] = result.etag
- }
-
- if (result.vary) {
- for (const key in result.vary) {
- if (result.vary[key] != null) {
- headers[key] = result.vary[key]
- }
- }
- }
-
- // Background revalidation - update cache if we get new data
- dispatch(
- {
- ...opts,
- headers
- },
- new CacheHandler(globalOpts, cacheKey, {
- // Silent handler that just updates the cache
- onRequestStart () {},
- onRequestUpgrade () {},
- onResponseStart () {},
- onResponseData () {},
- onResponseEnd () {},
- onResponseError () {}
- })
- )
- })
-
- return true
- }
-
- let withinStaleIfErrorThreshold = false
- const staleIfErrorExpiry = result.cacheControlDirectives['stale-if-error'] ?? reqCacheControl?.['stale-if-error']
- if (staleIfErrorExpiry) {
- withinStaleIfErrorThreshold = now < (result.staleAt + (staleIfErrorExpiry * 1000))
- }
-
- const headers = {
- ...opts.headers,
- 'if-modified-since': new Date(result.cachedAt).toUTCString()
- }
-
- if (result.etag) {
- headers['if-none-match'] = result.etag
- }
-
- if (result.vary) {
- for (const key in result.vary) {
- if (result.vary[key] != null) {
- headers[key] = result.vary[key]
- }
- }
- }
-
- // We need to revalidate the response
- return dispatch(
- {
- ...opts,
- headers
- },
- new CacheRevalidationHandler(
- (success, context) => {
- if (success) {
- // TODO: successful revalidation should be considered fresh (not give stale warning).
- sendCachedValue(handler, opts, result, age, context, stale)
- } else if (util.isStream(result.body)) {
- result.body.on('error', nop).destroy()
- }
- },
- new CacheHandler(globalOpts, cacheKey, handler),
- withinStaleIfErrorThreshold
- )
- )
- }
-
- // Dump request body.
- if (util.isStream(opts.body)) {
- opts.body.on('error', nop).destroy()
- }
-
- sendCachedValue(handler, opts, result, age, null, false)
-}
-
-/**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions} [opts]
- * @returns {import('../../types/dispatcher.d.ts').default.DispatcherComposeInterceptor}
- */
-module.exports = (opts = {}) => {
- const {
- store = new MemoryCacheStore(),
- methods = ['GET'],
- cacheByDefault = undefined,
- type = 'shared',
- origins = undefined
- } = opts
-
- if (typeof opts !== 'object' || opts === null) {
- throw new TypeError(`expected type of opts to be an Object, got ${opts === null ? 'null' : typeof opts}`)
- }
-
- assertCacheStore(store, 'opts.store')
- assertCacheMethods(methods, 'opts.methods')
- assertCacheOrigins(origins, 'opts.origins')
-
- if (typeof cacheByDefault !== 'undefined' && typeof cacheByDefault !== 'number') {
- throw new TypeError(`expected opts.cacheByDefault to be number or undefined, got ${typeof cacheByDefault}`)
- }
-
- if (typeof type !== 'undefined' && type !== 'shared' && type !== 'private') {
- throw new TypeError(`expected opts.type to be shared, private, or undefined, got ${typeof type}`)
- }
-
- const globalOpts = {
- store,
- methods,
- cacheByDefault,
- type
- }
-
- const safeMethodsToNotCache = util.safeHTTPMethods.filter(method => methods.includes(method) === false)
-
- return dispatch => {
- return (opts, handler) => {
- if (!opts.origin || safeMethodsToNotCache.includes(opts.method)) {
- // Not a method we want to cache or we don't have the origin, skip
- return dispatch(opts, handler)
- }
-
- // Check if origin is in whitelist
- if (origins !== undefined) {
- const requestOrigin = opts.origin.toString().toLowerCase()
- let isAllowed = false
-
- for (let i = 0; i < origins.length; i++) {
- const allowed = origins[i]
- if (typeof allowed === 'string') {
- if (allowed.toLowerCase() === requestOrigin) {
- isAllowed = true
- break
- }
- } else if (allowed.test(requestOrigin)) {
- isAllowed = true
- break
- }
- }
-
- if (!isAllowed) {
- return dispatch(opts, handler)
- }
- }
-
- opts = {
- ...opts,
- headers: normalizeHeaders(opts)
- }
-
- const reqCacheControl = opts.headers?.['cache-control']
- ? parseCacheControlHeader(opts.headers['cache-control'])
- : undefined
-
- if (reqCacheControl?.['no-store']) {
- return dispatch(opts, handler)
- }
-
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}
- */
- const cacheKey = makeCacheKey(opts)
- const result = store.get(cacheKey)
-
- if (result && typeof result.then === 'function') {
- return result
- .then(result => handleResult(dispatch,
- globalOpts,
- cacheKey,
- handler,
- opts,
- reqCacheControl,
- result
- ))
- } else {
- return handleResult(
- dispatch,
- globalOpts,
- cacheKey,
- handler,
- opts,
- reqCacheControl,
- result
- )
- }
- }
- }
-}
diff --git a/vanilla/node_modules/undici/lib/interceptor/decompress.js b/vanilla/node_modules/undici/lib/interceptor/decompress.js
deleted file mode 100644
index ee4202a..0000000
--- a/vanilla/node_modules/undici/lib/interceptor/decompress.js
+++ /dev/null
@@ -1,259 +0,0 @@
-'use strict'
-
-const { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = require('node:zlib')
-const { pipeline } = require('node:stream')
-const DecoratorHandler = require('../handler/decorator-handler')
-const { runtimeFeatures } = require('../util/runtime-features')
-
-/** @typedef {import('node:stream').Transform} Transform */
-/** @typedef {import('node:stream').Transform} Controller */
-/** @typedef {Transform&import('node:zlib').Zlib} DecompressorStream */
-
-/** @type {Record<string, () => DecompressorStream>} */
-const supportedEncodings = {
- gzip: createGunzip,
- 'x-gzip': createGunzip,
- br: createBrotliDecompress,
- deflate: createInflate,
- compress: createInflate,
- 'x-compress': createInflate,
- ...(runtimeFeatures.has('zstd') ? { zstd: createZstdDecompress } : {})
-}
-
-const defaultSkipStatusCodes = /** @type {const} */ ([204, 304])
-
-let warningEmitted = /** @type {boolean} */ (false)
-
-/**
- * @typedef {Object} DecompressHandlerOptions
- * @property {number[]|Readonly<number[]>} [skipStatusCodes=[204, 304]] - List of status codes to skip decompression for
- * @property {boolean} [skipErrorResponses] - Whether to skip decompression for error responses (status codes >= 400)
- */
-
-class DecompressHandler extends DecoratorHandler {
- /** @type {Transform[]} */
- #decompressors = []
- /** @type {Readonly<number[]>} */
- #skipStatusCodes
- /** @type {boolean} */
- #skipErrorResponses
-
- constructor (handler, { skipStatusCodes = defaultSkipStatusCodes, skipErrorResponses = true } = {}) {
- super(handler)
- this.#skipStatusCodes = skipStatusCodes
- this.#skipErrorResponses = skipErrorResponses
- }
-
- /**
- * Determines if decompression should be skipped based on encoding and status code
- * @param {string} contentEncoding - Content-Encoding header value
- * @param {number} statusCode - HTTP status code of the response
- * @returns {boolean} - True if decompression should be skipped
- */
- #shouldSkipDecompression (contentEncoding, statusCode) {
- if (!contentEncoding || statusCode < 200) return true
- if (this.#skipStatusCodes.includes(statusCode)) return true
- if (this.#skipErrorResponses && statusCode >= 400) return true
- return false
- }
-
- /**
- * Creates a chain of decompressors for multiple content encodings
- *
- * @param {string} encodings - Comma-separated list of content encodings
- * @returns {Array<DecompressorStream>} - Array of decompressor streams
- * @throws {Error} - If the number of content-encodings exceeds the maximum allowed
- */
- #createDecompressionChain (encodings) {
- const parts = encodings.split(',')
-
- // Limit the number of content-encodings to prevent resource exhaustion.
- // CVE fix similar to urllib3 (GHSA-gm62-xv2j-4w53) and curl (CVE-2022-32206).
- const maxContentEncodings = 5
- if (parts.length > maxContentEncodings) {
- throw new Error(`too many content-encodings in response: ${parts.length}, maximum allowed is ${maxContentEncodings}`)
- }
-
- /** @type {DecompressorStream[]} */
- const decompressors = []
-
- for (let i = parts.length - 1; i >= 0; i--) {
- const encoding = parts[i].trim()
- if (!encoding) continue
-
- if (!supportedEncodings[encoding]) {
- decompressors.length = 0 // Clear if unsupported encoding
- return decompressors // Unsupported encoding
- }
-
- decompressors.push(supportedEncodings[encoding]())
- }
-
- return decompressors
- }
-
- /**
- * Sets up event handlers for a decompressor stream using readable events
- * @param {DecompressorStream} decompressor - The decompressor stream
- * @param {Controller} controller - The controller to coordinate with
- * @returns {void}
- */
- #setupDecompressorEvents (decompressor, controller) {
- decompressor.on('readable', () => {
- let chunk
- while ((chunk = decompressor.read()) !== null) {
- const result = super.onResponseData(controller, chunk)
- if (result === false) {
- break
- }
- }
- })
-
- decompressor.on('error', (error) => {
- super.onResponseError(controller, error)
- })
- }
-
- /**
- * Sets up event handling for a single decompressor
- * @param {Controller} controller - The controller to handle events
- * @returns {void}
- */
- #setupSingleDecompressor (controller) {
- const decompressor = this.#decompressors[0]
- this.#setupDecompressorEvents(decompressor, controller)
-
- decompressor.on('end', () => {
- super.onResponseEnd(controller, {})
- })
- }
-
- /**
- * Sets up event handling for multiple chained decompressors using pipeline
- * @param {Controller} controller - The controller to handle events
- * @returns {void}
- */
- #setupMultipleDecompressors (controller) {
- const lastDecompressor = this.#decompressors[this.#decompressors.length - 1]
- this.#setupDecompressorEvents(lastDecompressor, controller)
-
- pipeline(this.#decompressors, (err) => {
- if (err) {
- super.onResponseError(controller, err)
- return
- }
- super.onResponseEnd(controller, {})
- })
- }
-
- /**
- * Cleans up decompressor references to prevent memory leaks
- * @returns {void}
- */
- #cleanupDecompressors () {
- this.#decompressors.length = 0
- }
-
- /**
- * @param {Controller} controller
- * @param {number} statusCode
- * @param {Record<string, string | string[] | undefined>} headers
- * @param {string} statusMessage
- * @returns {void}
- */
- onResponseStart (controller, statusCode, headers, statusMessage) {
- const contentEncoding = headers['content-encoding']
-
- // If content encoding is not supported or status code is in skip list
- if (this.#shouldSkipDecompression(contentEncoding, statusCode)) {
- return super.onResponseStart(controller, statusCode, headers, statusMessage)
- }
-
- const decompressors = this.#createDecompressionChain(contentEncoding.toLowerCase())
-
- if (decompressors.length === 0) {
- this.#cleanupDecompressors()
- return super.onResponseStart(controller, statusCode, headers, statusMessage)
- }
-
- this.#decompressors = decompressors
-
- // Remove compression headers since we're decompressing
- const { 'content-encoding': _, 'content-length': __, ...newHeaders } = headers
-
- if (this.#decompressors.length === 1) {
- this.#setupSingleDecompressor(controller)
- } else {
- this.#setupMultipleDecompressors(controller)
- }
-
- return super.onResponseStart(controller, statusCode, newHeaders, statusMessage)
- }
-
- /**
- * @param {Controller} controller
- * @param {Buffer} chunk
- * @returns {void}
- */
- onResponseData (controller, chunk) {
- if (this.#decompressors.length > 0) {
- this.#decompressors[0].write(chunk)
- return
- }
- super.onResponseData(controller, chunk)
- }
-
- /**
- * @param {Controller} controller
- * @param {Record<string, string | string[]> | undefined} trailers
- * @returns {void}
- */
- onResponseEnd (controller, trailers) {
- if (this.#decompressors.length > 0) {
- this.#decompressors[0].end()
- this.#cleanupDecompressors()
- return
- }
- super.onResponseEnd(controller, trailers)
- }
-
- /**
- * @param {Controller} controller
- * @param {Error} err
- * @returns {void}
- */
- onResponseError (controller, err) {
- if (this.#decompressors.length > 0) {
- for (const decompressor of this.#decompressors) {
- decompressor.destroy(err)
- }
- this.#cleanupDecompressors()
- }
- super.onResponseError(controller, err)
- }
-}
-
-/**
- * Creates a decompression interceptor for HTTP responses
- * @param {DecompressHandlerOptions} [options] - Options for the interceptor
- * @returns {Function} - Interceptor function
- */
-function createDecompressInterceptor (options = {}) {
- // Emit experimental warning only once
- if (!warningEmitted) {
- process.emitWarning(
- 'DecompressInterceptor is experimental and subject to change',
- 'ExperimentalWarning'
- )
- warningEmitted = true
- }
-
- return (dispatch) => {
- return (opts, handler) => {
- const decompressHandler = new DecompressHandler(handler, options)
- return dispatch(opts, decompressHandler)
- }
- }
-}
-
-module.exports = createDecompressInterceptor
diff --git a/vanilla/node_modules/undici/lib/interceptor/deduplicate.js b/vanilla/node_modules/undici/lib/interceptor/deduplicate.js
deleted file mode 100644
index 11c4f37..0000000
--- a/vanilla/node_modules/undici/lib/interceptor/deduplicate.js
+++ /dev/null
@@ -1,107 +0,0 @@
-'use strict'
-
-const diagnosticsChannel = require('node:diagnostics_channel')
-const util = require('../core/util')
-const DeduplicationHandler = require('../handler/deduplication-handler')
-const { normalizeHeaders, makeCacheKey, makeDeduplicationKey } = require('../util/cache.js')
-
-const pendingRequestsChannel = diagnosticsChannel.channel('undici:request:pending-requests')
-
-/**
- * @param {import('../../types/interceptors.d.ts').default.DeduplicateInterceptorOpts} [opts]
- * @returns {import('../../types/dispatcher.d.ts').default.DispatcherComposeInterceptor}
- */
-module.exports = (opts = {}) => {
- const {
- methods = ['GET'],
- skipHeaderNames = [],
- excludeHeaderNames = []
- } = opts
-
- if (typeof opts !== 'object' || opts === null) {
- throw new TypeError(`expected type of opts to be an Object, got ${opts === null ? 'null' : typeof opts}`)
- }
-
- if (!Array.isArray(methods)) {
- throw new TypeError(`expected opts.methods to be an array, got ${typeof methods}`)
- }
-
- for (const method of methods) {
- if (!util.safeHTTPMethods.includes(method)) {
- throw new TypeError(`expected opts.methods to only contain safe HTTP methods, got ${method}`)
- }
- }
-
- if (!Array.isArray(skipHeaderNames)) {
- throw new TypeError(`expected opts.skipHeaderNames to be an array, got ${typeof skipHeaderNames}`)
- }
-
- if (!Array.isArray(excludeHeaderNames)) {
- throw new TypeError(`expected opts.excludeHeaderNames to be an array, got ${typeof excludeHeaderNames}`)
- }
-
- // Convert to lowercase Set for case-insensitive header matching
- const skipHeaderNamesSet = new Set(skipHeaderNames.map(name => name.toLowerCase()))
-
- // Convert to lowercase Set for case-insensitive header exclusion from deduplication key
- const excludeHeaderNamesSet = new Set(excludeHeaderNames.map(name => name.toLowerCase()))
-
- /**
- * Map of pending requests for deduplication
- * @type {Map<string, DeduplicationHandler>}
- */
- const pendingRequests = new Map()
-
- return dispatch => {
- return (opts, handler) => {
- if (!opts.origin || methods.includes(opts.method) === false) {
- return dispatch(opts, handler)
- }
-
- opts = {
- ...opts,
- headers: normalizeHeaders(opts)
- }
-
- // Skip deduplication if request contains any of the specified headers
- if (skipHeaderNamesSet.size > 0) {
- for (const headerName of Object.keys(opts.headers)) {
- if (skipHeaderNamesSet.has(headerName.toLowerCase())) {
- return dispatch(opts, handler)
- }
- }
- }
-
- const cacheKey = makeCacheKey(opts)
- const dedupeKey = makeDeduplicationKey(cacheKey, excludeHeaderNamesSet)
-
- // Check if there's already a pending request for this key
- const pendingHandler = pendingRequests.get(dedupeKey)
- if (pendingHandler) {
- // Add this handler to the waiting list
- pendingHandler.addWaitingHandler(handler)
- return true
- }
-
- // Create a new deduplication handler
- const deduplicationHandler = new DeduplicationHandler(
- handler,
- () => {
- // Clean up when request completes
- pendingRequests.delete(dedupeKey)
- if (pendingRequestsChannel.hasSubscribers) {
- pendingRequestsChannel.publish({ size: pendingRequests.size, key: dedupeKey, type: 'removed' })
- }
- }
- )
-
- // Register the pending request
- pendingRequests.set(dedupeKey, deduplicationHandler)
- if (pendingRequestsChannel.hasSubscribers) {
- pendingRequestsChannel.publish({ size: pendingRequests.size, key: dedupeKey, type: 'added' })
- }
-
- return dispatch(opts, deduplicationHandler)
- }
- }
-}
diff --git a/vanilla/node_modules/undici/lib/interceptor/dns.js b/vanilla/node_modules/undici/lib/interceptor/dns.js
deleted file mode 100644
index 9dba957..0000000
--- a/vanilla/node_modules/undici/lib/interceptor/dns.js
+++ /dev/null
@@ -1,474 +0,0 @@
-'use strict'
-const { isIP } = require('node:net')
-const { lookup } = require('node:dns')
-const DecoratorHandler = require('../handler/decorator-handler')
-const { InvalidArgumentError, InformationalError } = require('../core/errors')
-const maxInt = Math.pow(2, 31) - 1
-
-class DNSStorage {
- #maxItems = 0
- #records = new Map()
-
- constructor (opts) {
- this.#maxItems = opts.maxItems
- }
-
- get size () {
- return this.#records.size
- }
-
- get (hostname) {
- return this.#records.get(hostname) ?? null
- }
-
- set (hostname, records) {
- this.#records.set(hostname, records)
- }
-
- delete (hostname) {
- this.#records.delete(hostname)
- }
-
- // Delegate to storage decide can we do more lookups or not
- full () {
- return this.size >= this.#maxItems
- }
-}
-
-class DNSInstance {
- #maxTTL = 0
- #maxItems = 0
- dualStack = true
- affinity = null
- lookup = null
- pick = null
- storage = null
-
- constructor (opts) {
- this.#maxTTL = opts.maxTTL
- this.#maxItems = opts.maxItems
- this.dualStack = opts.dualStack
- this.affinity = opts.affinity
- this.lookup = opts.lookup ?? this.#defaultLookup
- this.pick = opts.pick ?? this.#defaultPick
- this.storage = opts.storage ?? new DNSStorage(opts)
- }
-
- runLookup (origin, opts, cb) {
- const ips = this.storage.get(origin.hostname)
-
- // If full, we just return the origin
- if (ips == null && this.storage.full()) {
- cb(null, origin)
- return
- }
-
- const newOpts = {
- affinity: this.affinity,
- dualStack: this.dualStack,
- lookup: this.lookup,
- pick: this.pick,
- ...opts.dns,
- maxTTL: this.#maxTTL,
- maxItems: this.#maxItems
- }
-
- // If no IPs we lookup
- if (ips == null) {
- this.lookup(origin, newOpts, (err, addresses) => {
- if (err || addresses == null || addresses.length === 0) {
- cb(err ?? new InformationalError('No DNS entries found'))
- return
- }
-
- this.setRecords(origin, addresses)
- const records = this.storage.get(origin.hostname)
-
- const ip = this.pick(
- origin,
- records,
- newOpts.affinity
- )
-
- let port
- if (typeof ip.port === 'number') {
- port = `:${ip.port}`
- } else if (origin.port !== '') {
- port = `:${origin.port}`
- } else {
- port = ''
- }
-
- cb(
- null,
- new URL(`${origin.protocol}//${
- ip.family === 6 ? `[${ip.address}]` : ip.address
- }${port}`)
- )
- })
- } else {
- // If there's IPs we pick
- const ip = this.pick(
- origin,
- ips,
- newOpts.affinity
- )
-
- // If no IPs we lookup - deleting old records
- if (ip == null) {
- this.storage.delete(origin.hostname)
- this.runLookup(origin, opts, cb)
- return
- }
-
- let port
- if (typeof ip.port === 'number') {
- port = `:${ip.port}`
- } else if (origin.port !== '') {
- port = `:${origin.port}`
- } else {
- port = ''
- }
-
- cb(
- null,
- new URL(`${origin.protocol}//${
- ip.family === 6 ? `[${ip.address}]` : ip.address
- }${port}`)
- )
- }
- }
-
- #defaultLookup (origin, opts, cb) {
- lookup(
- origin.hostname,
- {
- all: true,
- family: this.dualStack === false ? this.affinity : 0,
- order: 'ipv4first'
- },
- (err, addresses) => {
- if (err) {
- return cb(err)
- }
-
- const results = new Map()
-
- for (const addr of addresses) {
- // On linux we found duplicates, we attempt to remove them with
- // the latest record
- results.set(`${addr.address}:${addr.family}`, addr)
- }
-
- cb(null, results.values())
- }
- )
- }
-
- #defaultPick (origin, hostnameRecords, affinity) {
- let ip = null
- const { records, offset } = hostnameRecords
-
- let family
- if (this.dualStack) {
- if (affinity == null) {
- // Balance between ip families
- if (offset == null || offset === maxInt) {
- hostnameRecords.offset = 0
- affinity = 4
- } else {
- hostnameRecords.offset++
- affinity = (hostnameRecords.offset & 1) === 1 ? 6 : 4
- }
- }
-
- if (records[affinity] != null && records[affinity].ips.length > 0) {
- family = records[affinity]
- } else {
- family = records[affinity === 4 ? 6 : 4]
- }
- } else {
- family = records[affinity]
- }
-
- // If no IPs we return null
- if (family == null || family.ips.length === 0) {
- return ip
- }
-
- if (family.offset == null || family.offset === maxInt) {
- family.offset = 0
- } else {
- family.offset++
- }
-
- const position = family.offset % family.ips.length
- ip = family.ips[position] ?? null
-
- if (ip == null) {
- return ip
- }
-
- if (Date.now() - ip.timestamp > ip.ttl) { // record TTL is already in ms
- // We delete expired records
- // It is possible that they have different TTL, so we manage them individually
- family.ips.splice(position, 1)
- return this.pick(origin, hostnameRecords, affinity)
- }
-
- return ip
- }
-
- pickFamily (origin, ipFamily) {
- const records = this.storage.get(origin.hostname)?.records
- if (!records) {
- return null
- }
-
- const family = records[ipFamily]
- if (!family) {
- return null
- }
-
- if (family.offset == null || family.offset === maxInt) {
- family.offset = 0
- } else {
- family.offset++
- }
-
- const position = family.offset % family.ips.length
- const ip = family.ips[position] ?? null
- if (ip == null) {
- return ip
- }
-
- if (Date.now() - ip.timestamp > ip.ttl) { // record TTL is already in ms
- // We delete expired records
- // It is possible that they have different TTL, so we manage them individually
- family.ips.splice(position, 1)
- }
-
- return ip
- }
-
- setRecords (origin, addresses) {
- const timestamp = Date.now()
- const records = { records: { 4: null, 6: null } }
- let minTTL = this.#maxTTL
- for (const record of addresses) {
- record.timestamp = timestamp
- if (typeof record.ttl === 'number') {
- // The record TTL is expected to be in ms
- record.ttl = Math.min(record.ttl, this.#maxTTL)
- minTTL = Math.min(minTTL, record.ttl)
- } else {
- record.ttl = this.#maxTTL
- }
-
- const familyRecords = records.records[record.family] ?? { ips: [] }
-
- familyRecords.ips.push(record)
- records.records[record.family] = familyRecords
- }
-
- // We provide a default TTL if external storage will be used without TTL per record-level support
- this.storage.set(origin.hostname, records, { ttl: minTTL })
- }
-
- deleteRecords (origin) {
- this.storage.delete(origin.hostname)
- }
-
- getHandler (meta, opts) {
- return new DNSDispatchHandler(this, meta, opts)
- }
-}
-
-class DNSDispatchHandler extends DecoratorHandler {
- #state = null
- #opts = null
- #dispatch = null
- #origin = null
- #controller = null
- #newOrigin = null
- #firstTry = true
-
- constructor (state, { origin, handler, dispatch, newOrigin }, opts) {
- super(handler)
- this.#origin = origin
- this.#newOrigin = newOrigin
- this.#opts = { ...opts }
- this.#state = state
- this.#dispatch = dispatch
- }
-
- onResponseError (controller, err) {
- switch (err.code) {
- case 'ETIMEDOUT':
- case 'ECONNREFUSED': {
- if (this.#state.dualStack) {
- if (!this.#firstTry) {
- super.onResponseError(controller, err)
- return
- }
- this.#firstTry = false
-
- // Pick an ip address from the other family
- const otherFamily = this.#newOrigin.hostname[0] === '[' ? 4 : 6
- const ip = this.#state.pickFamily(this.#origin, otherFamily)
- if (ip == null) {
- super.onResponseError(controller, err)
- return
- }
-
- let port
- if (typeof ip.port === 'number') {
- port = `:${ip.port}`
- } else if (this.#origin.port !== '') {
- port = `:${this.#origin.port}`
- } else {
- port = ''
- }
-
- const dispatchOpts = {
- ...this.#opts,
- origin: `${this.#origin.protocol}//${
- ip.family === 6 ? `[${ip.address}]` : ip.address
- }${port}`
- }
- this.#dispatch(dispatchOpts, this)
- return
- }
-
- // if dual-stack disabled, we error out
- super.onResponseError(controller, err)
- break
- }
- case 'ENOTFOUND':
- this.#state.deleteRecords(this.#origin)
- super.onResponseError(controller, err)
- break
- default:
- super.onResponseError(controller, err)
- break
- }
- }
-}
-
-module.exports = interceptorOpts => {
- if (
- interceptorOpts?.maxTTL != null &&
- (typeof interceptorOpts?.maxTTL !== 'number' || interceptorOpts?.maxTTL < 0)
- ) {
- throw new InvalidArgumentError('Invalid maxTTL. Must be a positive number')
- }
-
- if (
- interceptorOpts?.maxItems != null &&
- (typeof interceptorOpts?.maxItems !== 'number' ||
- interceptorOpts?.maxItems < 1)
- ) {
- throw new InvalidArgumentError(
- 'Invalid maxItems. Must be a positive number and greater than zero'
- )
- }
-
- if (
- interceptorOpts?.affinity != null &&
- interceptorOpts?.affinity !== 4 &&
- interceptorOpts?.affinity !== 6
- ) {
- throw new InvalidArgumentError('Invalid affinity. Must be either 4 or 6')
- }
-
- if (
- interceptorOpts?.dualStack != null &&
- typeof interceptorOpts?.dualStack !== 'boolean'
- ) {
- throw new InvalidArgumentError('Invalid dualStack. Must be a boolean')
- }
-
- if (
- interceptorOpts?.lookup != null &&
- typeof interceptorOpts?.lookup !== 'function'
- ) {
- throw new InvalidArgumentError('Invalid lookup. Must be a function')
- }
-
- if (
- interceptorOpts?.pick != null &&
- typeof interceptorOpts?.pick !== 'function'
- ) {
- throw new InvalidArgumentError('Invalid pick. Must be a function')
- }
-
- if (
- interceptorOpts?.storage != null &&
- (typeof interceptorOpts?.storage?.get !== 'function' ||
- typeof interceptorOpts?.storage?.set !== 'function' ||
- typeof interceptorOpts?.storage?.full !== 'function' ||
- typeof interceptorOpts?.storage?.delete !== 'function'
- )
- ) {
- throw new InvalidArgumentError('Invalid storage. Must be a object with methods: { get, set, full, delete }')
- }
-
- const dualStack = interceptorOpts?.dualStack ?? true
- let affinity
- if (dualStack) {
- affinity = interceptorOpts?.affinity ?? null
- } else {
- affinity = interceptorOpts?.affinity ?? 4
- }
-
- const opts = {
- maxTTL: interceptorOpts?.maxTTL ?? 10e3, // Expressed in ms
- lookup: interceptorOpts?.lookup ?? null,
- pick: interceptorOpts?.pick ?? null,
- dualStack,
- affinity,
- maxItems: interceptorOpts?.maxItems ?? Infinity,
- storage: interceptorOpts?.storage
- }
-
- const instance = new DNSInstance(opts)
-
- return dispatch => {
- return function dnsInterceptor (origDispatchOpts, handler) {
- const origin =
- origDispatchOpts.origin.constructor === URL
- ? origDispatchOpts.origin
- : new URL(origDispatchOpts.origin)
-
- if (isIP(origin.hostname) !== 0) {
- return dispatch(origDispatchOpts, handler)
- }
-
- instance.runLookup(origin, origDispatchOpts, (err, newOrigin) => {
- if (err) {
- return handler.onResponseError(null, err)
- }
-
- const dispatchOpts = {
- ...origDispatchOpts,
- servername: origin.hostname, // For SNI on TLS
- origin: newOrigin.origin,
- headers: {
- host: origin.host,
- ...origDispatchOpts.headers
- }
- }
-
- dispatch(
- dispatchOpts,
- instance.getHandler(
- { origin, dispatch, handler, newOrigin },
- origDispatchOpts
- )
- )
- })
-
- return true
- }
- }
-}
diff --git a/vanilla/node_modules/undici/lib/interceptor/dump.js b/vanilla/node_modules/undici/lib/interceptor/dump.js
deleted file mode 100644
index 4810a09..0000000
--- a/vanilla/node_modules/undici/lib/interceptor/dump.js
+++ /dev/null
@@ -1,112 +0,0 @@
-'use strict'
-
-const { InvalidArgumentError, RequestAbortedError } = require('../core/errors')
-const DecoratorHandler = require('../handler/decorator-handler')
-
-class DumpHandler extends DecoratorHandler {
- #maxSize = 1024 * 1024
- #dumped = false
- #size = 0
- #controller = null
- aborted = false
- reason = false
-
- constructor ({ maxSize, signal }, handler) {
- if (maxSize != null && (!Number.isFinite(maxSize) || maxSize < 1)) {
- throw new InvalidArgumentError('maxSize must be a number greater than 0')
- }
-
- super(handler)
-
- this.#maxSize = maxSize ?? this.#maxSize
- // this.#handler = handler
- }
-
- #abort (reason) {
- this.aborted = true
- this.reason = reason
- }
-
- onRequestStart (controller, context) {
- controller.abort = this.#abort.bind(this)
- this.#controller = controller
-
- return super.onRequestStart(controller, context)
- }
-
- onResponseStart (controller, statusCode, headers, statusMessage) {
- const contentLength = headers['content-length']
-
- if (contentLength != null && contentLength > this.#maxSize) {
- throw new RequestAbortedError(
- `Response size (${contentLength}) larger than maxSize (${
- this.#maxSize
- })`
- )
- }
-
- if (this.aborted === true) {
- return true
- }
-
- return super.onResponseStart(controller, statusCode, headers, statusMessage)
- }
-
- onResponseError (controller, err) {
- if (this.#dumped) {
- return
- }
-
- // On network errors before connect, controller will be null
- err = this.#controller?.reason ?? err
-
- super.onResponseError(controller, err)
- }
-
- onResponseData (controller, chunk) {
- this.#size = this.#size + chunk.length
-
- if (this.#size >= this.#maxSize) {
- this.#dumped = true
-
- if (this.aborted === true) {
- super.onResponseError(controller, this.reason)
- } else {
- super.onResponseEnd(controller, {})
- }
- }
-
- return true
- }
-
- onResponseEnd (controller, trailers) {
- if (this.#dumped) {
- return
- }
-
- if (this.#controller.aborted === true) {
- super.onResponseError(controller, this.reason)
- return
- }
-
- super.onResponseEnd(controller, trailers)
- }
-}
-
-function createDumpInterceptor (
- { maxSize: defaultMaxSize } = {
- maxSize: 1024 * 1024
- }
-) {
- return dispatch => {
- return function Intercept (opts, handler) {
- const { dumpMaxSize = defaultMaxSize } = opts
-
- const dumpHandler = new DumpHandler({ maxSize: dumpMaxSize, signal: opts.signal }, handler)
-
- return dispatch(opts, dumpHandler)
- }
- }
-}
-
-module.exports = createDumpInterceptor
diff --git a/vanilla/node_modules/undici/lib/interceptor/redirect.js b/vanilla/node_modules/undici/lib/interceptor/redirect.js
deleted file mode 100644
index b7df180..0000000
--- a/vanilla/node_modules/undici/lib/interceptor/redirect.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict'
-
-const RedirectHandler = require('../handler/redirect-handler')
-
-function createRedirectInterceptor ({ maxRedirections: defaultMaxRedirections } = {}) {
- return (dispatch) => {
- return function Intercept (opts, handler) {
- const { maxRedirections = defaultMaxRedirections, ...rest } = opts
-
- if (maxRedirections == null || maxRedirections === 0) {
- return dispatch(opts, handler)
- }
-
- const dispatchOpts = { ...rest } // Stop sub dispatcher from also redirecting.
- const redirectHandler = new RedirectHandler(dispatch, maxRedirections, dispatchOpts, handler)
- return dispatch(dispatchOpts, redirectHandler)
- }
- }
-}
-
-module.exports = createRedirectInterceptor
diff --git a/vanilla/node_modules/undici/lib/interceptor/response-error.js b/vanilla/node_modules/undici/lib/interceptor/response-error.js
deleted file mode 100644
index a8105aa..0000000
--- a/vanilla/node_modules/undici/lib/interceptor/response-error.js
+++ /dev/null
@@ -1,95 +0,0 @@
-'use strict'
-
-// const { parseHeaders } = require('../core/util')
-const DecoratorHandler = require('../handler/decorator-handler')
-const { ResponseError } = require('../core/errors')
-
-class ResponseErrorHandler extends DecoratorHandler {
- #statusCode
- #contentType
- #decoder
- #headers
- #body
-
- constructor (_opts, { handler }) {
- super(handler)
- }
-
- #checkContentType (contentType) {
- return (this.#contentType ?? '').indexOf(contentType) === 0
- }
-
- onRequestStart (controller, context) {
- this.#statusCode = 0
- this.#contentType = null
- this.#decoder = null
- this.#headers = null
- this.#body = ''
-
- return super.onRequestStart(controller, context)
- }
-
- onResponseStart (controller, statusCode, headers, statusMessage) {
- this.#statusCode = statusCode
- this.#headers = headers
- this.#contentType = headers['content-type']
-
- if (this.#statusCode < 400) {
- return super.onResponseStart(controller, statusCode, headers, statusMessage)
- }
-
- if (this.#checkContentType('application/json') || this.#checkContentType('text/plain')) {
- this.#decoder = new TextDecoder('utf-8')
- }
- }
-
- onResponseData (controller, chunk) {
- if (this.#statusCode < 400) {
- return super.onResponseData(controller, chunk)
- }
-
- this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? ''
- }
-
- onResponseEnd (controller, trailers) {
- if (this.#statusCode >= 400) {
- this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? ''
-
- if (this.#checkContentType('application/json')) {
- try {
- this.#body = JSON.parse(this.#body)
- } catch {
- // Do nothing...
- }
- }
-
- let err
- const stackTraceLimit = Error.stackTraceLimit
- Error.stackTraceLimit = 0
- try {
- err = new ResponseError('Response Error', this.#statusCode, {
- body: this.#body,
- headers: this.#headers
- })
- } finally {
- Error.stackTraceLimit = stackTraceLimit
- }
-
- super.onResponseError(controller, err)
- } else {
- super.onResponseEnd(controller, trailers)
- }
- }
-
- onResponseError (controller, err) {
- super.onResponseError(controller, err)
- }
-}
-
-module.exports = () => {
- return (dispatch) => {
- return function Intercept (opts, handler) {
- return dispatch(opts, new ResponseErrorHandler(opts, { handler }))
- }
- }
-}
diff --git a/vanilla/node_modules/undici/lib/interceptor/retry.js b/vanilla/node_modules/undici/lib/interceptor/retry.js
deleted file mode 100644
index 1c16fd8..0000000
--- a/vanilla/node_modules/undici/lib/interceptor/retry.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict'
-const RetryHandler = require('../handler/retry-handler')
-
-module.exports = globalOpts => {
- return dispatch => {
- return function retryInterceptor (opts, handler) {
- return dispatch(
- opts,
- new RetryHandler(
- { ...opts, retryOptions: { ...globalOpts, ...opts.retryOptions } },
- {
- handler,
- dispatch
- }
- )
- )
- }
- }
-}
diff --git a/vanilla/node_modules/undici/lib/llhttp/.gitkeep b/vanilla/node_modules/undici/lib/llhttp/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/vanilla/node_modules/undici/lib/llhttp/.gitkeep
+++ /dev/null
diff --git a/vanilla/node_modules/undici/lib/llhttp/constants.d.ts b/vanilla/node_modules/undici/lib/llhttp/constants.d.ts
deleted file mode 100644
index def2436..0000000
--- a/vanilla/node_modules/undici/lib/llhttp/constants.d.ts
+++ /dev/null
@@ -1,195 +0,0 @@
-export type IntDict = Record<string, number>;
-export declare const ERROR: IntDict;
-export declare const TYPE: IntDict;
-export declare const FLAGS: IntDict;
-export declare const LENIENT_FLAGS: IntDict;
-export declare const METHODS: IntDict;
-export declare const STATUSES: IntDict;
-export declare const FINISH: IntDict;
-export declare const HEADER_STATE: IntDict;
-export declare const METHODS_HTTP: number[];
-export declare const METHODS_ICE: number[];
-export declare const METHODS_RTSP: number[];
-export declare const METHOD_MAP: IntDict;
-export declare const H_METHOD_MAP: {
- [k: string]: number;
-};
-export declare const STATUSES_HTTP: number[];
-export type CharList = (string | number)[];
-export declare const ALPHA: CharList;
-export declare const NUM_MAP: {
- 0: number;
- 1: number;
- 2: number;
- 3: number;
- 4: number;
- 5: number;
- 6: number;
- 7: number;
- 8: number;
- 9: number;
-};
-export declare const HEX_MAP: {
- 0: number;
- 1: number;
- 2: number;
- 3: number;
- 4: number;
- 5: number;
- 6: number;
- 7: number;
- 8: number;
- 9: number;
- A: number;
- B: number;
- C: number;
- D: number;
- E: number;
- F: number;
- a: number;
- b: number;
- c: number;
- d: number;
- e: number;
- f: number;
-};
-export declare const NUM: CharList;
-export declare const ALPHANUM: CharList;
-export declare const MARK: CharList;
-export declare const USERINFO_CHARS: CharList;
-export declare const URL_CHAR: CharList;
-export declare const HEX: CharList;
-export declare const TOKEN: CharList;
-export declare const HEADER_CHARS: CharList;
-export declare const CONNECTION_TOKEN_CHARS: CharList;
-export declare const QUOTED_STRING: CharList;
-export declare const HTAB_SP_VCHAR_OBS_TEXT: CharList;
-export declare const MAJOR: {
- 0: number;
- 1: number;
- 2: number;
- 3: number;
- 4: number;
- 5: number;
- 6: number;
- 7: number;
- 8: number;
- 9: number;
-};
-export declare const MINOR: {
- 0: number;
- 1: number;
- 2: number;
- 3: number;
- 4: number;
- 5: number;
- 6: number;
- 7: number;
- 8: number;
- 9: number;
-};
-export declare const SPECIAL_HEADERS: {
- connection: number;
- 'content-length': number;
- 'proxy-connection': number;
- 'transfer-encoding': number;
- upgrade: number;
-};
-declare const _default: {
- ERROR: IntDict;
- TYPE: IntDict;
- FLAGS: IntDict;
- LENIENT_FLAGS: IntDict;
- METHODS: IntDict;
- STATUSES: IntDict;
- FINISH: IntDict;
- HEADER_STATE: IntDict;
- ALPHA: CharList;
- NUM_MAP: {
- 0: number;
- 1: number;
- 2: number;
- 3: number;
- 4: number;
- 5: number;
- 6: number;
- 7: number;
- 8: number;
- 9: number;
- };
- HEX_MAP: {
- 0: number;
- 1: number;
- 2: number;
- 3: number;
- 4: number;
- 5: number;
- 6: number;
- 7: number;
- 8: number;
- 9: number;
- A: number;
- B: number;
- C: number;
- D: number;
- E: number;
- F: number;
- a: number;
- b: number;
- c: number;
- d: number;
- e: number;
- f: number;
- };
- NUM: CharList;
- ALPHANUM: CharList;
- MARK: CharList;
- USERINFO_CHARS: CharList;
- URL_CHAR: CharList;
- HEX: CharList;
- TOKEN: CharList;
- HEADER_CHARS: CharList;
- CONNECTION_TOKEN_CHARS: CharList;
- QUOTED_STRING: CharList;
- HTAB_SP_VCHAR_OBS_TEXT: CharList;
- MAJOR: {
- 0: number;
- 1: number;
- 2: number;
- 3: number;
- 4: number;
- 5: number;
- 6: number;
- 7: number;
- 8: number;
- 9: number;
- };
- MINOR: {
- 0: number;
- 1: number;
- 2: number;
- 3: number;
- 4: number;
- 5: number;
- 6: number;
- 7: number;
- 8: number;
- 9: number;
- };
- SPECIAL_HEADERS: {
- connection: number;
- 'content-length': number;
- 'proxy-connection': number;
- 'transfer-encoding': number;
- upgrade: number;
- };
- METHODS_HTTP: number[];
- METHODS_ICE: number[];
- METHODS_RTSP: number[];
- METHOD_MAP: IntDict;
- H_METHOD_MAP: {
- [k: string]: number;
- };
- STATUSES_HTTP: number[];
-};
-export default _default;
diff --git a/vanilla/node_modules/undici/lib/llhttp/constants.js b/vanilla/node_modules/undici/lib/llhttp/constants.js
deleted file mode 100644
index 8b88dfd..0000000
--- a/vanilla/node_modules/undici/lib/llhttp/constants.js
+++ /dev/null
@@ -1,531 +0,0 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-exports.SPECIAL_HEADERS = exports.MINOR = exports.MAJOR = exports.HTAB_SP_VCHAR_OBS_TEXT = exports.QUOTED_STRING = exports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS = exports.TOKEN = exports.HEX = exports.URL_CHAR = exports.USERINFO_CHARS = exports.MARK = exports.ALPHANUM = exports.NUM = exports.HEX_MAP = exports.NUM_MAP = exports.ALPHA = exports.STATUSES_HTTP = exports.H_METHOD_MAP = exports.METHOD_MAP = exports.METHODS_RTSP = exports.METHODS_ICE = exports.METHODS_HTTP = exports.HEADER_STATE = exports.FINISH = exports.STATUSES = exports.METHODS = exports.LENIENT_FLAGS = exports.FLAGS = exports.TYPE = exports.ERROR = void 0;
-const utils_1 = require("./utils");
-// Emums
-exports.ERROR = {
- OK: 0,
- INTERNAL: 1,
- STRICT: 2,
- CR_EXPECTED: 25,
- LF_EXPECTED: 3,
- UNEXPECTED_CONTENT_LENGTH: 4,
- UNEXPECTED_SPACE: 30,
- CLOSED_CONNECTION: 5,
- INVALID_METHOD: 6,
- INVALID_URL: 7,
- INVALID_CONSTANT: 8,
- INVALID_VERSION: 9,
- INVALID_HEADER_TOKEN: 10,
- INVALID_CONTENT_LENGTH: 11,
- INVALID_CHUNK_SIZE: 12,
- INVALID_STATUS: 13,
- INVALID_EOF_STATE: 14,
- INVALID_TRANSFER_ENCODING: 15,
- CB_MESSAGE_BEGIN: 16,
- CB_HEADERS_COMPLETE: 17,
- CB_MESSAGE_COMPLETE: 18,
- CB_CHUNK_HEADER: 19,
- CB_CHUNK_COMPLETE: 20,
- PAUSED: 21,
- PAUSED_UPGRADE: 22,
- PAUSED_H2_UPGRADE: 23,
- USER: 24,
- CB_URL_COMPLETE: 26,
- CB_STATUS_COMPLETE: 27,
- CB_METHOD_COMPLETE: 32,
- CB_VERSION_COMPLETE: 33,
- CB_HEADER_FIELD_COMPLETE: 28,
- CB_HEADER_VALUE_COMPLETE: 29,
- CB_CHUNK_EXTENSION_NAME_COMPLETE: 34,
- CB_CHUNK_EXTENSION_VALUE_COMPLETE: 35,
- CB_RESET: 31,
- CB_PROTOCOL_COMPLETE: 38,
-};
-exports.TYPE = {
- BOTH: 0, // default
- REQUEST: 1,
- RESPONSE: 2,
-};
-exports.FLAGS = {
- CONNECTION_KEEP_ALIVE: 1 << 0,
- CONNECTION_CLOSE: 1 << 1,
- CONNECTION_UPGRADE: 1 << 2,
- CHUNKED: 1 << 3,
- UPGRADE: 1 << 4,
- CONTENT_LENGTH: 1 << 5,
- SKIPBODY: 1 << 6,
- TRAILING: 1 << 7,
- // 1 << 8 is unused
- TRANSFER_ENCODING: 1 << 9,
-};
-exports.LENIENT_FLAGS = {
- HEADERS: 1 << 0,
- CHUNKED_LENGTH: 1 << 1,
- KEEP_ALIVE: 1 << 2,
- TRANSFER_ENCODING: 1 << 3,
- VERSION: 1 << 4,
- DATA_AFTER_CLOSE: 1 << 5,
- OPTIONAL_LF_AFTER_CR: 1 << 6,
- OPTIONAL_CRLF_AFTER_CHUNK: 1 << 7,
- OPTIONAL_CR_BEFORE_LF: 1 << 8,
- SPACES_AFTER_CHUNK_SIZE: 1 << 9,
-};
-exports.METHODS = {
- 'DELETE': 0,
- 'GET': 1,
- 'HEAD': 2,
- 'POST': 3,
- 'PUT': 4,
- /* pathological */
- 'CONNECT': 5,
- 'OPTIONS': 6,
- 'TRACE': 7,
- /* WebDAV */
- 'COPY': 8,
- 'LOCK': 9,
- 'MKCOL': 10,
- 'MOVE': 11,
- 'PROPFIND': 12,
- 'PROPPATCH': 13,
- 'SEARCH': 14,
- 'UNLOCK': 15,
- 'BIND': 16,
- 'REBIND': 17,
- 'UNBIND': 18,
- 'ACL': 19,
- /* subversion */
- 'REPORT': 20,
- 'MKACTIVITY': 21,
- 'CHECKOUT': 22,
- 'MERGE': 23,
- /* upnp */
- 'M-SEARCH': 24,
- 'NOTIFY': 25,
- 'SUBSCRIBE': 26,
- 'UNSUBSCRIBE': 27,
- /* RFC-5789 */
- 'PATCH': 28,
- 'PURGE': 29,
- /* CalDAV */
- 'MKCALENDAR': 30,
- /* RFC-2068, section 19.6.1.2 */
- 'LINK': 31,
- 'UNLINK': 32,
- /* icecast */
- 'SOURCE': 33,
- /* RFC-7540, section 11.6 */
- 'PRI': 34,
- /* RFC-2326 RTSP */
- 'DESCRIBE': 35,
- 'ANNOUNCE': 36,
- 'SETUP': 37,
- 'PLAY': 38,
- 'PAUSE': 39,
- 'TEARDOWN': 40,
- 'GET_PARAMETER': 41,
- 'SET_PARAMETER': 42,
- 'REDIRECT': 43,
- 'RECORD': 44,
- /* RAOP */
- 'FLUSH': 45,
- /* DRAFT https://www.ietf.org/archive/id/draft-ietf-httpbis-safe-method-w-body-02.html */
- 'QUERY': 46,
-};
-exports.STATUSES = {
- CONTINUE: 100,
- SWITCHING_PROTOCOLS: 101,
- PROCESSING: 102,
- EARLY_HINTS: 103,
- RESPONSE_IS_STALE: 110, // Unofficial
- REVALIDATION_FAILED: 111, // Unofficial
- DISCONNECTED_OPERATION: 112, // Unofficial
- HEURISTIC_EXPIRATION: 113, // Unofficial
- MISCELLANEOUS_WARNING: 199, // Unofficial
- OK: 200,
- CREATED: 201,
- ACCEPTED: 202,
- NON_AUTHORITATIVE_INFORMATION: 203,
- NO_CONTENT: 204,
- RESET_CONTENT: 205,
- PARTIAL_CONTENT: 206,
- MULTI_STATUS: 207,
- ALREADY_REPORTED: 208,
- TRANSFORMATION_APPLIED: 214, // Unofficial
- IM_USED: 226,
- MISCELLANEOUS_PERSISTENT_WARNING: 299, // Unofficial
- MULTIPLE_CHOICES: 300,
- MOVED_PERMANENTLY: 301,
- FOUND: 302,
- SEE_OTHER: 303,
- NOT_MODIFIED: 304,
- USE_PROXY: 305,
- SWITCH_PROXY: 306, // No longer used
- TEMPORARY_REDIRECT: 307,
- PERMANENT_REDIRECT: 308,
- BAD_REQUEST: 400,
- UNAUTHORIZED: 401,
- PAYMENT_REQUIRED: 402,
- FORBIDDEN: 403,
- NOT_FOUND: 404,
- METHOD_NOT_ALLOWED: 405,
- NOT_ACCEPTABLE: 406,
- PROXY_AUTHENTICATION_REQUIRED: 407,
- REQUEST_TIMEOUT: 408,
- CONFLICT: 409,
- GONE: 410,
- LENGTH_REQUIRED: 411,
- PRECONDITION_FAILED: 412,
- PAYLOAD_TOO_LARGE: 413,
- URI_TOO_LONG: 414,
- UNSUPPORTED_MEDIA_TYPE: 415,
- RANGE_NOT_SATISFIABLE: 416,
- EXPECTATION_FAILED: 417,
- IM_A_TEAPOT: 418,
- PAGE_EXPIRED: 419, // Unofficial
- ENHANCE_YOUR_CALM: 420, // Unofficial
- MISDIRECTED_REQUEST: 421,
- UNPROCESSABLE_ENTITY: 422,
- LOCKED: 423,
- FAILED_DEPENDENCY: 424,
- TOO_EARLY: 425,
- UPGRADE_REQUIRED: 426,
- PRECONDITION_REQUIRED: 428,
- TOO_MANY_REQUESTS: 429,
- REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL: 430, // Unofficial
- REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
- LOGIN_TIMEOUT: 440, // Unofficial
- NO_RESPONSE: 444, // Unofficial
- RETRY_WITH: 449, // Unofficial
- BLOCKED_BY_PARENTAL_CONTROL: 450, // Unofficial
- UNAVAILABLE_FOR_LEGAL_REASONS: 451,
- CLIENT_CLOSED_LOAD_BALANCED_REQUEST: 460, // Unofficial
- INVALID_X_FORWARDED_FOR: 463, // Unofficial
- REQUEST_HEADER_TOO_LARGE: 494, // Unofficial
- SSL_CERTIFICATE_ERROR: 495, // Unofficial
- SSL_CERTIFICATE_REQUIRED: 496, // Unofficial
- HTTP_REQUEST_SENT_TO_HTTPS_PORT: 497, // Unofficial
- INVALID_TOKEN: 498, // Unofficial
- CLIENT_CLOSED_REQUEST: 499, // Unofficial
- INTERNAL_SERVER_ERROR: 500,
- NOT_IMPLEMENTED: 501,
- BAD_GATEWAY: 502,
- SERVICE_UNAVAILABLE: 503,
- GATEWAY_TIMEOUT: 504,
- HTTP_VERSION_NOT_SUPPORTED: 505,
- VARIANT_ALSO_NEGOTIATES: 506,
- INSUFFICIENT_STORAGE: 507,
- LOOP_DETECTED: 508,
- BANDWIDTH_LIMIT_EXCEEDED: 509,
- NOT_EXTENDED: 510,
- NETWORK_AUTHENTICATION_REQUIRED: 511,
- WEB_SERVER_UNKNOWN_ERROR: 520, // Unofficial
- WEB_SERVER_IS_DOWN: 521, // Unofficial
- CONNECTION_TIMEOUT: 522, // Unofficial
- ORIGIN_IS_UNREACHABLE: 523, // Unofficial
- TIMEOUT_OCCURED: 524, // Unofficial
- SSL_HANDSHAKE_FAILED: 525, // Unofficial
- INVALID_SSL_CERTIFICATE: 526, // Unofficial
- RAILGUN_ERROR: 527, // Unofficial
- SITE_IS_OVERLOADED: 529, // Unofficial
- SITE_IS_FROZEN: 530, // Unofficial
- IDENTITY_PROVIDER_AUTHENTICATION_ERROR: 561, // Unofficial
- NETWORK_READ_TIMEOUT: 598, // Unofficial
- NETWORK_CONNECT_TIMEOUT: 599, // Unofficial
-};
-exports.FINISH = {
- SAFE: 0,
- SAFE_WITH_CB: 1,
- UNSAFE: 2,
-};
-exports.HEADER_STATE = {
- GENERAL: 0,
- CONNECTION: 1,
- CONTENT_LENGTH: 2,
- TRANSFER_ENCODING: 3,
- UPGRADE: 4,
- CONNECTION_KEEP_ALIVE: 5,
- CONNECTION_CLOSE: 6,
- CONNECTION_UPGRADE: 7,
- TRANSFER_ENCODING_CHUNKED: 8,
-};
-// C headers
-exports.METHODS_HTTP = [
- exports.METHODS.DELETE,
- exports.METHODS.GET,
- exports.METHODS.HEAD,
- exports.METHODS.POST,
- exports.METHODS.PUT,
- exports.METHODS.CONNECT,
- exports.METHODS.OPTIONS,
- exports.METHODS.TRACE,
- exports.METHODS.COPY,
- exports.METHODS.LOCK,
- exports.METHODS.MKCOL,
- exports.METHODS.MOVE,
- exports.METHODS.PROPFIND,
- exports.METHODS.PROPPATCH,
- exports.METHODS.SEARCH,
- exports.METHODS.UNLOCK,
- exports.METHODS.BIND,
- exports.METHODS.REBIND,
- exports.METHODS.UNBIND,
- exports.METHODS.ACL,
- exports.METHODS.REPORT,
- exports.METHODS.MKACTIVITY,
- exports.METHODS.CHECKOUT,
- exports.METHODS.MERGE,
- exports.METHODS['M-SEARCH'],
- exports.METHODS.NOTIFY,
- exports.METHODS.SUBSCRIBE,
- exports.METHODS.UNSUBSCRIBE,
- exports.METHODS.PATCH,
- exports.METHODS.PURGE,
- exports.METHODS.MKCALENDAR,
- exports.METHODS.LINK,
- exports.METHODS.UNLINK,
- exports.METHODS.PRI,
- // TODO(indutny): should we allow it with HTTP?
- exports.METHODS.SOURCE,
- exports.METHODS.QUERY,
-];
-exports.METHODS_ICE = [
- exports.METHODS.SOURCE,
-];
-exports.METHODS_RTSP = [
- exports.METHODS.OPTIONS,
- exports.METHODS.DESCRIBE,
- exports.METHODS.ANNOUNCE,
- exports.METHODS.SETUP,
- exports.METHODS.PLAY,
- exports.METHODS.PAUSE,
- exports.METHODS.TEARDOWN,
- exports.METHODS.GET_PARAMETER,
- exports.METHODS.SET_PARAMETER,
- exports.METHODS.REDIRECT,
- exports.METHODS.RECORD,
- exports.METHODS.FLUSH,
- // For AirPlay
- exports.METHODS.GET,
- exports.METHODS.POST,
-];
-exports.METHOD_MAP = (0, utils_1.enumToMap)(exports.METHODS);
-exports.H_METHOD_MAP = Object.fromEntries(Object.entries(exports.METHODS).filter(([k]) => k.startsWith('H')));
-exports.STATUSES_HTTP = [
- exports.STATUSES.CONTINUE,
- exports.STATUSES.SWITCHING_PROTOCOLS,
- exports.STATUSES.PROCESSING,
- exports.STATUSES.EARLY_HINTS,
- exports.STATUSES.RESPONSE_IS_STALE,
- exports.STATUSES.REVALIDATION_FAILED,
- exports.STATUSES.DISCONNECTED_OPERATION,
- exports.STATUSES.HEURISTIC_EXPIRATION,
- exports.STATUSES.MISCELLANEOUS_WARNING,
- exports.STATUSES.OK,
- exports.STATUSES.CREATED,
- exports.STATUSES.ACCEPTED,
- exports.STATUSES.NON_AUTHORITATIVE_INFORMATION,
- exports.STATUSES.NO_CONTENT,
- exports.STATUSES.RESET_CONTENT,
- exports.STATUSES.PARTIAL_CONTENT,
- exports.STATUSES.MULTI_STATUS,
- exports.STATUSES.ALREADY_REPORTED,
- exports.STATUSES.TRANSFORMATION_APPLIED,
- exports.STATUSES.IM_USED,
- exports.STATUSES.MISCELLANEOUS_PERSISTENT_WARNING,
- exports.STATUSES.MULTIPLE_CHOICES,
- exports.STATUSES.MOVED_PERMANENTLY,
- exports.STATUSES.FOUND,
- exports.STATUSES.SEE_OTHER,
- exports.STATUSES.NOT_MODIFIED,
- exports.STATUSES.USE_PROXY,
- exports.STATUSES.SWITCH_PROXY,
- exports.STATUSES.TEMPORARY_REDIRECT,
- exports.STATUSES.PERMANENT_REDIRECT,
- exports.STATUSES.BAD_REQUEST,
- exports.STATUSES.UNAUTHORIZED,
- exports.STATUSES.PAYMENT_REQUIRED,
- exports.STATUSES.FORBIDDEN,
- exports.STATUSES.NOT_FOUND,
- exports.STATUSES.METHOD_NOT_ALLOWED,
- exports.STATUSES.NOT_ACCEPTABLE,
- exports.STATUSES.PROXY_AUTHENTICATION_REQUIRED,
- exports.STATUSES.REQUEST_TIMEOUT,
- exports.STATUSES.CONFLICT,
- exports.STATUSES.GONE,
- exports.STATUSES.LENGTH_REQUIRED,
- exports.STATUSES.PRECONDITION_FAILED,
- exports.STATUSES.PAYLOAD_TOO_LARGE,
- exports.STATUSES.URI_TOO_LONG,
- exports.STATUSES.UNSUPPORTED_MEDIA_TYPE,
- exports.STATUSES.RANGE_NOT_SATISFIABLE,
- exports.STATUSES.EXPECTATION_FAILED,
- exports.STATUSES.IM_A_TEAPOT,
- exports.STATUSES.PAGE_EXPIRED,
- exports.STATUSES.ENHANCE_YOUR_CALM,
- exports.STATUSES.MISDIRECTED_REQUEST,
- exports.STATUSES.UNPROCESSABLE_ENTITY,
- exports.STATUSES.LOCKED,
- exports.STATUSES.FAILED_DEPENDENCY,
- exports.STATUSES.TOO_EARLY,
- exports.STATUSES.UPGRADE_REQUIRED,
- exports.STATUSES.PRECONDITION_REQUIRED,
- exports.STATUSES.TOO_MANY_REQUESTS,
- exports.STATUSES.REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL,
- exports.STATUSES.REQUEST_HEADER_FIELDS_TOO_LARGE,
- exports.STATUSES.LOGIN_TIMEOUT,
- exports.STATUSES.NO_RESPONSE,
- exports.STATUSES.RETRY_WITH,
- exports.STATUSES.BLOCKED_BY_PARENTAL_CONTROL,
- exports.STATUSES.UNAVAILABLE_FOR_LEGAL_REASONS,
- exports.STATUSES.CLIENT_CLOSED_LOAD_BALANCED_REQUEST,
- exports.STATUSES.INVALID_X_FORWARDED_FOR,
- exports.STATUSES.REQUEST_HEADER_TOO_LARGE,
- exports.STATUSES.SSL_CERTIFICATE_ERROR,
- exports.STATUSES.SSL_CERTIFICATE_REQUIRED,
- exports.STATUSES.HTTP_REQUEST_SENT_TO_HTTPS_PORT,
- exports.STATUSES.INVALID_TOKEN,
- exports.STATUSES.CLIENT_CLOSED_REQUEST,
- exports.STATUSES.INTERNAL_SERVER_ERROR,
- exports.STATUSES.NOT_IMPLEMENTED,
- exports.STATUSES.BAD_GATEWAY,
- exports.STATUSES.SERVICE_UNAVAILABLE,
- exports.STATUSES.GATEWAY_TIMEOUT,
- exports.STATUSES.HTTP_VERSION_NOT_SUPPORTED,
- exports.STATUSES.VARIANT_ALSO_NEGOTIATES,
- exports.STATUSES.INSUFFICIENT_STORAGE,
- exports.STATUSES.LOOP_DETECTED,
- exports.STATUSES.BANDWIDTH_LIMIT_EXCEEDED,
- exports.STATUSES.NOT_EXTENDED,
- exports.STATUSES.NETWORK_AUTHENTICATION_REQUIRED,
- exports.STATUSES.WEB_SERVER_UNKNOWN_ERROR,
- exports.STATUSES.WEB_SERVER_IS_DOWN,
- exports.STATUSES.CONNECTION_TIMEOUT,
- exports.STATUSES.ORIGIN_IS_UNREACHABLE,
- exports.STATUSES.TIMEOUT_OCCURED,
- exports.STATUSES.SSL_HANDSHAKE_FAILED,
- exports.STATUSES.INVALID_SSL_CERTIFICATE,
- exports.STATUSES.RAILGUN_ERROR,
- exports.STATUSES.SITE_IS_OVERLOADED,
- exports.STATUSES.SITE_IS_FROZEN,
- exports.STATUSES.IDENTITY_PROVIDER_AUTHENTICATION_ERROR,
- exports.STATUSES.NETWORK_READ_TIMEOUT,
- exports.STATUSES.NETWORK_CONNECT_TIMEOUT,
-];
-exports.ALPHA = [];
-for (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); i++) {
- // Upper case
- exports.ALPHA.push(String.fromCharCode(i));
- // Lower case
- exports.ALPHA.push(String.fromCharCode(i + 0x20));
-}
-exports.NUM_MAP = {
- 0: 0, 1: 1, 2: 2, 3: 3, 4: 4,
- 5: 5, 6: 6, 7: 7, 8: 8, 9: 9,
-};
-exports.HEX_MAP = {
- 0: 0, 1: 1, 2: 2, 3: 3, 4: 4,
- 5: 5, 6: 6, 7: 7, 8: 8, 9: 9,
- A: 0XA, B: 0XB, C: 0XC, D: 0XD, E: 0XE, F: 0XF,
- a: 0xa, b: 0xb, c: 0xc, d: 0xd, e: 0xe, f: 0xf,
-};
-exports.NUM = [
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
-];
-exports.ALPHANUM = exports.ALPHA.concat(exports.NUM);
-exports.MARK = ['-', '_', '.', '!', '~', '*', '\'', '(', ')'];
-exports.USERINFO_CHARS = exports.ALPHANUM
- .concat(exports.MARK)
- .concat(['%', ';', ':', '&', '=', '+', '$', ',']);
-// TODO(indutny): use RFC
-exports.URL_CHAR = [
- '!', '"', '$', '%', '&', '\'',
- '(', ')', '*', '+', ',', '-', '.', '/',
- ':', ';', '<', '=', '>',
- '@', '[', '\\', ']', '^', '_',
- '`',
- '{', '|', '}', '~',
-].concat(exports.ALPHANUM);
-exports.HEX = exports.NUM.concat(['a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F']);
-/* Tokens as defined by rfc 2616. Also lowercases them.
- * token = 1*<any CHAR except CTLs or separators>
- * separators = "(" | ")" | "<" | ">" | "@"
- * | "," | ";" | ":" | "\" | <">
- * | "/" | "[" | "]" | "?" | "="
- * | "{" | "}" | SP | HT
- */
-exports.TOKEN = [
- '!', '#', '$', '%', '&', '\'',
- '*', '+', '-', '.',
- '^', '_', '`',
- '|', '~',
-].concat(exports.ALPHANUM);
-/*
- * Verify that a char is a valid visible (printable) US-ASCII
- * character or %x80-FF
- */
-exports.HEADER_CHARS = ['\t'];
-for (let i = 32; i <= 255; i++) {
- if (i !== 127) {
- exports.HEADER_CHARS.push(i);
- }
-}
-// ',' = \x44
-exports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS.filter((c) => c !== 44);
-exports.QUOTED_STRING = ['\t', ' '];
-for (let i = 0x21; i <= 0xff; i++) {
- if (i !== 0x22 && i !== 0x5c) { // All characters in ASCII except \ and "
- exports.QUOTED_STRING.push(i);
- }
-}
-exports.HTAB_SP_VCHAR_OBS_TEXT = ['\t', ' '];
-// VCHAR: https://tools.ietf.org/html/rfc5234#appendix-B.1
-for (let i = 0x21; i <= 0x7E; i++) {
- exports.HTAB_SP_VCHAR_OBS_TEXT.push(i);
-}
-// OBS_TEXT: https://datatracker.ietf.org/doc/html/rfc9110#name-collected-abnf
-for (let i = 0x80; i <= 0xff; i++) {
- exports.HTAB_SP_VCHAR_OBS_TEXT.push(i);
-}
-exports.MAJOR = exports.NUM_MAP;
-exports.MINOR = exports.MAJOR;
-exports.SPECIAL_HEADERS = {
- 'connection': exports.HEADER_STATE.CONNECTION,
- 'content-length': exports.HEADER_STATE.CONTENT_LENGTH,
- 'proxy-connection': exports.HEADER_STATE.CONNECTION,
- 'transfer-encoding': exports.HEADER_STATE.TRANSFER_ENCODING,
- 'upgrade': exports.HEADER_STATE.UPGRADE,
-};
-exports.default = {
- ERROR: exports.ERROR,
- TYPE: exports.TYPE,
- FLAGS: exports.FLAGS,
- LENIENT_FLAGS: exports.LENIENT_FLAGS,
- METHODS: exports.METHODS,
- STATUSES: exports.STATUSES,
- FINISH: exports.FINISH,
- HEADER_STATE: exports.HEADER_STATE,
- ALPHA: exports.ALPHA,
- NUM_MAP: exports.NUM_MAP,
- HEX_MAP: exports.HEX_MAP,
- NUM: exports.NUM,
- ALPHANUM: exports.ALPHANUM,
- MARK: exports.MARK,
- USERINFO_CHARS: exports.USERINFO_CHARS,
- URL_CHAR: exports.URL_CHAR,
- HEX: exports.HEX,
- TOKEN: exports.TOKEN,
- HEADER_CHARS: exports.HEADER_CHARS,
- CONNECTION_TOKEN_CHARS: exports.CONNECTION_TOKEN_CHARS,
- QUOTED_STRING: exports.QUOTED_STRING,
- HTAB_SP_VCHAR_OBS_TEXT: exports.HTAB_SP_VCHAR_OBS_TEXT,
- MAJOR: exports.MAJOR,
- MINOR: exports.MINOR,
- SPECIAL_HEADERS: exports.SPECIAL_HEADERS,
- METHODS_HTTP: exports.METHODS_HTTP,
- METHODS_ICE: exports.METHODS_ICE,
- METHODS_RTSP: exports.METHODS_RTSP,
- METHOD_MAP: exports.METHOD_MAP,
- H_METHOD_MAP: exports.H_METHOD_MAP,
- STATUSES_HTTP: exports.STATUSES_HTTP,
-};
diff --git a/vanilla/node_modules/undici/lib/llhttp/llhttp-wasm.js b/vanilla/node_modules/undici/lib/llhttp/llhttp-wasm.js
deleted file mode 100644
index 8e89806..0000000
--- a/vanilla/node_modules/undici/lib/llhttp/llhttp-wasm.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict'
-
-const { Buffer } = require('node:buffer')
-
-const wasmBase64 = 'AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAn9/AGABfwBgBH9/f38Bf2AAAGADf39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQAEA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAAzU0BQYAAAMAAAAAAAADAQMAAwMDAAACAAAAAAICAgICAgICAgIBAQEBAQEBAQEBAwAAAwAAAAQFAXABExMFAwEAAgYIAX8BQcDZBAsHxQcoBm1lbW9yeQIAC19pbml0aWFsaXplAAgZX19pbmRpcmVjdF9mdW5jdGlvbl90YWJsZQEAC2xsaHR0cF9pbml0AAkYbGxodHRwX3Nob3VsZF9rZWVwX2FsaXZlADcMbGxodHRwX2FsbG9jAAsGbWFsbG9jADkLbGxodHRwX2ZyZWUADARmcmVlAAwPbGxodHRwX2dldF90eXBlAA0VbGxodHRwX2dldF9odHRwX21ham9yAA4VbGxodHRwX2dldF9odHRwX21pbm9yAA8RbGxodHRwX2dldF9tZXRob2QAEBZsbGh0dHBfZ2V0X3N0YXR1c19jb2RlABESbGxodHRwX2dldF91cGdyYWRlABIMbGxodHRwX3Jlc2V0ABMObGxodHRwX2V4ZWN1dGUAFBRsbGh0dHBfc2V0dGluZ3NfaW5pdAAVDWxsaHR0cF9maW5pc2gAFgxsbGh0dHBfcGF1c2UAFw1sbGh0dHBfcmVzdW1lABgbbGxodHRwX3Jlc3VtZV9hZnRlcl91cGdyYWRlABkQbGxodHRwX2dldF9lcnJubwAaF2xsaHR0cF9nZXRfZXJyb3JfcmVhc29uABsXbGxodHRwX3NldF9lcnJvcl9yZWFzb24AHBRsbGh0dHBfZ2V0X2Vycm9yX3BvcwAdEWxsaHR0cF9lcnJub19uYW1lAB4SbGxodHRwX21ldGhvZF9uYW1lAB8SbGxodHRwX3N0YXR1c19uYW1lACAabGxodHRw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AXRrQT5qIQILIAEgAjYCHCABQgA3AhAgAkECdEH01wBqIQACQEHI1QAoAgAiA0EBIAJ0IgdxRQRAIAAgATYCAEHI1QAgAyAHcjYCACABIAA2AhggASABNgIIIAEgATYCDAwBCyAEQRkgAkEBdmtBACACQR9HG3QhAiAAKAIAIQACQANAIAAiAygCBEF4cSAERg0BIAJBHXYhACACQQF0IQIgAyAAQQRxakEQaiIHKAIAIgANAAsgByABNgIAIAEgAzYCGCABIAE2AgwgASABNgIIDAELIAMoAggiACABNgIMIAMgATYCCCABQQA2AhggASADNgIMIAEgADYCCAtB5NUAQeTVACgCAEEBayIAQX8gABs2AgALCwcAIAAtACgLBwAgAC0AKgsHACAALQArCwcAIAAtACkLBwAgAC8BNAsHACAALQAwC0ABBH8gACgCGCEBIAAvAS4hAiAALQAoIQMgACgCOCEEIAAQOCAAIAQ2AjggACADOgAoIAAgAjsBLiAAIAE2AhgL5YUCAgd/A34gASACaiEEAkAgACIDKAIMIgANACADKAIEBEAgAyABNgIECyMAQRBrIgkkAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAn8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAygCHCICQQJrDvwBAfkBAgMEBQYHCAkKCwwNDg8QERL4ARP3ARQV9gEWF/UBGBkaGxwdHh8g/QH7ASH0ASIjJCUmJygpKivzASwtLi8wMTLyAfEBMzTwAe8BNTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5P+gFQUVJT7gHtAVTsAVXrAVZXWFla6gFbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gAGBAYIBgwGEAYUBhgGHAYgBiQGKAYsBjAGNAY4BjwGQAZEBkgGTAZQBlQGWAZcBmAGZAZoBmwGcAZ0BngGfAaABoQGiAaMBpAGlAaYBpwGoAakBqgGrAawBrQGuAa8BsAGxAbIBswG0AbUBtgG3AbgBuQG6AbsBvAG9Ab4BvwHAAcEBwgHDAcQBxQHGAccByAHJAcoBywHMAc0BzgHpAegBzwHnAdAB5gHRAdIB0wHUAeUB1QHWAdcB2AHZAdoB2wHcAd0B3gHfAeAB4QHiAeMBAPwBC0EADOMBC0EODOIBC0ENDOEBC0EPDOABC0EQDN8BC0ETDN4BC0EUDN0BC0EVDNwBC0EWDNsBC0EXDNoBC0EYDNkBC0EZDNgBC0EaDNcBC0EbDNYBC0EcDNUBC0EdDNQBC0EeDNMBC0EfDNIBC0EgDNEBC0EhDNABC0EIDM8BC0EiDM4BC0EkDM0BC0EjDMwBC0EHDMsBC0ElDMoBC0EmDMkBC0EnDMgBC0EoDMcBC0ESDMYBC0ERDMUBC0EpDMQBC0EqDMMBC0ErDMIBC0EsDMEBC0HeAQzAAQtBLgy/AQtBLwy+AQtBMAy9AQtBMQy8AQtBMgy7AQtBMwy6AQtBNAy5AQtB3wEMuAELQTUMtwELQTkMtgELQQwMtQELQTYMtAELQTcMswELQTgMsgELQT4MsQELQToMsAELQeABDK8BC0ELDK4BC0E/DK0BC0E7DKwBC0EKDKsBC0E8DKoBC0E9DKkBC0HhAQyoAQtBwQAMpwELQcAADKYBC0HCAAylAQtBCQykAQtBLQyjAQtBwwAMogELQcQADKEBC0HFAAygAQtBxgAMnwELQccADJ4BC0HIAAydAQtByQAMnAELQcoADJsBC0HLAAyaAQtBzAAMmQELQc0ADJgBC0HOAAyXAQtBzwAMlgELQdAADJUBC0HRAAyUAQtB0gAMkwELQdMADJIBC0HVAAyRAQtB1AAMkAELQdYADI8BC0HXAAyOAQtB2AAMjQELQdkADIwBC0HaAAyLAQtB2wAMigELQdwADIkBC0HdAAyIAQtB3gAMhwELQd8ADIYBC0HgAAyFAQtB4QAMhAELQeIADIMBC0HjAAyCAQtB5AAMgQELQeUADIABC0HiAQx/C0HmAAx+C0HnAAx9C0EGDHwLQegADHsLQQUMegtB6QAMeQtBBAx4C0HqAAx3C0HrAAx2C0HsAAx1C0HtAAx0C0EDDHMLQe4ADHILQe8ADHELQfAADHALQfIADG8LQfEADG4LQfMADG0LQfQADGwLQfUADGsLQfYADGoLQQIMaQtB9wAMaAtB+AAMZwtB+QAMZgtB+gAMZQtB+wAMZAtB/AAMYwtB/QAMYgtB/gAMYQtB/wAMYAtBgAEMXwtBgQEMXgtBggEMXQtBgwEMXAtBhAEMWwtBhQEMWgtBhgEMWQtBhwEMWAtBiAEMVwtBiQEMVgtBigEMVQtBiwEMVAtBjAEMUwtBjQEMUgtBjgEMUQtBjwEMUAtBkAEMTwtBkQEMTgtBkgEMTQtBkwEMTAtBlAEMSwtBlQEMSgtBlgEMSQtBlwEMSAtBmAEMRwtBmQEMRgtBmgEMRQtBmwEMRAtBnAEMQwtBnQEMQgtBngEMQQtBnwEMQAtBoAEMPwtBoQEMPgtBogEMPQtBowEMPAtBpAEMOwtBpQEMOgtBpgEMOQtBpwEMOAtBqAEMNwtBqQEMNgtBqgEMNQtBqwEMNAtBrAEMMwtBrQEMMgtBrgEMMQtBrwEMMAtBsAEMLwtBsQEMLgtBsgEMLQtBswEMLAtBtAEMKwtBtQEMKgtBtgEMKQtBtwEMKAtBuAEMJwtBuQEMJgtBugEMJQtBuwEMJAtBvAEMIwtBvQEMIgtBvgEMIQtBvwEMIAtBwAEMHwtBwQEMHgtBwgEMHQtBAQwcC0HDAQwbC0HEAQwaC0HFAQwZC0HGAQwYC0HHAQwXC0HIAQwWC0HJAQwVC0HKAQwUC0HLAQwTC0HMAQwSC0HNAQwRC0HOAQwQC0HPAQwPC0HQAQwOC0HRAQwNC0HSAQwMC0HTAQwLC0HUAQwKC0HVAQwJC0HWAQwIC0HjAQwHC0HXAQwGC0HYAQwFC0HZAQwEC0HaAQwDC0HbAQwCC0HdAQwBC0HcAQshAgNAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCADAn8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAn8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAn8CQAJAAkACQAJAAkACQAJ/AkACQAJAAn8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAMCfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAg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ogKiAqICogKiAqICogKiAqICogKiAqICogIAogILIAQgAUEBaiIBRw0AC0HJACECDOwCCyABQQFqIQEgA0Evai0AAEEBcQ2lAiADQQA2AhwgAyABNgIUIANBlxA2AhAgA0EKNgIMQQAhAgzrAgsgASAERwRAA0AgAS0AAEEgRw0VIAQgAUEBaiIBRw0AC0H4ACECDOsCC0H4ACECDOoCCyADQQI6ACgMOAtBACECIANBADYCHCADQb8LNgIQIANBAjYCDCADIAFBAWo2AhQM6AILQQAhAgzOAgtBDSECDM0CC0ETIQIMzAILQRUhAgzLAgtBFiECDMoCC0EYIQIMyQILQRkhAgzIAgtBGiECDMcCC0EbIQIMxgILQRwhAgzFAgtBHSECDMQCC0EeIQIMwwILQR8hAgzCAgtBICECDMECC0EiIQIMwAILQSMhAgy/AgtBJSECDL4CC0HlACECDL0CCyADQT02AhwgAyABNgIUIAMgADYCDEEAIQIM1QILIANBGzYCHCADIAE2AhQgA0GkHDYCECADQRU2AgxBACECDNQCCyADQSA2AhwgAyABNgIUIANBmBo2AhAgA0EVNgIMQQAhAgzTAgsgA0ETNgIcIAMgATYCFCADQZgaNgIQIANBFTYCDEEAIQIM0gILIANBCzYCHCADIAE2AhQgA0GYGjYCECADQRU2AgxBACECDNECCyADQRA2AhwgAyABNgIUIANBmBo2AhAgA0EVNgIMQQAhAgzQAgsgA0EgNgIcIAMgATYCFCADQaQcNgIQIANBFTYCDEEAIQIMzwILIANBCzYCHCADIAE2AhQgA0GkHDYCECADQRU2AgxBACECDM4CCyADQQw2AhwgAyABNgIUIANBpBw2AhAgA0EVNgIMQQAhAgzNAgtBACECIANBADYCHCADIAE2AhQgA0HdDjYCECADQRI2AgwMzAILAkADQAJAIAEtAABBCmsOBAACAgACCyAEIAFBAWoiAUcNAAtB/QEhAgzMAgsCQAJAIAMtADZBAUcNAEEAIQACQCADKAI4IgJFDQAgAigCYCICRQ0AIAMgAhEAACEACyAARQ0AIABBFUcNASADQfwBNgIcIAMgATYCFCADQdwZNgIQIANBFTYCDEEAIQIMzQILQdwBIQIMswILIANBADYCHCADIAE2AhQgA0H5CzYCECADQR82AgxBACECDMsCCwJAAkAgAy0AKEEBaw4CBAEAC0HbASECDLICC0HUASECDLECCyADQQI6ADFBACEAAkAgAygCOCICRQ0AIAIoAgAiAkUNACADIAIRAAAhAAsgAEUEQEHdASECDLECCyAAQRVHBEAgA0EANgIcIAMgATYCFCADQbQMNgIQIANBEDYCDEEAIQIMygILIANB+wE2AhwgAyABNgIUIANBgRo2AhAgA0EVNgIMQQAhAgzJAgsgASAERgRAQfoBIQIMyQILIAEtAABByABGDQEgA0EBOgAoC0HAASECDK4CC0HaASECDK0CCyABIARHBEAgA0EMNgIIIAMgATYCBEHZASECDK0CC0H5ASECDMUCCyABIARGBEBB+AEhAgzFAgsgAS0AAEHIAEcNBCABQQFqIQFB2AEhAgyrAgsgASAERgRAQfcBIQIMxAILAkACQCABLQAAQcUAaw4QAAUFBQUFBQUFBQUFBQUFAQULIAFBAWohAUHWASECDKsCCyABQQFqIQFB1wEhAgyqAgtB9gEhAiABIARGDcICIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQbrVAGotAABHDQMgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADMMCCyADKAIEIQAgA0IANwMAIAMgACAGQQFqIgEQLiIARQRAQeMBIQIMqgILIANB9QE2AhwgAyABNgIUIAMgADYCDEEAIQIMwgILQfQBIQIgASAERg3BAiADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEG41QBqLQAARw0CIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzCAgsgA0GBBDsBKCADKAIEIQAgA0IANwMAIAMgACAGQQFqIgEQLiIADQMMAgsgA0EANgIAC0EAIQIgA0EANgIcIAMgATYCFCADQeUfNgIQIANBCDYCDAy/AgtB1QEhAgylAgsgA0HzATYCHCADIAE2AhQgAyAANgIMQQAhAgy9AgtBACEAAkAgAygCOCICRQ0AIAIoAkAiAkUNACADIAIRAAAhAAsgAEUNbiAAQRVHBEAgA0EANgIcIAMgATYCFCADQYIPNgIQIANBIDYCDEEAIQIMvQILIANBjwE2AhwgAyABNgIUIANB7Bs2AhAgA0EVNgIMQQAhAgy8AgsgASAERwRAIANBDTYCCCADIAE2AgRB0wEhAgyjAgtB8gEhAgy7AgsgASAERgRAQfEBIQIMuwILAkACQAJAIAEtAABByABrDgsAAQgICAgICAgIAggLIAFBAWohAUHQASECDKMCCyABQQFqIQFB0QEhAgyiAgsgAUEBaiEBQdIBIQIMoQILQfABIQIgASAERg25AiADKAIAIgAgBCABa2ohBiABIABrQQJqIQUDQCABLQAAIABBtdUAai0AAEcNBCAAQQJGDQMgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAY2AgAMuQILQe8BIQIgASAERg24AiADKAIAIgAgBCABa2ohBiABIABrQQFqIQUDQCABLQAAIABBs9UAai0AAEcNAyAAQQFGDQIgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAY2AgAMuAILQe4BIQIgASAERg23AiADKAIAIgAgBCABa2ohBiABIABrQQJqIQUDQCABLQAAIABBsNUAai0AAEcNAiAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAY2AgAMtwILIAMoAgQhACADQgA3AwAgAyAAIAVBAWoiARArIgBFDQIgA0HsATYCHCADIAE2AhQgAyAANgIMQQAhAgy2AgsgA0EANgIACyADKAIEIQAgA0EANgIEIAMgACABECsiAEUNnAIgA0HtATYCHCADIAE2AhQgAyAANgIMQQAhAgy0AgtBzwEhAgyaAgtBACEAAkAgAygCOCICRQ0AIAIoAjQiAkUNACADIAIRAAAhAAsCQCAABEAgAEEVRg0BIANBADYCHCADIAE2AhQgA0HqDTYCECADQSY2AgxBACECDLQCC0HOASECDJoCCyADQesBNgIcIAMgATYCFCADQYAbNgIQIANBFTYCDEEAIQIMsgILIAEgBEYEQEHrASECDLICCyABLQAAQS9GBEAgAUEBaiEBDAELIANBADYCHCADIAE2AhQgA0GyODYCECADQQg2AgxBACECDLECC0HNASECDJcCCyABIARHBEAgA0EONgIIIAMgATYCBEHMASECDJcCC0HqASECDK8CCyABIARGBEBB6QEhAgyvAgsgAS0AAEEwayIAQf8BcUEKSQRAIAMgADoAKiABQQFqIQFBywEhAgyWAgsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDZcCIANB6AE2AhwgAyABNgIUIAMgADYCDEEAIQIMrgILIAEgBEYEQEHnASECDK4CCwJAIAEtAABBLkYEQCABQQFqIQEMAQsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDZgCIANB5gE2AhwgAyABNgIUIAMgADYCDEEAIQIMrgILQcoBIQIMlAILIAEgBEYEQEHlASECDK0CC0EAIQBBASEFQQEhB0EAIQICQAJAAkACQAJAAn8CQAJAAkACQAJAAkACQCABLQAAQTBrDgoKCQABAgMEBQYICwtBAgwGC0EDDAULQQQMBAtBBQwDC0EGDAILQQcMAQtBCAshAkEAIQVBACEHDAILQQkhAkEBIQBBACEFQQAhBwwBC0EAIQVBASECCyADIAI6ACsgAUEBaiEBAkACQCADLQAuQRBxDQACQAJAAkAgAy0AKg4DAQACBAsgB0UNAwwCCyAADQEMAgsgBUUNAQsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDQIgA0HiATYCHCADIAE2AhQgAyAANgIMQQAhAgyvAgsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDZoCIANB4wE2AhwgAyABNgIUIAMgADYCDEEAIQIMrgILIAMoAgQhACADQQA2AgQgAyAAIAEQLyIARQ2YAiADQeQBNgIcIAMgATYCFCADIAA2AgwMrQILQckBIQIMkwILQQAhAAJAIAMoAjgiAkUNACACKAJEIgJFDQAgAyACEQAAIQALAkAgAARAIABBFUYNASADQQA2AhwgAyABNgIUIANBpA02AhAgA0EhNgIMQQAhAgytAgtByAEhAgyTAgsgA0HhATYCHCADIAE2AhQgA0HQGjYCECADQRU2AgxBACECDKsCCyABIARGBEBB4QEhAgyrAgsCQCABLQAAQSBGBEAgA0EAOwE0IAFBAWohAQwBCyADQQA2AhwgAyABNgIUIANBmRE2AhAgA0EJNgIMQQAhAgyrAgtBxwEhAgyRAgsgASAERgRAQeABIQIMqgILAkAgAS0AAEEwa0H/AXEiAkEKSQRAIAFBAWohAQJAIAMvATQiAEGZM0sNACADIABBCmwiADsBNCAAQf7/A3EgAkH//wNzSw0AIAMgACACajsBNAwCC0EAIQIgA0EANgIcIAMgATYCFCADQZUeNgIQIANBDTYCDAyrAgsgA0EANgIcIAMgATYCFCADQZUeNgIQIANBDTYCDEEAIQIMqgILQcYBIQIMkAILIAEgBEYEQEHfASECDKkCCwJAIAEtAABBMGtB/wFxIgJBCkkEQCABQQFqIQECQCADLwE0IgBBmTNLDQAgAyAAQQpsIgA7ATQgAEH+/wNxIAJB//8Dc0sNACADIAAgAmo7ATQMAgtBACECIANBADYCHCADIAE2AhQgA0GVHjYCECADQQ02AgwMqgILIANBADYCHCADIAE2AhQgA0GVHjYCECADQQ02AgxBACECDKkCC0HFASECDI8CCyABIARGBEBB3gEhAgyoAgsCQCABLQAAQTBrQf8BcSICQQpJBEAgAUEBaiEBAkAgAy8BNCIAQZkzSw0AIAMgAEEKbCIAOwE0IABB/v8DcSACQf//A3NLDQAgAyAAIAJqOwE0DAILQQAhAiADQQA2AhwgAyABNgIUIANBlR42AhAgA0ENNgIMDKkCCyADQQA2AhwgAyABNgIUIANBlR42AhAgA0ENNgIMQQAhAgyoAgtBxAEhAgyOAgsgASAERgRAQd0BIQIMpwILAkACQAJAAkAgAS0AAEEKaw4XAgMDAAMDAwMDAwMDAwMDAwMDAwMDAwEDCyABQQFqDAULIAFBAWohAUHDASECDI8CCyABQQFqIQEgA0Evai0AAEEBcQ0IIANBADYCHCADIAE2AhQgA0GNCzYCECADQQ02AgxBACECDKcCCyADQQA2AhwgAyABNgIUIANBjQs2AhAgA0ENNgIMQQAhAgymAgsgASAERwRAIANBDzYCCCADIAE2AgRBASECDI0CC0HcASECDKUCCwJAAkADQAJAIAEtAABBCmsOBAIAAAMACyAEIAFBAWoiAUcNAAtB2wEhAgymAgsgAygCBCEAIANBADYCBCADIAAgARAtIgBFBEAgAUEBaiEBDAQLIANB2gE2AhwgAyAANgIMIAMgAUEBajYCFEEAIQIMpQILIAMoAgQhACADQQA2AgQgAyAAIAEQLSIADQEgAUEBagshAUHBASECDIoCCyADQdkBNgIcIAMgADYCDCADIAFBAWo2AhRBACECDKICC0HCASECDIgCCyADQS9qLQAAQQFxDQEgA0EANgIcIAMgATYCFCADQeQcNgIQIANBGTYCDEEAIQIMoAILIAEgBEYEQEHZASEC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IAFBAWohAUGeASECDOQBCyABQQFqIQFBnwEhAgzjAQtBtwEhAiABIARGDfsBIAMoAgAiACAEIAFraiEFIAEgAGtBA2ohBgJAA0AgAS0AACAAQezUAGotAABHDSggAEEDRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPwBCyADQQA2AgAgBkEBaiEBQQ0MKAtBtgEhAiABIARGDfoBIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQaHVAGotAABHDScgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPsBCyADQQA2AgAgBkEBaiEBQQwMJwtBtQEhAiABIARGDfkBIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQerUAGotAABHDSYgAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPoBCyADQQA2AgAgBkEBaiEBQQMMJgtBtAEhAiABIARGDfgBIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQejUAGotAABHDSUgAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPkBCyADQQA2AgAgBkEBaiEBQSYMJQsgASAERgRAQbMBIQIM+AELAkACQCABLQAAQdQAaw4CAAEnCyABQQFqIQFBmQEhAgzfAQsgAUEBaiEBQZoBIQIM3gELQbIBIQIgASAERg32ASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHm1ABqLQAARw0jIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAz3AQsgA0EANgIAIAZBAWohAUEnDCMLQbEBIQIgASAERg31ASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHk1ABqLQAARw0iIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAz2AQsgA0EANgIAIAZBAWohAUEcDCILQbABIQIgASAERg30ASADKAIAIgAgBCABa2ohBSABIABrQQVqIQYCQANAIAEtAAAgAEHe1ABqLQAARw0hIABBBUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAz1AQsgA0EANgIAIAZBAWohAUEGDCELQa8BIQIgASAERg3zASADKAIAIgAgBCABa2ohBSABIABrQQRqIQYCQANAIAEtAAAgAEHZ1ABqLQAARw0gIABBBEYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAz0AQsgA0EANgIAIAZBAWohAUEZDCALIAEgBEYEQEGuASECDPMBCwJAAkACQAJAIAEtAABBLWsOIwAkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJAEkJCQkJAIkJCQDJAsgAUEBaiEBQY4BIQIM3AELIAFBAWohAUGPASECDNsBCyABQQFqIQFBlAEhAgzaAQsgAUEBaiEBQZUBIQIM2QELQa0BIQIgASAERg3xASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHX1ABqLQAARw0eIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzyAQsgA0EANgIAIAZBAWohAUELDB4LIAEgBEYEQEGsASECDPEBCwJAAkAgAS0AAEHBAGsOAwAgASALIAFBAWohAUGQASECDNgBCyABQQFqIQFBkwEhAgzXAQsgASAERgRAQasBIQIM8AELAkACQCABLQAAQcEAaw4PAB8fHx8fHx8fHx8fHx8BHwsgAUEBaiEBQZEBIQIM1wELIAFBAWohAUGSASECDNYBCyABIARGBEBBqgEhAgzvAQsgAS0AAEHMAEcNHCABQQFqIQFBCgwbC0GpASECIAEgBEYN7QEgAygCACIAIAQgAWtqIQUgASAAa0EFaiEGAkADQCABLQAAIABB0dQAai0AAEcNGiAAQQVGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM7gELIANBADYCACAGQQFqIQFBHgwaC0GoASECIAEgBEYN7AEgAygCACIAIAQgAWtqIQUgASAAa0EGaiEGAkADQCABLQAAIABBytQAai0AAEcNGSAAQQZGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM7QELIANBADYCACAGQQFqIQFBFQwZC0GnASECIAEgBEYN6wEgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBx9QAai0AAEcNGCAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM7AELIANBADYCACAGQQFqIQFBFwwYC0GmASECIAEgBEYN6gEgAygCACIAIAQgAWtqIQUgASAAa0EFaiEGAkADQCABLQAAIABBwdQAai0AAEcNFyAAQQVGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM6wELIANBADYCACAGQQFqIQFBGAwXCyABIARGBEBBpQEhAgzqAQsCQAJAIAEtAABByQBrDgcAGRkZGRkBGQsgAUEBaiEBQYsBIQIM0QELIAFBAWohAUGMASECDNABC0GkASECIAEgBEYN6AEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGAkADQCABLQAAIABBptUAai0AAEcNFSAAQQFGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM6QELIANBADYCACAGQQFqIQFBCQwVC0GjASECIAEgBEYN5wEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGAkADQCABLQAAIABBpNUAai0AAEcNFCAAQQFGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM6AELIANBADYCACAGQQFqIQFBHwwUC0GiASECIAEgBEYN5gEgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBvtQAai0AAEcNEyAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM5wELIANBADYCACAGQQFqIQFBAgwTC0GhASECIAEgBEYN5QEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGA0AgAS0AACAAQbzUAGotAABHDREgAEEBRg0CIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADOUBCyABIARGBEBBoAEhAgzlAQtBASABLQAAQd8ARw0RGiABQQFqIQFBhwEhAgzLAQsgA0EANgIAIAZBAWohAUGIASECDMoBC0GfASECIAEgBEYN4gEgAygCACIAIAQgAWtqIQUgASAAa0EIaiEGAkADQCABLQAAIABBhNUAai0AAEcNDyAAQQhGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM4wELIANBADYCACAGQQFqIQFBKQwPC0GeASECIAEgBEYN4QEgAygCACIAIAQgAWtqIQUgASAAa0EDaiEGAkADQCABLQAAIABBuNQAai0AAEcNDiAAQQNGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM4gELIANBADYCACAGQQFqIQFBLQwOCyABIARGBEBBnQEhAgzhAQsgAS0AAEHFAEcNDiABQQFqIQFBhAEhAgzHAQsgASAERgRAQZwBIQIM4AELAkACQCABLQAAQcwAaw4IAA8PDw8PDwEPCyABQQFqIQFBggEhAgzHAQsgAUEBaiEBQYMBIQIMxgELQZsBIQIgASAERg3eASADKAIAIgAgBCABa2ohBSABIABrQQRqIQYCQANAIAEtAAAgAEGz1ABqLQAARw0LIABBBEYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzfAQsgA0EANgIAIAZBAWohAUEjDAsLQZoBIQIgASAERg3dASADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGw1ABqLQAARw0KIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzeAQsgA0EANgIAIAZBAWohAUEADAoLIAEgBEYEQEGZASECDN0BCwJAAkAgAS0AAEHIAGsOCAAMDAwMDAwBDAsgAUEBaiEBQf0AIQIMxAELIAFBAWohAUGAASECDMMBCyABIARGBEBBmAEhAgzcAQsCQAJAIAEtAABBzgBrDgMACwELCyABQQFqIQFB/gAhAgzDAQsgAUEBaiEBQf8AIQIMwgELIAEgBEYEQEGXASECDNsBCyABLQAAQdkARw0IIAFBAWohAUEIDAcLQZYBIQIgASAERg3ZASADKAIAIgAgBCABa2ohBSABIABrQQNqIQYCQANAIAEtAAAgAEGs1ABqLQAARw0GIABBA0YNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzaAQsgA0EANgIAIAZBAWohAUEFDAYLQZUBIQIgASAERg3YASADKAIAIgAgBCABa2ohBSABIABrQQVqIQYCQANAIAEtAAAgAEGm1ABqLQAARw0FIABBBUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzZAQsgA0EANgIAIAZBAWohAUEWDAULQZQBIQIgASAERg3XASADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGh1QBqLQAARw0EIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzYAQsgA0EANgIAIAZBAWohAUEQDAQLIAEgBEYEQEGTASECDNcBCwJAAkAgAS0AAEHDAGsODAAGBgYGBgYGBgYGAQYLIAFBAWohAUH5ACECDL4BCyABQQFqIQFB+gAhAgy9AQtBkgEhAiABIARGDdUBIAMoAgAiACAEIAFraiEFIAEgAGtBBWohBgJAA0AgAS0AACAAQaDUAGotAABHDQIgAEEFRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADNYBCyADQQA2AgAgBkEBaiEBQSQMAgsgA0EANgIADAILIAEgBEYEQEGRASECDNQBCyABLQAAQcwARw0BIAFBAWohAUETCzoAKSADKAIEIQAgA0EANgIEIAMgACABEC4iAA0CDAELQQAhAiADQQA2AhwgAyABNgIUIANB/h82AhAgA0EGNgIMDNEBC0H4ACECDLcBCyADQZABNgIcIAMgATYCFCADIAA2AgxBACECDM8BC0EAIQACQCADKAI4IgJFDQAgAigCQCICRQ0AIAMgAhEAACEACyAARQ0AIABBFUYNASADQQA2AhwgAyABNgIUIANBgg82AhAgA0EgNgIMQQAhAgzOAQtB9wAhAgy0AQsgA0GPATYCHCADIAE2AhQgA0HsGzYCECADQRU2AgxBACECDMwBCyABIARGBEBBjwEhAgzMAQsCQCABLQAAQSBGBEAgAUEBaiEBDAELIANBADYCHCADIAE2AhQgA0GbHzYCECADQQY2AgxBACECDMwBC0ECIQIMsgELA0AgAS0AAEEgRw0CIAQgAUEBaiIBRw0AC0GOASECDMoBCyABIARGBEBBjQEhAgzKAQsCQCABLQAAQQlrDgRKAABKAAtB9QAhAgywAQsgAy0AKUEFRgRAQfYAIQIMsAELQfQAIQIMrwELIAEgBEYEQEGMASECDMgBCyADQRA2AgggAyABNgIEDAoLIAEgBEYEQEGLASECDMcBCwJAIAEtAABBCWsOBEcAAEcAC0HzACECDK0BCyABIARHBEAgA0EQNgIIIAMgATYCBEHxACECDK0BC0GKASECDMUBCwJAIAEgBEcEQANAIAEtAABBoNAAai0AACIAQQNHBEACQCAAQQFrDgJJAAQLQfAAIQIMrwELIAQgAUEBaiIBRw0AC0GIASEC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BCABQQFqIgFHDQALQfsAIQIMpwELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0DIANB2wA2AhwgAyABNgIUIAMgADYCDEEAIQIMpgELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0DIANB3QA2AhwgAyABNgIUIAMgADYCDEEAIQIMpQELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0DIANB+gA2AhwgAyABNgIUIAMgADYCDEEAIQIMpAELIANBADYCHCADIAE2AhQgA0G8CjYCECADQQc2AgxBACECDKMBC0HPACECDIkBC0HRACECDIgBC0HnACECDIcBCyABIARGBEBB+gAhAgygAQsCQCABLQAAQQlrDgQgAAAgAAsgAUEBaiEBQeYAIQIMhgELIAEgBEYEQEH5ACECDJ8BCwJAIAEtAABBCWsOBB8AAB8AC0EAIQACQCADKAI4IgJFDQAgAigCOCICRQ0AIAMgAhEAACEACyAARQRAQeIBIQIMhgELIABBFUcEQCADQQA2AhwgAyABNgIUIANByQ02AhAgA0EaNgIMQQAhAgyfAQsgA0H4ADYCHCADIAE2AhQgA0HqGjYCECADQRU2AgxBACECDJ4BCyABIARHBEAgA0ENNgIIIAMgATYCBEHkACECDIUBC0H3ACECDJ0BCyABIARGBEBB9gAhAgydAQsCQAJAAkAgAS0AAEHIAGsOCwABCwsLCwsLCwsCCwsgAUEBaiEBQd0AIQIMhQELIAFBAWohAUHgACECDIQBCyABQQFqIQFB4wAhAgyDAQtB9QAhAiABIARGDZsBIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQbXVAGotAABHDQggAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADJwBCyADKAIEIQAgA0IANwMAIAMgACAGQQFqIgEQKyIABEAgA0H0ADYCHCADIAE2AhQgAyAANgIMQQAhAgycAQtB4gAhAgyCAQtBACEAAkAgAygCOCICRQ0AIAIoAjQiAkUNACADIAIRAAAhAAsCQCAABEAgAEEVRg0BIANBADYCHCADIAE2AhQgA0HqDTYCECADQSY2AgxBACECDJwBC0HhACECDIIBCyADQfMANgIcIAMgATYCFCADQYAbNgIQIANBFTYCDEEAIQIMmgELIAMtACkiAEEja0ELSQ0JAkAgAEEGSw0AQQEgAHRBygBxRQ0ADAoLQQAhAiADQQA2AhwgAyABNgIUIANB7Qk2AhAgA0EINgIMDJkBC0HyACECIAEgBEYNmAEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGAkADQCABLQAAIABBs9UAai0AAEcNBSAAQQFGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMmQELIAMoAgQhACADQgA3AwAgAyAAIAZBAWoiARArIgAEQCADQfEANgIcIAMgATYCFCADIAA2AgxBACECDJkBC0HfACECDH8LQQAhAAJAIAMoAjgiAkUNACACKAI0IgJFDQAgAyACEQAAIQALAkAgAARAIABBFUYNASADQQA2AhwgAyABNgIUIANB6g02AhAgA0EmNgIMQQAhAgyZAQtB3gAhAgx/CyADQfAANgIcIAMgATYCFCADQYAbNgIQIANBFTYCDEEAIQIMlwELIAMtAClBIUYNBiADQQA2AhwgAyABNgIUIANBkQo2AhAgA0EINgIMQQAhAgyWAQtB7wAhAiABIARGDZUBIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQbDVAGotAABHDQIgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADJYBCyADKAIEIQAgA0IANwMAIAMgACAGQQFqIgEQKyIARQ0CIANB7QA2AhwgAyABNgIUIAMgADYCDEEAIQIMlQELIANBADYCAAsgAygCBCEAIANBADYCBCADIAAgARArIgBFDYABIANB7gA2AhwgAyABNgIUIAMgADYCDEEAIQIMkwELQdwAIQIMeQtBACEAAkAgAygCOCICRQ0AIAIoAjQiAkUNACADIAIRAAAhAAsCQCAABEAgAEEVRg0BIANBADYCHCADIAE2AhQgA0HqDTYCECADQSY2AgxBACECDJMBC0HbACECDHkLIANB7AA2AhwgAyABNgIUIANBgBs2AhAgA0EVNgIMQQAhAgyRAQsgAy0AKSIAQSNJDQAgAEEuRg0AIANBADYCHCADIAE2AhQgA0HJCTYCECADQQg2AgxBACECDJABC0HaACECDHYLIAEgBEYEQEHrACECDI8BCwJAIAEtAABBL0YEQCABQQFqIQEMAQsgA0EANgIcIAMgATYCFCADQbI4NgIQIANBCDYCDEEAIQIMjwELQdkAIQIMdQsgASAERwRAIANBDjYCCCADIAE2AgRB2AAhAgx1C0HqACECDI0BCyABIARGBEBB6QAhAgyNAQsgAS0AAEEwayIAQf8BcUEKSQRAIAMgADoAKiABQQFqIQFB1wAhAgx0CyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNeiADQegANgIcIAMgATYCFCADIAA2AgxBACECDIwBCyABIARGBEBB5wAhAgyMAQsCQCABLQAAQS5GBEAgAUEBaiEBDAELIAMoAgQhACADQQA2AgQgAyAAIAEQLyIARQ17IANB5gA2AhwgAyABNgIUIAMgADYCDEEAIQIMjAELQdYAIQIMcgsgASAERgRAQeUAIQIMiwELQQAhAEEBIQVBASEHQQAhAgJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAIAEtAABBMGsOCgoJAAECAwQFBggLC0ECDAYLQQMMBQtBBAwEC0EFDAMLQQYMAgtBBwwBC0EICyECQQAhBUEAIQcMAgtBCSECQQEhAEEAIQVBACEHDAELQQAhBUEBIQILIAMgAjoAKyABQQFqIQECQAJAIAMtAC5BEHENAAJAAkACQCADLQAqDgMBAAIECyAHRQ0DDAILIAANAQwCCyAFRQ0BCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNAiADQeIANgIcIAMgATYCFCADIAA2AgxBACECDI0BCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNfSADQeMANgIcIAMgATYCFCADIAA2AgxBACECDIwBCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNeyADQeQANgIcIAMgATYCFCADIAA2AgwMiwELQdQAIQIMcQsgAy0AKUEiRg2GAUHTACECDHALQQAhAAJAIAMoAjgiAkUNACACKAJEIgJFDQAgAyACEQAAIQALIABFBEBB1QAhAgxwCyAAQRVHBEAgA0EANgIcIAMgATYCFCADQaQNNgIQIANBITYCDEEAIQIMiQELIANB4QA2AhwgAyABNgIUIANB0Bo2AhAgA0EVNgIMQQAhAgyIAQsgASAERgRAQeAAIQIMiAELAkACQAJAAkACQCABLQAAQQprDgQBBAQABAsgAUEBaiEBDAELIAFBAWohASADQS9qLQAAQQFxRQ0BC0HSACECDHALIANBADYCHCADIAE2AhQgA0G2ETYCECADQQk2AgxBACECDIgBCyADQQA2AhwgAyABNgIUIANBthE2AhAgA0EJNgIMQQAhAgyHAQsgASAERgRAQd8AIQIMhwELIAEtAABBCkYEQCABQQFqIQEMCQsgAy0ALkHAAHENCCADQQA2AhwgAyABNgIUIANBthE2AhAgA0ECNgIMQQAhAgyGAQsgASAERgRAQd0AIQIMhgELIAEtAAAiAkENRgRAIAFBAWohAUHQACECDG0LIAEhACACQQlrDgQFAQEFAQsgBCABIgBGBEBB3AAhAgyFAQsgAC0AAEEKRw0AIABBAWoMAgtBACECIANBADYCHCADIAA2AhQgA0HKLTYCECADQQc2AgwMgwELIAEgBEYEQEHbACECDIMBCwJAIAEtAABBCWsOBAMAAAMACyABQQFqCyEBQc4AIQIMaAsgASAERgRAQdoAIQIMgQELIAEtAABBCWsOBAABAQABC0EAIQIgA0EANgIcIANBmhI2AhAgA0EHNgIMIAMgAUEBajYCFAx/CyADQYASOwEqQQAhAAJAIAMoAjgiAkUNACACKAI4IgJFDQAgAyACEQAAIQALIABFDQAgAEEVRw0BIANB2QA2AhwgAyABNgIUIANB6ho2AhAgA0EVNgIMQQAhAgx+C0HNACECDGQLIANBADYCHCADIAE2AhQgA0HJDTYCECADQRo2AgxBACECDHwLIAEgBEYEQEHZACECDHwLIAEtAABBIEcNPSABQQFqIQEgAy0ALkEBcQ09IANBADYCHCADIAE2AhQgA0HCHDYCECADQR42AgxBACECDHsLIAEgBEYEQEHYACECDHsLAkACQAJAAkACQCABLQAAIgBBCmsOBAIDAwABCyABQQFqIQFBLCECDGULIABBOkcNASADQQA2AhwgAyABNgIUIANB5xE2AhAgA0EKNgIMQQAhAgx9CyABQQFqIQEgA0Evai0AAEEBcUUNcyADLQAyQYABcUUEQCADQTJqIQIgAxA1QQAhAAJAIAMoAjgiBkUNACAGKAIoIgZFDQAgAyAGEQAAIQALAkACQCAADhZNTEsBAQEBAQEBAQEBAQEBAQEBAQEAAQsgA0EpNgIcIAMgATYCFCADQawZNgIQIANBFTYCDEEAIQIMfgsg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c3BvbnNlIHN0YXR1cwBFeHBlY3RlZCBMRiBhZnRlciBoZWFkZXJzAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMAVXNlciBjYWxsYmFjayBlcnJvcgBgb25fcmVzZXRgIGNhbGxiYWNrIGVycm9yAGBvbl9jaHVua19oZWFkZXJgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2JlZ2luYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlYCBjYWxsYmFjayBlcnJvcgBgb25fc3RhdHVzX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdmVyc2lvbl9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3VybF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3Byb3RvY29sX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWV0aG9kX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX25hbWVgIGNhbGxiYWNrIGVycm9yAFVuZXhwZWN0ZWQgY2hhciBpbiB1cmwgc2VydmVyAEludmFsaWQgaGVhZGVyIHZhbHVlIGNoYXIASW52YWxpZCBoZWFkZXIgZmllbGQgY2hhcgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3ZlcnNpb24ASW52YWxpZCBtaW5vciB2ZXJzaW9uAEludmFsaWQgbWFqb3IgdmVyc2lvbgBFeHBlY3RlZCBzcGFjZSBhZnRlciB2ZXJzaW9uAEV4cGVjdGVkIENSTEYgYWZ0ZXIgdmVyc2lvbgBJbnZhbGlkIEhUVFAgdmVyc2lvbgBJbnZhbGlkIGhlYWRlciB0b2tlbgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3VybABJbnZhbGlkIGNoYXJhY3RlcnMgaW4gdXJsAFVuZXhwZWN0ZWQgc3RhcnQgY2hhciBpbiB1cmwARG91YmxlIEAgaW4gdXJsAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fcHJvdG9jb2wARW1wdHkgQ29udGVudC1MZW5ndGgASW52YWxpZCBjaGFyYWN0ZXIgaW4gQ29udGVudC1MZW5ndGgAVHJhbnNmZXItRW5jb2RpbmcgY2FuJ3QgYmUgcHJlc2VudCB3aXRoIENvbnRlbnQtTGVuZ3RoAER1cGxpY2F0ZSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXIgaW4gdXJsIHBhdGgAQ29udGVudC1MZW5ndGggY2FuJ3QgYmUgcHJlc2VudCB3aXRoIFRyYW5zZmVyLUVuY29kaW5nAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgY2h1bmsgc2l6ZQBFeHBlY3RlZCBMRiBhZnRlciBjaHVuayBzaXplAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIHNpemUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfdmFsdWUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyB2YWx1ZQBVbmV4cGVjdGVkIHdoaXRlc3BhY2UgYWZ0ZXIgaGVhZGVyIHZhbHVlAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgaGVhZGVyIHZhbHVlAE1pc3NpbmcgZXhwZWN0ZWQgTEYgYWZ0ZXIgaGVhZGVyIHZhbHVlAEludmFsaWQgYFRyYW5zZmVyLUVuY29kaW5nYCBoZWFkZXIgdmFsdWUATWlzc2luZyBleHBlY3RlZCBDUiBhZnRlciBjaHVuayBleHRlbnNpb24gdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBxdW90ZSB2YWx1ZQBJbnZhbGlkIHF1b3RlZC1wYWlyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAFBhdXNlZCBieSBvbl9oZWFkZXJzX2NvbXBsZXRlAEludmFsaWQgRU9GIHN0YXRlAG9uX3Jlc2V0IHBhdXNlAG9uX2NodW5rX2hlYWRlciBwYXVzZQBvbl9tZXNzYWdlX2JlZ2luIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl92YWx1ZSBwYXVzZQBvbl9zdGF0dXNfY29tcGxldGUgcGF1c2UAb25fdmVyc2lvbl9jb21wbGV0ZSBwYXVzZQBvbl91cmxfY29tcGxldGUgcGF1c2UAb25fcHJvdG9jb2xfY29tcGxldGUgcGF1c2UAb25fY2h1bmtfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX3ZhbHVlX2NvbXBsZXRlIHBhdXNlAG9uX21lc3NhZ2VfY29tcGxldGUgcGF1c2UAb25fbWV0aG9kX2NvbXBsZXRlIHBhdXNlAG9uX2hlYWRlcl9maWVsZF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19leHRlbnNpb25fbmFtZSBwYXVzZQBVbmV4cGVjdGVkIHNwYWNlIGFmdGVyIHN0YXJ0IGxpbmUATWlzc2luZyBleHBlY3RlZCBDUiBhZnRlciByZXNwb25zZSBsaW5lAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fY2h1bmtfZXh0ZW5zaW9uX25hbWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBuYW1lAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgY2h1bmsgZXh0ZW5zaW9uIG5hbWUASW52YWxpZCBzdGF0dXMgY29kZQBQYXVzZSBvbiBDT05ORUNUL1VwZ3JhZGUAUGF1c2Ugb24gUFJJL1VwZ3JhZGUARXhwZWN0ZWQgSFRUUC8yIENvbm5lY3Rpb24gUHJlZmFjZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX21ldGhvZABFeHBlY3RlZCBzcGFjZSBhZnRlciBtZXRob2QAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfZmllbGQAUGF1c2VkAEludmFsaWQgd29yZCBlbmNvdW50ZXJlZABJbnZhbGlkIG1ldGhvZCBlbmNvdW50ZXJlZABNaXNzaW5nIGV4cGVjdGVkIENSIGFmdGVyIGNodW5rIGRhdGEARXhwZWN0ZWQgTEYgYWZ0ZXIgY2h1bmsgZGF0YQBVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNjaGVtYQBSZXF1ZXN0IGhhcyBpbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AARGF0YSBhZnRlciBgQ29ubmVjdGlvbjogY2xvc2VgAFNXSVRDSF9QUk9YWQBVU0VfUFJPWFkATUtBQ1RJVklUWQBVTlBST0NFU1NBQkxFX0VOVElUWQBRVUVSWQBDT1BZAE1PVkVEX1BFUk1BTkVOVExZAFRPT19FQVJMWQBOT1RJRlkARkFJTEVEX0RFUEVOREVOQ1kAQkFEX0dBVEVXQVkAUExBWQBQVVQAQ0hFQ0tPVVQAR0FURVdBWV9USU1FT1VUAFJFUVVFU1RfVElNRU9VVABORVRXT1JLX0NPTk5FQ1RfVElNRU9VVABDT05ORUNUSU9OX1RJTUVPVVQATE9HSU5fVElNRU9VVABORVRXT1JLX1JFQURfVElNRU9VVABQT1NUAE1JU0RJUkVDVEVEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9SRVFVRVNUAENMSUVOVF9DTE9TRURfTE9BRF9CQUxBTkNFRF9SRVFVRVNUAEJBRF9SRVFVRVNUAEhUVFBfUkVRVUVTVF9TRU5UX1RPX0hUVFBTX1BPUlQAUkVQT1JUAElNX0FfVEVBUE9UAFJFU0VUX0NPTlRFTlQATk9fQ09OVEVOVABQQVJUSUFMX0NPTlRFTlQASFBFX0lOVkFMSURfQ09OU1RBTlQASFBFX0NCX1JFU0VUAEdFVABIUEVfU1RSSUNUAENPTkZMSUNUAFRFTVBPUkFSWV9SRURJUkVDVABQRVJNQU5FTlRfUkVESVJFQ1QAQ09OTkVDVABNVUxUSV9TVEFUVVMASFBFX0lOVkFMSURfU1RBVFVTAFRPT19NQU5ZX1JFUVVFU1RTAEVBUkxZX0hJTlRTAFVOQVZBSUxBQkxFX0ZPUl9MRUdBTF9SRUFTT05TAE9QVElPTlMAU1dJVENISU5HX1BST1RPQ09MUwBWQVJJQU5UX0FMU09fTkVHT1RJQVRFUwBNVUxUSVBMRV9DSE9JQ0VTAElOVEVSTkFMX1NFUlZFUl9FUlJPUgBXRUJfU0VSVkVSX1VOS05PV05fRVJST1IAUkFJTEdVTl9FUlJPUgBJREVOVElUWV9QUk9WSURFUl9BVVRIRU5USUNBVElPTl9FUlJPUgBTU0xfQ0VSVElGSUNBVEVfRVJST1IASU5WQUxJRF9YX0ZPUldBUkRFRF9GT1IAU0VUX1BBUkFNRVRFUgBHRVRfUEFSQU1FVEVSAEhQRV9VU0VSAFNFRV9PVEhFUgBIUEVfQ0JfQ0hVTktfSEVBREVSAEV4cGVjdGVkIExGIGFmdGVyIENSAE1LQ0FMRU5EQVIAU0VUVVAAV0VCX1NFUlZFUl9JU19ET1dOAFRFQVJET1dOAEhQRV9DTE9TRURfQ09OTkVDVElPTgBIRVVSSVNUSUNfRVhQSVJBVElPTgBESVNDT05ORUNURURfT1BFUkFUSU9OAE5PTl9BVVRIT1JJVEFUSVZFX0lORk9STUFUSU9OAEhQRV9JTlZBTElEX1ZFUlNJT04ASFBFX0NCX01FU1NBR0VfQkVHSU4AU0lURV9JU19GUk9aRU4ASFBFX0lOVkFMSURfSEVBREVSX1RPS0VOAElOVkFMSURfVE9LRU4ARk9SQklEREVOAEVOSEFOQ0VfWU9VUl9DQUxNAEhQRV9JTlZBTElEX1VSTABCTE9DS0VEX0JZX1BBUkVOVEFMX0NPTlRST0wATUtDT0wAQUNMAEhQRV9JTlRFUk5BTABSRVFVRVNUX0hFQURFUl9GSUVMRFNfVE9PX0xBUkdFX1VOT0ZGSUNJQUwASFBFX09LAFVOTElOSwBVTkxPQ0sAUFJJAFJFVFJZX1dJVEgASFBFX0lOVkFMSURfQ09OVEVOVF9MRU5HVEgASFBFX1VORVhQRUNURURfQ09OVEVOVF9MRU5HVEgARkxVU0gAUFJPUFBBVENIAE0tU0VBUkNIAFVSSV9UT09fTE9ORwBQUk9DRVNTSU5HAE1JU0NFTExBTkVPVVNfUEVSU0lTVEVOVF9XQVJOSU5HAE1JU0NFTExBTkVPVVNfV0FSTklORwBIUEVfSU5WQUxJRF9UUkFOU0ZFUl9FTkNPRElORwBFeHBlY3RlZCBDUkxGAEhQRV9JTlZBTElEX0NIVU5LX1NJWkUATU9WRQBDT05USU5VRQBIUEVfQ0JfU1RBVFVTX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJTX0NPTVBMRVRFAEhQRV9DQl9WRVJTSU9OX0NPTVBMRVRFAEhQRV9DQl9VUkxfQ09NUExFVEUASFBFX0NCX1BST1RPQ09MX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19DT01QTEVURQBIUEVfQ0JfSEVBREVSX1ZBTFVFX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19FWFRFTlNJT05fVkFMVUVfQ09NUExFVEUASFBFX0NCX0NIVU5LX0VYVEVOU0lPTl9OQU1FX0NPTVBMRVRFAEhQRV9DQl9NRVNTQUdFX0NPTVBMRVRFAEhQRV9DQl9NRVRIT0RfQ09NUExFVEUASFBFX0NCX0hFQURFUl9GSUVMRF9DT01QTEVURQBERUxFVEUASFBFX0lOVkFMSURfRU9GX1NUQVRFAElOVkFMSURfU1NMX0NFUlRJRklDQVRFAFBBVVNFAE5PX1JFU1BPTlNFAFVOU1VQUE9SVEVEX01FRElBX1RZUEUAR09ORQBOT1RfQUNDRVBUQUJMRQBTRVJWSUNFX1VOQVZBSUxBQkxFAFJBTkdFX05PVF9TQVRJU0ZJQUJMRQBPUklHSU5fSVNfVU5SRUFDSEFCTEUAUkVTUE9OU0VfSVNfU1RBTEUAUFVSR0UATUVSR0UAUkVRVUVTVF9IRUFERVJfRklFTERTX1RPT19MQVJHRQBSRVFVRVNUX0hFQURFUl9UT09fTEFSR0UAUEFZTE9BRF9UT09fTEFSR0UASU5TVUZGSUNJRU5UX1NUT1JBR0UASFBFX1BBVVNFRF9VUEdSQURFAEhQRV9QQVVTRURfSDJfVVBHUkFERQBTT1VSQ0UAQU5OT1VOQ0UAVFJBQ0UASFBFX1VORVhQRUNURURfU1BBQ0UAREVTQ1JJQkUAVU5TVUJTQ1JJQkUAUkVDT1JEAEhQRV9JTlZBTElEX01FVEhPRABOT1RfRk9VTkQAUFJPUEZJTkQAVU5CSU5EAFJFQklORABVTkFVVEhPUklaRUQATUVUSE9EX05PVF9BTExPV0VEAEhUVFBfVkVSU0lPTl9OT1RfU1VQUE9SVEVEAEFMUkVBRFlfUkVQT1JURUQAQUNDRVBURUQATk9UX0lNUExFTUVOVEVEAExPT1BfREVURUNURUQASFBFX0NSX0VYUEVDVEVEAEhQRV9MRl9FWFBFQ1RFRABDUkVBVEVEAElNX1VTRUQASFBFX1BBVVNFRABUSU1FT1VUX09DQ1VSRUQAUEFZTUVOVF9SRVFVSVJFRABQUkVDT05ESVRJT05fUkVRVUlSRUQAUFJPWFlfQVVUSEVOVElDQVRJT05fUkVRVUlSRUQATkVUV09SS19BVVRIRU5USUNBVElPTl9SRVFVSVJFRABMRU5HVEhfUkVRVUlSRUQAU1NMX0NFUlRJRklDQVRFX1JFUVVJUkVEAFVQR1JBREVfUkVRVUlSRUQAUEFHRV9FWFBJUkVEAFBSRUNPTkRJVElPTl9GQUlMRUQARVhQRUNUQVRJT05fRkFJTEVEAFJFVkFMSURBVElPTl9GQUlMRUQAU1NMX0hBTkRTSEFLRV9GQUlMRUQATE9DS0VEAFRSQU5TRk9STUFUSU9OX0FQUExJRUQATk9UX01PRElGSUVEAE5PVF9FWFRFTkRFRABCQU5EV0lEVEhfTElNSVRfRVhDRUVERUQAU0lURV9JU19PVkVSTE9BREVEAEhFQUQARXhwZWN0ZWQgSFRUUC8sIFJUU1AvIG9yIElDRS8A5xUAAK8VAACkEgAAkhoAACYWAACeFAAA2xkAAHkVAAB+EgAA/hQAADYVAAALFgAA2BYAAPMSAABCGAAArBYAABIVAAAUFwAA7xcAAEgUAABxFwAAshoAAGsZAAB+GQAANRQAAIIaAABEFwAA/RYAAB4YAACHFwAAqhkAAJMSAAAHGAAALBcAAMoXAACkFwAA5xUAAOcVAABYFwAAOxgAAKASAAAtHAAAwxEAAEgRAADeEgAAQhMAAKQZAAD9EAAA9xUAAKUVAADvFgAA+BkAAEoWAABWFgAA9RUAAAoaAAAIGgAAARoAAKsVAABCEgAA1xAAAEwRAAAFGQAAVBYAAB4RAADKGQAAyBkAAE4WAAD/GAAAcRQAAPAVAADuFQAAlBkAAPwVAAC/GQAAmxkAAHwUAABDEQAAcBgAAJUUAAAnFAAAGRQAANUSAADUGQAARBYAAPcQAEG5OwsBAQBB0DsL4AEBAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBBuj0LBAEAAAIAQdE9C14DBAMDAwMDAAADAwADAwADAwMDAwMDAwMDAAUAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAwADAEG6PwsEAQAAAgBB0T8LXgMAAwMDAwMAAAMDAAMDAAMDAwMDAwMDAwMABAAFAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwADAAMAQbDBAAsNbG9zZWVlcC1hbGl2ZQBBycEACwEBAEHgwQAL4AEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBBycMACwEBAEHgwwAL5wEBAQEBAQEBAQEBAQECAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAWNodW5rZWQAQfHFAAteAQABAQEBAQAAAQEAAQEAAQEBAQEBAQEBAQAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQBB0McACyFlY3Rpb25lbnQtbGVuZ3Rob25yb3h5LWNvbm5lY3Rpb24AQYDIAAsgcmFuc2Zlci1lbmNvZGluZ3BncmFkZQ0KDQpTTQ0KDQoAQanIAAsFAQIAAQMAQcDIAAtfBAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAQanKAAsFAQIAAQMAQcDKAAtfBAUFBgUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAQanMAAsEAQAAAQBBwcwAC14CAgACAgICAgICAgICAgICAgICAgICAgICAgICAgIAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAEGpzgALBQECAAEDAEHAzgALXwQFAAAFBQUFBQUFBQUFBQYFBQUFBQUFBQUFBQUABQAHCAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQAFAAUABQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAAAAFAEGp0AALBQEBAAEBAEHA0AALAQEAQdrQAAtBAgAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAQanSAAsFAQEAAQEAQcDSAAsBAQBBytIACwYCAAAAAAIAQeHSAAs6AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBBoNQAC50BTk9VTkNFRUNLT1VUTkVDVEVURUNSSUJFTFVTSEVURUFEU0VBUkNIUkdFQ1RJVklUWUxFTkRBUlZFT1RJRllQVElPTlNDSFNFQVlTVEFUQ0hHRVVFUllPUkRJUkVDVE9SVFJDSFBBUkFNRVRFUlVSQ0VCU0NSSUJFQVJET1dOQUNFSU5ETktDS1VCU0NSSUJFVFRQQ0VUU1BBRFRQLw=='
-
-let wasmBuffer
-
-Object.defineProperty(module, 'exports', {
- get: () => {
- return wasmBuffer
- ? wasmBuffer
- : (wasmBuffer = Buffer.from(wasmBase64, 'base64'))
- }
-})
diff --git a/vanilla/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js b/vanilla/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js
deleted file mode 100644
index 4508cb1..0000000
--- a/vanilla/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict'
-
-const { Buffer } = require('node:buffer')
-
-const wasmBase64 = 'AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAn9/AGABfwBgBH9/f38Bf2AAAGADf39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQAEA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAAzU0BQYAAAMAAAAAAAADAQMAAwMDAAACAAAAAAICAgICAgICAgIBAQEBAQEBAQEBAwAAAwAAAAQFAXABExMFAwEAAgYIAX8BQcDZBAsHxQcoBm1lbW9yeQIAC19pbml0aWFsaXplAAgZX19pbmRpcmVjdF9mdW5jdGlvbl90YWJsZQEAC2xsaHR0cF9pbml0AAkYbGxodHRwX3Nob3VsZF9rZWVwX2FsaXZlADcMbGxodHRwX2FsbG9jAAsGbWFsbG9jADkLbGxodHRwX2ZyZWUADARmcmVlAAwPbGxodHRwX2dldF90eXBlAA0VbGxodHRwX2dldF9odHRwX21ham9yAA4VbGxodHRwX2dldF9odHRwX21pbm9yAA8RbGxodHRwX2dldF9tZXRob2QAEBZsbGh0dHBfZ2V0X3N0YXR1c19jb2RlABESbGxodHRwX2dldF91cGdyYWRlABIMbGxodHRwX3Jlc2V0ABMObGxodHRwX2V4ZWN1dGUAFBRsbGh0dHBfc2V0dGluZ3NfaW5pdAAVDWxsaHR0cF9maW5pc2gAFgxsbGh0dHBfcGF1c2UAFw1sbGh0dHBfcmVzdW1lABgbbGxodHRwX3Jlc3VtZV9hZnRlcl91cGdyYWRlABkQbGxodHRwX2dldF9lcnJubwAaF2xsaHR0cF9nZXRfZXJyb3JfcmVhc29uABsXbGxodHRwX3NldF9lcnJvcl9yZWFzb24AHBRsbGh0dHBfZ2V0X2Vycm9yX3BvcwAdEWxsaHR0cF9lcnJub19uYW1lAB4SbGxodHRwX21ldGhvZF9uYW1lAB8SbGxodHRwX3N0YXR1c19uYW1lACAabGxodHRw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AXRrQT5qIQILIAEgAjYCHCABQgA3AhAgAkECdEH01wBqIQACQEHI1QAoAgAiA0EBIAJ0IgdxRQRAIAAgATYCAEHI1QAgAyAHcjYCACABIAA2AhggASABNgIIIAEgATYCDAwBCyAEQRkgAkEBdmtBACACQR9HG3QhAiAAKAIAIQACQANAIAAiAygCBEF4cSAERg0BIAJBHXYhACACQQF0IQIgAyAAQQRxakEQaiIHKAIAIgANAAsgByABNgIAIAEgAzYCGCABIAE2AgwgASABNgIIDAELIAMoAggiACABNgIMIAMgATYCCCABQQA2AhggASADNgIMIAEgADYCCAtB5NUAQeTVACgCAEEBayIAQX8gABs2AgALCwcAIAAtACgLBwAgAC0AKgsHACAALQArCwcAIAAtACkLBwAgAC8BNAsHACAALQAwC0ABBH8gACgCGCEBIAAvAS4hAiAALQAoIQMgACgCOCEEIAAQOCAAIAQ2AjggACADOgAoIAAgAjsBLiAAIAE2AhgLhocCAwd/A34BeyABIAJqIQQCQCAAIgMoAgwiAA0AIAMoAgQEQCADIAE2AgQLIwBBEGsiCSQAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAC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DAgLQeMBDAcLQdcBDAYLQdgBDAULQdkBDAQLQdoBDAMLQdsBDAILQd0BDAELQdwBCyECA0ACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAMCfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAAn8CQAJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAwJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAC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A0EXNgIcIAMgADYCDCADIAFBAWo2AhQM+AILIABBFUYNI0EAIQIgA0EANgIcIAMgATYCFCADQdYMNgIQIANBIzYCDAz3AgsgAygCBCEAQQAhAiADQQA2AgQgAyAAIAEQNCIARQRAIAFBAWohAQwdCyADQRk2AhwgAyAANgIMIAMgAUEBajYCFAz2AgsgAygCBCEAQQAhAiADQQA2AgQgAyAAIAEQNCIARQRAIAFBAWohAQzvAgsgA0EaNgIcIAMgADYCDCADIAFBAWo2AhQM9QILIABBFUYNH0EAIQIgA0EANgIcIAMgATYCFCADQdAPNgIQIANBIjYCDAz0AgsgAygCBCEAIANBADYCBCADIAAgARAzIgBFBEAgAUEBaiEBDBsLIANBHDYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgzzAgsgAygCBCEAIANBADYCBCADIAAgARAzIgBFBEAgAUEBaiEBDOsCCyADQR02AhwgAyAANgIMIAMgAUEBajYCFEEAIQIM8gILIABBO0cNASABQQFqIQELQSYhAgzXAgtBACECIANBADYCHCADIAE2AhQgA0GfFTYCECADQQw2AgwM7wILIAEgBEcEQANAIAEtAABBIEcNhAIgBCABQQFqIgFHDQALQSwhAgzvAgtBLCECDO4CCyABIARGBEBBNCECDO4CCwJAAkADQAJAIAEtAABBCmsOBAIAAAMACyAEIAFBAWoiAUcNAAtBNCECDO8CCyADKAIEIQAgA0EANgIEIAMgACABEDEiAEUNnwIgA0EyNgIcIAMgATYCFCADIAA2AgxBACECDO4CCyADKAIEIQAgA0EANgIEIAMgACABEDEiAEUEQCABQQFqIQEMnwILIANBMjYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgztAgsgASAERwRAAkADQCABLQAAQTBrIgBB/wFxQQpPBEBBOiECDNcCCyADKQMgIgtCmbPmzJmz5swZVg0BIAMgC0IKfiIKNwMgIAogAK1C/wGDIgtCf4VWDQEgAyAKIAt8NwMgIAQgAUEBaiIBRw0AC0HAACECDO4CCyADKAIEIQAgA0EANgIEIAMgACABQQFqIgEQMSIADRcM4gILQcAAIQIM7AILIAEgBEYEQEHJACECDOwCCwJAA0ACQCABLQAAQQlrDhgAAqICogKpAqICogKiAqICogKiAqICogKiAqICogKiAqICogKiAqICogKiAgCiAgsgBCABQQFqIgFHDQALQckAIQIM7AILIAFBAWohASADQS9qLQAAQQFxDaUCIANBADYCHCADIAE2AhQgA0GXEDYCECADQQo2AgxBACECDOsCCyABIARHBEADQCABLQAAQSBHDRUgBCABQQFqIgFHDQALQfgAIQIM6wILQfgAIQIM6gILIANBAjoAKAw4C0EAIQIgA0EANgIcIANBvws2AhAgA0ECNgIMIAMgAUEBajYCFAzoAgtBACECDM4CC0ENIQIMzQILQRMhAgzMAgtBFSECDMsCC0EWIQIMygILQRghAgzJAgtBGSECDMgCC0EaIQIMxwILQRshAgzGAgtBHCECDMUCC0EdIQIMxAILQR4hAgzDAgtBHyECDMICC0EgIQIMwQILQSIhAgzAAgtBIyECDL8CC0ElIQIMvgILQeUAIQIMvQILIANBPTYCHCADIAE2AhQgAyAANgIMQQAhAgzVAgsgA0EbNgIcIAMgATYCFCADQaQcNgIQIANBFTYCDEEAIQIM1AILIANBIDYCHCADIAE2AhQgA0GYGjYCECADQRU2AgxBACECDNMCCyADQRM2AhwgAyABNgIUIANBmBo2AhAgA0EVNgIMQQAhAgzSAgsgA0ELNgIcIAMgATYCFCADQZgaNgIQIANBFTYCDEEAIQIM0QILIANBEDYCHCADIAE2AhQgA0GYGjYCECADQRU2AgxBACECDNACCyADQSA2AhwgAyABNgIUIANBpBw2AhAgA0EVNgIMQQAhAgzPAgsgA0ELNgIcIAMgATYCFCADQaQcNgIQIANBFTYCDEEAIQIMzgILIANBDDYCHCADIAE2AhQgA0GkHDYCECADQRU2AgxBACECDM0CC0EAIQIgA0EANgIcIAMgATYCFCADQd0ONgIQIANBEjYCDAzMAgsCQANAAkAgAS0AAEEKaw4EAAICAAILIAQgAUEBaiIBRw0AC0H9ASECDMwCCwJAAkAgAy0ANkEBRw0AQQAhAAJAIAMoAjgiAkUNACACKAJgIgJFDQAgAyACEQAAIQALIABFDQAgAEEVRw0BIANB/AE2AhwgAyABNgIUIANB3Bk2AhAgA0EVNgIMQQAhAgzNAgtB3AEhAgyzAgsgA0EANgIcIAMgATYCFCADQfkLNgIQIANBHzYCDEEAIQIMywILAkACQCADLQAoQQFrDgIEAQALQdsBIQIMsgILQdQBIQIMsQILIANBAjoAMUEAIQACQCADKAI4IgJFDQAgAigCACICRQ0AIAMgAhEAACEACyAARQRAQd0BIQIMsQILIABBFUcEQCADQQA2AhwgAyABNgIUIANBtAw2AhAgA0EQNgIMQQAhAgzKAgsgA0H7ATYCHCADIAE2AhQgA0GBGjYCECADQRU2AgxBACECDMkCCyABIARGBEBB+gEhAgzJAgsgAS0AAEHIAEYNASADQQE6ACgLQcABIQIMrgILQdoBIQIMrQILIAEgBEcEQCADQQw2AgggAyABNgIEQdkBIQIMrQILQfkBIQIMxQILIAEgBEYEQEH4ASECDMUCCyABLQAAQcgARw0EIAFBAWohAUHYASECDKsCCyABIARGBEBB9wEhAgzEAgsCQAJAIAEtAABBxQBrDhAABQUFBQUFBQUFBQUFBQUBBQsgAUEBaiEBQdYBIQIMqwILIAFBAWohAUHXASECDKoCC0H2ASECIAEgBEYNwgIgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABButUAai0AAEcNAyAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMwwILIAMoAgQhACADQgA3AwAgAyAAIAZBAWoiARAuIgBFBEBB4wEhAgyqAgsgA0H1ATYCHCADIAE2AhQgAyAANgIMQQAhAgzCAgtB9AEhAiABIARGDcECIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQbjVAGotAABHDQIgAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADMICCyADQYEEOwEoIAMoAgQhACADQgA3AwAgAyAAIAZBAWoiARAuIgANAwwCCyADQQA2AgALQQAhAiADQQA2AhwgAyABNgIUIANB5R82AhAgA0EINgIMDL8CC0HVASECDKUCCyADQfMBNgIcIAMgATYCFCADIAA2AgxBACECDL0CC0EAIQACQCADKAI4IgJFDQAgAigCQCICRQ0AIAMgAhEAACEACyAARQ1uIABBFUcEQCADQQA2AhwgAyABNgIUIANBgg82AhAgA0EgNgIMQQAhAgy9AgsgA0GPATYCHCADIAE2AhQgA0HsGzYCECADQRU2AgxBACECDLwCCyABIARHBEAgA0ENNgIIIAMgATYCBEHTASECDKMCC0HyASECDLsCCyABIARGBEBB8QEhAgy7AgsCQAJAAkAgAS0AAEHIAGsOCwABCAgICAgICAgCCAsgAUEBaiEBQdABIQIMowILIAFBAWohAUHRASECDKICCyABQQFqIQFB0gEhAgyhAgtB8AEhAiABIARGDbkCIAMoAgAiACAEIAFraiEGIAEgAGtBAmohBQNAIAEtAAAgAEG11QBqLQAARw0EIABBAkYNAyAAQQFqIQAgBCABQQFqIgFHDQALIAMgBjYCAAy5AgtB7wEhAiABIARGDbgCIAMoAgAiACAEIAFraiEGIAEgAGtBAWohBQNAIAEtAAAgAEGz1QBqLQAARw0DIABBAUYNAiAAQQFqIQAgBCABQQFqIgFHDQALIAMgBjYCAAy4AgtB7gEhAiABIARGDbcCIAMoAgAiACAEIAFraiEGIAEgAGtBAmohBQNAIAEtAAAgAEGw1QBqLQAARw0CIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBjYCAAy3AgsgAygCBCEAIANCADcDACADIAAgBUEBaiIBECsiAEUNAiADQewBNgIcIAMgATYCFCADIAA2AgxBACECDLYCCyADQQA2AgALIAMoAgQhACADQQA2AgQgAyAAIAEQKyIARQ2cAiADQe0BNgIcIAMgATYCFCADIAA2AgxBACECDLQCC0HPASECDJoCC0EAIQACQCADKAI4IgJFDQAgAigCNCICRQ0AIAMgAhEAACEACwJAIAAEQCAAQRVGDQEgA0EANgIcIAMgATYCFCADQeoNNgIQIANBJjYCDEEAIQIMtAILQc4BIQIMmgILIANB6wE2AhwgAyABNgIUIANBgBs2AhAgA0EVNgIMQQAhAgyyAgsgASAERgRAQesBIQIMsgILIAEtAABBL0YEQCABQQFqIQEMAQsgA0EANgIcIAMgATYCFCADQbI4NgIQIANBCDYCDEEAIQIMsQILQc0BIQIMlwILIAEgBEcEQCADQQ42AgggAyABNgIEQcwBIQIMlwILQeoBIQIMrwILIAEgBEYEQEHpASECDK8CCyABLQAAQTBrIgBB/wFxQQpJBEAgAyAAOgAqIAFBAWohAUHLASECDJYCCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNlwIgA0HoATYCHCADIAE2AhQgAyAANgIMQQAhAgyuAgsgASAERgRAQecBIQIMrgILAkAgAS0AAEEuRgRAIAFBAWohAQwBCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNmAIgA0HmATYCHCADIAE2AhQgAyAANgIMQQAhAgyuAgtBygEhAgyUAgsgASAERgRAQeUBIQIMrQILQQAhAEEBIQVBASEHQQAhAgJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAIAEtAABBMGsOCgoJAAECAwQFBggLC0ECDAYLQQMMBQtBBAwEC0EFDAMLQQYMAgtBBwwBC0EICyECQQAhBUEAIQcMAgtBCSECQQEhAEEAIQVBACEHDAELQQAhBUEBIQILIAMgAjoAKyABQQFqIQECQAJAIAMtAC5BEHENAAJAAkACQCADLQAqDgMBAAIECyAHRQ0DDAILIAANAQwCCyAFRQ0BCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNAiADQeIBNgIcIAMgATYCFCADIAA2AgxBACECDK8CCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNmgIgA0HjATYCHCADIAE2AhQgAyAANgIMQQAhAgyuAgsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDZgCIANB5AE2AhwgAyABNgIUIAMgADYCDAytAgtByQEhAgyTAgtBACEAAkAgAygCOCICRQ0AIAIoAkQiAkUNACADIAIRAAAhAAsCQCAABEAgAEEVRg0BIANBADYCHCADIAE2AhQgA0GkDTYCECADQSE2AgxBACECDK0CC0HIASECDJMCCyADQeEBNgIcIAMgATYCFCADQdAaNgIQIANBFTYCDEEAIQIMqwILIAEgBEYEQEHhASECDKsCCwJAIAEtAABBIEYEQCADQQA7ATQgAUEBaiEBDAELIANBADYCHCADIAE2AhQgA0GZETYCECADQQk2AgxBACECDKsCC0HHASECDJECCyABIARGBEBB4AEhAgyqAgsCQCABLQAAQTBrQf8BcSICQQpJBEAgAUEBaiEBAkAgAy8BNCIAQZkzSw0AIAMgAEEKbCIAOwE0IABB/v8DcSACQf//A3NLDQAgAyAAIAJqOwE0DAILQQAhAiADQQA2AhwgAyABNgIUIANBlR42AhAgA0ENNgIMDKsCCyADQQA2AhwgAyABNgIUIANBlR42AhAgA0ENNgIMQQAhAgyqAgtBxgEhAgyQAgsgASAERgRAQd8BIQIMqQILAkAgAS0AAEEwa0H/AXEiAkEKSQRAIAFBAWohAQJAIAMvATQiAEGZM0sNACADIABBCmwiADsBNCAAQf7/A3EgAkH//wNzSw0AIAMgACACajsBNAwCC0EAIQIgA0EANgIcIAMgATYCFCADQZUeNgIQIANBDTYCDAyqAgsgA0EANgIcIAMgATYCFCADQZUeNgIQIANBDTYCDEEAIQIMqQILQcUBIQIMjwILIAEgBEYEQEHeASECDKgCCwJAIAEtAABBMGtB/wFxIgJBCkkEQCABQQFqIQECQCADLwE0IgBBmTNLDQAgAyAAQQpsIgA7ATQgAEH+/wNxIAJB//8Dc0sNACADIAAgAmo7ATQMAgtBACECIANBADYCHCADIAE2AhQgA0GVHjYCECADQQ02AgwMqQILIANBADYCHCADIAE2AhQgA0GVHjYCECADQQ02AgxBACECDKgCC0HEASECDI4CCyABIARGBEBB3QEhAgynAgsCQAJAAkACQCABLQAAQQprDhcCAwMAAwMDAwMDAwMDAwMDAwMDAwMDAQMLIAFBAWoMBQsgAUEBaiEBQcMBIQIMjwILIAFBAWohASADQS9qLQAAQQFxDQggA0EANgIcIAMgATYCFCADQY0LNgIQIANBDTYCDEEAIQIMpwILIANBADYCHCADIAE2AhQgA0GNCzYCECADQQ02AgxBACECDKYCCyABIARHBEAgA0EPNgIIIAMgATYCBEEBIQIMjQILQdwBIQIMpQILAkACQANAAkAgAS0AAEEKaw4EAgAAAwALIAQgAUEBaiIBRw0AC0HbASECDKYCCyADKAIEIQAgA0EANgIEIAMgACABEC0iAEUEQCABQQFqIQEMBAsgA0HaATYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgylAgsgAygCBCEAIANBADYCBCADIAAgARAtIgANASABQQFqCyEBQcEBIQIMigILIANB2QE2AhwgAyAANgIMIAMgAUEBajYCFEEAIQIMogILQcIBIQIMiAILIANBL2otAABBAXENASADQQA2AhwgAyABNgIUIANB5Bw2AhAgA0EZNgIMQQAhAgygAgsgASAERgRAQdkB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CyADIAU2AgAMkAILIANBADYCACAGQQFqIQFBGgw8C0HKASECIAEgBEYNjgIgAygCACIAIAQgAWtqIQUgASAAa0EDaiEGAkADQCABLQAAIABBjdUAai0AAEcNOyAAQQNGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMjwILIANBADYCACAGQQFqIQFBIQw7CyABIARGBEBByQEhAgyOAgsCQAJAIAEtAABBwQBrDhQAPT09PT09PT09PT09PT09PT09AT0LIAFBAWohAUGtASECDPUBCyABQQFqIQFBsAEhAgz0AQsgASAERgRAQcgBIQIMjQILAkACQCABLQAAQdUAaw4LADw8PDw8PDw8PAE8CyABQQFqIQFBrgEhAgz0AQsgAUEBaiEBQa8BIQIM8wELQccBIQIgASAERg2LAiADKAIAIgAgBCABa2ohBSABIABrQQhqIQYCQANAIAEtAAAgAEGE1QBqLQAARw04IABBCEYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyMAgsgA0EANgIAIAZBAWohAUEqDDgLIAEgBEYEQEHGASECDIsCCyABLQAAQdAARw04IAFBAWohAUElDDcLQcUBIQIgASAERg2JAiADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGB1QBqLQAARw02IABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyKAgsgA0EANgIAIAZBAWohAUEODDYLIAEgBEYEQEHEASECDIkCCyABLQAAQcUARw02IAFBAWohAUGrASECDO8BCyABIARGBEBBwwEhAgyIAgsCQAJAAkACQCABLQAAQcIAaw4PAAECOTk5OTk5OTk5OTkDOQsgAUEBaiEBQacBIQIM8QELIAFBAWohAUGoASECDPABCyABQQFqIQFBqQEhAgzvAQsgAUEBaiEBQaoBIQIM7gELQcIBIQIgASAERg2GAiADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEH+1ABqLQAARw0zIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyHAgsgA0EANgIAIAZBAWohAUEUDDMLQcEBIQIgASAERg2FAiADKAIAIgAgBCABa2ohBSABIABrQQRqIQYCQANA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BTYCAAzsAQsgA0EANgIAIAZBAWohAUEXDBgLQaYBIQIgASAERg3qASADKAIAIgAgBCABa2ohBSABIABrQQVqIQYCQANAIAEtAAAgAEHB1ABqLQAARw0XIABBBUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzrAQsgA0EANgIAIAZBAWohAUEYDBcLIAEgBEYEQEGlASECDOoBCwJAAkAgAS0AAEHJAGsOBwAZGRkZGQEZCyABQQFqIQFBiwEhAgzRAQsgAUEBaiEBQYwBIQIM0AELQaQBIQIgASAERg3oASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEGm1QBqLQAARw0VIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzpAQsgA0EANgIAIAZBAWohAUEJDBULQaMBIQIgASAERg3nASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEGk1QBqLQAARw0UIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzoAQsgA0EANgIAIAZBAWohAUEfDBQLQaIBIQIgASAERg3mASADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEG+1ABqLQAARw0TIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAznAQsgA0EANgIAIAZBAWohAUECDBMLQaEBIQIgASAERg3lASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYDQCABLQAAIABBvNQAai0AAEcNESAAQQFGDQIgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM5QELIAEgBEYEQEGgASECDOUBC0EBIAEtAABB3wBHDREaIAFBAWohAUGHASECDMsBCyADQQA2AgAgBkEBaiEBQYgBIQIMygELQZ8BIQIgASAERg3iASADKAIAIgAgBCABa2ohBSABIABrQQhqIQYCQANAIAEtAAAgAEGE1QBqLQAARw0PIABBCEYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzjAQsgA0EANgIAIAZBAWohAUEpDA8LQZ4BIQIgASAERg3hASADKAIAIgAgBCABa2ohBSABIABrQQNqIQYCQANAIAEtAAAgAEG41ABqLQAARw0OIABBA0YNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAziAQsgA0EANgIAIAZBAWohAUEtDA4LIAEgBEYEQEGdASECDOEBCyABLQAAQcUARw0OIAFBAWohAUGEASECDMcBCyABIARGBEBBnAEhAgzgAQsCQAJAIAEtAABBzABrDggADw8PDw8PAQ8LIAFBAWohAUGCASECDMcBCyABQQFqIQFBgwEhAgzGAQtBmwEhAiABIARGDd4BIAMoAgAiACAEIAFraiEFIAEgAGtBBGohBgJAA0AgAS0AACAAQbPUAGotAABHDQsgAEEERg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADN8BCyADQQA2AgAgBkEBaiEBQSMMCwtBmgEhAiABIARGDd0BIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQbDUAGotAABHDQogAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADN4BCyADQQA2AgAgBkEBaiEBQQAMCgsgASAERgRAQZkBIQIM3QELAkACQCABLQAAQcgAaw4IAAwMDAwMDAEMCyABQQFqIQFB/QAhAgzEAQsgAUEBaiEBQYABIQIMwwELIAEgBEYEQEGYASECDNwBCwJAAkAgAS0AAEHOAGsOAwALAQsLIAFBAWohAUH+ACECDMMBCyABQQFqIQFB/wAhAgzCAQsgASAERgRAQZcBIQIM2wELIAEtAABB2QBHDQggAUEBaiEBQQgMBwtBlgEhAiABIARGDdkBIAMoAgAiACAEIAFraiEFIAEgAGtBA2ohBgJAA0AgAS0AACAAQazUAGotAABHDQYgAEEDRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADNoBCyADQQA2AgAgBkEBaiEBQQUMBgtBlQEhAiABIARGDdgBIAMoAgAiACAEIAFraiEFIAEgAGtBBWohBgJAA0AgAS0AACAAQabUAGotAABHDQUgAEEFRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADNkBCyADQQA2AgAgBkEBaiEBQRYMBQtBlAEhAiABIARGDdcBIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQaHVAGotAABHDQQgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADNgBCyADQQA2AgAgBkEBaiEBQRAMBAsgASAERgRAQZMBIQIM1wELAkACQCABLQAAQcMAaw4MAAYGBgYGBgYGBgYBBgsgAUEBaiEBQfkAIQIMvgELIAFBAWohAUH6ACECDL0BC0GSASECIAEgBEYN1QEgAygCACIAIAQgAWtqIQUgASAAa0EFaiEGAkADQCABLQAAIABBoNQAai0AAEcNAiAAQQVGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM1gELIANBADYCACAGQQFqIQFBJAwCCyADQQA2AgAMAgsgASAERgRAQZEBIQIM1AELIAEtAABBzABHDQEgAUEBaiEBQRMLOgApIAMoAgQhACADQQA2AgQgAyAAIAEQLiIADQIMAQtBACECIANBADYCHCADIAE2AhQgA0H+HzYCECADQQY2AgwM0QELQfgAIQIMtwELIANBkAE2AhwgAyABNgIUIAMgADYCDEEAIQIMzwELQQAhAAJAIAMoAjgiAkUNACACKAJAIgJFDQAgAyACEQAAIQALIABFDQAgAEEVRg0BIANBADYCHCADIAE2AhQgA0GCDzYCECADQSA2AgxBACECDM4BC0H3ACECDLQBCyADQY8BNgIcIAMgATYCFCADQewbNgIQIANBFTYCDEEAIQIMzAELIAEgBEYEQEGPASECDMwBCwJAIAEtAABBIEYEQCABQQFqIQEMAQsgA0EANgIcIAMgATYCFCADQZsfNgIQIANBBjYCDEEAIQIMzAELQQIhAgyyAQsDQCABLQAAQSBHDQIgBCABQQFqIgFHDQALQY4BIQIMygELIAEgBEYEQEGNASECDMoBCwJAIAEtAABBCWsOBEoAAEoAC0H1ACECDLABCyADLQApQQVGBEBB9gAhAgywAQtB9AAhAgyvAQsgASAERgRAQYwBIQIMyAELIANBEDYCCCADIAE2AgQMCgsgASAERgRAQYsBIQIMxwELAkAgAS0AAEEJaw4ERwAARwALQfMAIQIMrQELIAEgBEcEQCADQRA2AgggAyABNgIEQfEAIQIMrQELQYoBIQIMxQELAkAgASAERwRAA0AgAS0AAEGg0ABqLQAAIgBBA0cEQAJAIABBAWsOAkkABAtB8AAhAgyvAQsgBCABQQFqIgFHDQALQYgBIQIMxgELQYgBIQIMxQELIANBADYCHCADIAE2AhQgA0HbIDYCECADQQc2AgxBACECDMQBCyABIARGBEBBiQEhAgzEAQsCQAJAAkAgAS0AAEGg0gBqLQAAQQFrDgNGAgABC0HyACECDKwBCyADQQA2AhwgAyABNgIUIANBtBI2AhAgA0EHNgIMQQAhAgzEAQtB6gAhAgyqAQsgASAERwRAIAFBAWohAUHvACECDKoBC0GHASECDMIBCyAEIAEiAEYEQEGGASECDMIBCyAALQAAIgFBL0YEQCAAQQFqIQFB7gAhAgypAQsgAUEJayICQRdLDQEgACEBQQEgAnRBm4CABHENQQwBCyAEIAEiAEYEQEGFASECDMEBCyAALQAAQS9HDQAgAEEBaiEBDAMLQQAhAiADQQA2AhwgAyAANgIUIANB2yA2AhAgA0EHNgIMDL8BCwJAAkACQAJAAkADQCABLQAAQaDOAGotAAAiAEEFRwRAAkACQCAAQQFrDghHBQYHCAAEAQgLQesAIQIMrQELIAFBAWohAUHtACECDKwBCyAEIAFBAWoiAUcNAAtBhAEhAgzDAQsgAUEBagwUCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNHiADQdsANgIcIAMgATYCFCADIAA2AgxBACECDMEBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNHiADQd0ANgIcIAMgATYCFCADIAA2AgxBACECDMABCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNHiADQfoANgIcIAMgATYCFCADIAA2AgxBACECDL8BCyADQQA2AhwgAyABNgIUIANB+Q82AhAgA0EHNgIMQQAhAgy+AQsgASAERgRAQYMBIQIMvgELAkAgAS0AAEGgzgBqLQAAQQFrDgg+BAUGAAgCAwcLIAFBAWohAQtBAyECDKMBCyABQQFqDA0LQQAhAiADQQA2AhwgA0HREjYCECADQQc2AgwgAyABQQFqNgIUDLoBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNFiADQdsANgIcIAMgATYCFCADIAA2AgxBACECDLkBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNFiADQd0ANgIcIAMgATYCFCADIAA2AgxBACECDLgBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNFiADQfoANgIcIAMgATYCFCADIAA2AgxBACECDLcBCyADQQA2AhwgAyABNgIUIANB+Q82AhAgA0EHNgIMQQAhAgy2AQtB7AAhAgycAQsgASAERgRAQYIBIQIMtQELIAFBAWoMAgsgASAERgRAQYEBIQIMtAELIAFBAWoMAQsgASAERg0BIAFBAWoLIQFBBCECDJgBC0GAASECDLABCwNAIAEtAABBoMwAai0AACIAQQJHBEAgAEEBRwRAQekAIQIMmQELDDELIAQgAUEBaiIBRw0AC0H/ACECDK8BCyABIARGBEBB/gAhAgyvAQsCQCABLQAAQQlrDjcvAwYvBAYGBgYGBgYGBgYGBgYGBgYGBgUGBgIGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYABgsgAUEBagshAUEFIQIMlAELIAFBAWoMBgsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQggA0HbADYCHCADIAE2AhQgAyAANgIMQQAhAgyrAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQggA0HdADYCHCADIAE2AhQgAyAANgIMQQAhAgyqAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQggA0H6ADYCHCADIAE2AhQgAyAANgIMQQAhAgypAQsgA0EANgIcIAMgATYCFCADQY0UNgIQIANBBzYCDEEAIQIMqAELAkACQAJAAkADQCABLQAAQaDKAGotAAAiAEEFRwRAAkAgAEEBaw4GLgMEBQYABgtB6AAhAgyUAQsgBCABQQFqIgFHDQALQf0AIQIMqwELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0HIANB2wA2AhwgAyABNgIUIAMgADYCDEEAIQIMqgELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0HIANB3QA2AhwgAyABNgIUIAMgADYCDEEAIQIMqQELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0HIANB+gA2AhwgAyABNgIUIAMgADYCDEEAIQIMqAELIANBADYCHCADIAE2AhQgA0HkCDYCECADQQc2AgxBACECDKcBCyABIARGDQEgAUEBagshAUEGIQIMjAELQfwAIQIMpAELAkACQAJAAkADQCABLQAAQaDIAGotAAAiAEEFRwRAIABBAWsOBCkCAwQFCyAEIAFBAWoiAUcNAAtB+wAhAgynAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQMgA0HbADYCHCADIAE2AhQgAyAANgIMQQAhAgymAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQMgA0HdADYCHCADIAE2AhQgAyAANgIMQQAhAgylAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQMgA0H6ADYCHCADIAE2AhQgAyAANgIMQQAhAgykAQsgA0EANgIcIAMgATYCFCADQbwKNgIQIANBBzYCDEEAIQIMowELQc8AIQIMiQELQdEAIQIMiAELQecAIQIMhwELIAEgBEYEQEH6ACECDKABCwJAIAEtAABBCWsOBCAAACAACyABQQFqIQFB5gAhAgyGAQsgASAERgRAQfkAIQIMnwELAkAgAS0AAEEJaw4EHwAAHwALQQAhAAJAIAMoAjgiAkUNACACKAI4IgJFDQAgAyACEQAAIQALIABFBEBB4gEhAgyGAQsgAEEVRwRAIANBADYCHCADIAE2AhQgA0HJDTYCECADQRo2AgxBACECDJ8BCyADQfgANgIcIAMgATYCFCADQeoaNgIQIANBFTYCDEEAIQIMngELIAEgBEcEQCADQQ02AgggAyABNgIEQeQAIQIMhQELQfcAIQIMnQELIAEgBEYEQEH2ACECDJ0BCwJAAkACQCABLQAAQcgAaw4LAAELCwsLCwsLCwILCyABQQFqIQFB3QAhAgyFAQsgAUEBaiEBQeAAIQIMhAELIAFBAWohAUHjACECDIMBC0H1ACECIAEgBEYNmwEgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBtdUAai0AAEcNCCAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMnAELIAMoAgQhACADQgA3AwAgAyAAIAZBAWoiARArIgAEQCADQfQANgIcIAMgATYCFCADIAA2AgxBACECDJwBC0HiACECDIIBC0EAIQACQCADKAI4IgJFDQAgAigCNCICRQ0AIAMgAhEAACEACwJAIAAEQCAAQRVGDQEgA0EANgIcIAMgATYCFCADQeoNNgIQIANBJjYCDEEAIQIMnAELQeEAIQIMggELIANB8wA2AhwgAyABNgIUIANBgBs2AhAgA0EVNgIMQQAhAgyaAQsgAy0AKSIAQSNrQQtJDQkCQCAAQQZLDQBBASAAdEHKAHFFDQAMCgtBACECIANBADYCHCADIAE2AhQgA0HtCTYCECADQQg2AgwMmQELQfIAIQIgASAERg2YASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEGz1QBqLQAARw0FIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyZAQsgAygCBCEAIANCADcDACADIAAgBkEBaiIBECsiAARAIANB8QA2AhwgAyABNgIUIAMgADYCDEEAIQIMmQELQd8AIQIMfwtBACEAAkAgAygCOCICRQ0AIAIoAjQiAkUNACADIAIRAAAhAAsCQCAABEAgAEEVRg0BIANBADYCHCADIAE2AhQgA0HqDTYCECADQSY2AgxBACECDJkBC0HeACECDH8LIANB8AA2AhwgAyABNgIUIANBgBs2AhAgA0EVNgIMQQAhAgyXAQsgAy0AKUEhRg0GIANBADYCHCADIAE2AhQgA0GRCjYCECADQQg2AgxBACECDJYBC0HvACECIAEgBEYNlQEgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBsNUAai0AAEcNAiAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMlgELIAMoAgQhACADQgA3AwAgAyAAIAZBAWoiARArIgBFDQIgA0HtADYCHCADIAE2AhQgAyAANgIMQQAhAgyVAQsgA0EANgIACyADKAIEIQAgA0EANgIEIAMgACABECsiAEUNgAEgA0HuADYCHCADIAE2AhQgAyAANgIMQQAhAgyTAQtB3AAhAgx5C0EAIQACQCADKAI4IgJFDQAgAigCNCICRQ0AIAMgAhEAACEACwJAIAAEQCAAQRVGDQEgA0EANgIcIAMgATYCFCADQeoNNgIQIANBJjYCDEEAIQIMkwELQdsAIQIMeQsgA0HsADYCHCADIAE2AhQgA0GAGzYCECADQRU2AgxBACECDJEBCyADLQApIgBBI0kNACAAQS5GDQAgA0EANgIcIAMgATYCFCADQckJNgIQIANBCDYCDEEAIQIMkAELQdoAIQIMdgsgASAERgRAQesAIQIMjwELAkAgAS0AAEEvRgRAIAFBAWohAQwBCyAD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ciBIVFRQL3gueCByZXF1ZXN0AEludmFsaWQgbWV0aG9kIGZvciBSVFNQL3gueCByZXF1ZXN0AEV4cGVjdGVkIFNPVVJDRSBtZXRob2QgZm9yIElDRS94LnggcmVxdWVzdABJbnZhbGlkIGNoYXIgaW4gdXJsIGZyYWdtZW50IHN0YXJ0AEV4cGVjdGVkIGRvdABTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3N0YXR1cwBJbnZhbGlkIHJlc3BvbnNlIHN0YXR1cwBFeHBlY3RlZCBMRiBhZnRlciBoZWFkZXJzAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMAVXNlciBjYWxsYmFjayBlcnJvcgBgb25fcmVzZXRgIGNhbGxiYWNrIGVycm9yAGBvbl9jaHVua19oZWFkZXJgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2JlZ2luYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlYCBjYWxsYmFjayBlcnJvcgBgb25fc3RhdHVzX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdmVyc2lvbl9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3VybF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3Byb3RvY29sX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWV0aG9kX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX25hbWVgIGNhbGxiYWNrIGVycm9yAFVuZXhwZWN0ZWQgY2hhciBpbiB1cmwgc2VydmVyAEludmFsaWQgaGVhZGVyIHZhbHVlIGNoYXIASW52YWxpZCBoZWFkZXIgZmllbGQgY2hhcgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3ZlcnNpb24ASW52YWxpZCBtaW5vciB2ZXJzaW9uAEludmFsaWQgbWFqb3IgdmVyc2lvbgBFeHBlY3RlZCBzcGFjZSBhZnRlciB2ZXJzaW9uAEV4cGVjdGVkIENSTEYgYWZ0ZXIgdmVyc2lvbgBJbnZhbGlkIEhUVFAgdmVyc2lvbgBJbnZhbGlkIGhlYWRlciB0b2tlbgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3VybABJbnZhbGlkIGNoYXJhY3RlcnMgaW4gdXJsAFVuZXhwZWN0ZWQgc3RhcnQgY2hhciBpbiB1cmwARG91YmxlIEAgaW4gdXJsAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fcHJvdG9jb2wARW1wdHkgQ29udGVudC1MZW5ndGgASW52YWxpZCBjaGFyYWN0ZXIgaW4gQ29udGVudC1MZW5ndGgAVHJhbnNmZXItRW5jb2RpbmcgY2FuJ3QgYmUgcHJlc2VudCB3aXRoIENvbnRlbnQtTGVuZ3RoAER1cGxpY2F0ZSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXIgaW4gdXJsIHBhdGgAQ29udGVudC1MZW5ndGggY2FuJ3QgYmUgcHJlc2VudCB3aXRoIFRyYW5zZmVyLUVuY29kaW5nAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgY2h1bmsgc2l6ZQBFeHBlY3RlZCBMRiBhZnRlciBjaHVuayBzaXplAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIHNpemUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfdmFsdWUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyB2YWx1ZQBVbmV4cGVjdGVkIHdoaXRlc3BhY2UgYWZ0ZXIgaGVhZGVyIHZhbHVlAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgaGVhZGVyIHZhbHVlAE1pc3NpbmcgZXhwZWN0ZWQgTEYgYWZ0ZXIgaGVhZGVyIHZhbHVlAEludmFsaWQgYFRyYW5zZmVyLUVuY29kaW5nYCBoZWFkZXIgdmFsdWUATWlzc2luZyBleHBlY3RlZCBDUiBhZnRlciBjaHVuayBleHRlbnNpb24gdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBxdW90ZSB2YWx1ZQBJbnZhbGlkIHF1b3RlZC1wYWlyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAFBhdXNlZCBieSBvbl9oZWFkZXJzX2NvbXBsZXRlAEludmFsaWQgRU9GIHN0YXRlAG9uX3Jlc2V0IHBhdXNlAG9uX2NodW5rX2hlYWRlciBwYXVzZQBvbl9tZXNzYWdlX2JlZ2luIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl92YWx1ZSBwYXVzZQBvbl9zdGF0dXNfY29tcGxldGUgcGF1c2UAb25fdmVyc2lvbl9jb21wbGV0ZSBwYXVzZQBvbl91cmxfY29tcGxldGUgcGF1c2UAb25fcHJvdG9jb2xfY29tcGxldGUgcGF1c2UAb25fY2h1bmtfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX3ZhbHVlX2NvbXBsZXRlIHBhdXNlAG9uX21lc3NhZ2VfY29tcGxldGUgcGF1c2UAb25fbWV0aG9kX2NvbXBsZXRlIHBhdXNlAG9uX2hlYWRlcl9maWVsZF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19leHRlbnNpb25fbmFtZSBwYXVzZQBVbmV4cGVjdGVkIHNwYWNlIGFmdGVyIHN0YXJ0IGxpbmUATWlzc2luZyBleHBlY3RlZCBDUiBhZnRlciByZXNwb25zZSBsaW5lAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fY2h1bmtfZXh0ZW5zaW9uX25hbWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBuYW1lAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgY2h1bmsgZXh0ZW5zaW9uIG5hbWUASW52YWxpZCBzdGF0dXMgY29kZQBQYXVzZSBvbiBDT05ORUNUL1VwZ3JhZGUAUGF1c2Ugb24gUFJJL1VwZ3JhZGUARXhwZWN0ZWQgSFRUUC8yIENvbm5lY3Rpb24gUHJlZmFjZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX21ldGhvZABFeHBlY3RlZCBzcGFjZSBhZnRlciBtZXRob2QAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfZmllbGQAUGF1c2VkAEludmFsaWQgd29yZCBlbmNvdW50ZXJlZABJbnZhbGlkIG1ldGhvZCBlbmNvdW50ZXJlZABNaXNzaW5nIGV4cGVjdGVkIENSIGFmdGVyIGNodW5rIGRhdGEARXhwZWN0ZWQgTEYgYWZ0ZXIgY2h1bmsgZGF0YQBVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNjaGVtYQBSZXF1ZXN0IGhhcyBpbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AARGF0YSBhZnRlciBgQ29ubmVjdGlvbjogY2xvc2VgAFNXSVRDSF9QUk9YWQBVU0VfUFJPWFkATUtBQ1RJVklUWQBVTlBST0NFU1NBQkxFX0VOVElUWQBRVUVSWQBDT1BZAE1PVkVEX1BFUk1BTkVOVExZAFRPT19FQVJMWQBOT1RJRlkARkFJTEVEX0RFUEVOREVOQ1kAQkFEX0dBVEVXQVkAUExBWQBQVVQAQ0hFQ0tPVVQAR0FURVdBWV9USU1FT1VUAFJFUVVFU1RfVElNRU9VVABORVRXT1JLX0NPTk5FQ1RfVElNRU9VVABDT05ORUNUSU9OX1RJTUVPVVQATE9HSU5fVElNRU9VVABORVRXT1JLX1JFQURfVElNRU9VVABQT1NUAE1JU0RJUkVDVEVEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9SRVFVRVNUAENMSUVOVF9DTE9TRURfTE9BRF9CQUxBTkNFRF9SRVFVRVNUAEJBRF9SRVFVRVNUAEhUVFBfUkVRVUVTVF9TRU5UX1RPX0hUVFBTX1BPUlQAUkVQT1JUAElNX0FfVEVBUE9UAFJFU0VUX0NPTlRFTlQATk9fQ09OVEVOVABQQVJUSUFMX0NPTlRFTlQASFBFX0lOVkFMSURfQ09OU1RBTlQASFBFX0NCX1JFU0VUAEdFVABIUEVfU1RSSUNUAENPTkZMSUNUAFRFTVBPUkFSWV9SRURJUkVDVABQRVJNQU5FTlRfUkVESVJFQ1QAQ09OTkVDVABNVUxUSV9TVEFUVVMASFBFX0lOVkFMSURfU1RBVFVTAFRPT19NQU5ZX1JFUVVFU1RTAEVBUkxZX0hJTlRTAFVOQVZBSUxBQkxFX0ZPUl9MRUdBTF9SRUFTT05TAE9QVElPTlMAU1dJVENISU5HX1BST1RPQ09MUwBWQVJJQU5UX0FMU09fTkVHT1RJQVRFUwBNVUxUSVBMRV9DSE9JQ0VTAElOVEVSTkFMX1NFUlZFUl9FUlJPUgBXRUJfU0VSVkVSX1VOS05PV05fRVJST1IAUkFJTEdVTl9FUlJPUgBJREVOVElUWV9QUk9WSURFUl9BVVRIRU5USUNBVElPTl9FUlJPUgBTU0xfQ0VSVElGSUNBVEVfRVJST1IASU5WQUxJRF9YX0ZPUldBUkRFRF9GT1IAU0VUX1BBUkFNRVRFUgBHRVRfUEFSQU1FVEVSAEhQRV9VU0VSAFNFRV9PVEhFUgBIUEVfQ0JfQ0hVTktfSEVBREVSAEV4cGVjdGVkIExGIGFmdGVyIENSAE1LQ0FMRU5EQVIAU0VUVVAAV0VCX1NFUlZFUl9JU19ET1dOAFRFQVJET1dOAEhQRV9DTE9TRURfQ09OTkVDVElPTgBIRVVSSVNUSUNfRVhQSVJBVElPTgBESVNDT05ORUNURURfT1BFUkFUSU9OAE5PTl9BVVRIT1JJVEFUSVZFX0lORk9STUFUSU9OAEhQRV9JTlZBTElEX1ZFUlNJT04ASFBFX0NCX01FU1NBR0VfQkVHSU4AU0lURV9JU19GUk9aRU4ASFBFX0lOVkFMSURfSEVBREVSX1RPS0VOAElOVkFMSURfVE9LRU4ARk9SQklEREVOAEVOSEFOQ0VfWU9VUl9DQUxNAEhQRV9JTlZBTElEX1VSTABCTE9DS0VEX0JZX1BBUkVOVEFMX0NPTlRST0wATUtDT0wAQUNMAEhQRV9JTlRFUk5BTABSRVFVRVNUX0hFQURFUl9GSUVMRFNfVE9PX0xBUkdFX1VOT0ZGSUNJQUwASFBFX09LAFVOTElOSwBVTkxPQ0sAUFJJAFJFVFJZX1dJVEgASFBFX0lOVkFMSURfQ09OVEVOVF9MRU5HVEgASFBFX1VORVhQRUNURURfQ09OVEVOVF9MRU5HVEgARkxVU0gAUFJPUFBBVENIAE0tU0VBUkNIAFVSSV9UT09fTE9ORwBQUk9DRVNTSU5HAE1JU0NFTExBTkVPVVNfUEVSU0lTVEVOVF9XQVJOSU5HAE1JU0NFTExBTkVPVVNfV0FSTklORwBIUEVfSU5WQUxJRF9UUkFOU0ZFUl9FTkNPRElORwBFeHBlY3RlZCBDUkxGAEhQRV9JTlZBTElEX0NIVU5LX1NJWkUATU9WRQBDT05USU5VRQBIUEVfQ0JfU1RBVFVTX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJTX0NPTVBMRVRFAEhQRV9DQl9WRVJTSU9OX0NPTVBMRVRFAEhQRV9DQl9VUkxfQ09NUExFVEUASFBFX0NCX1BST1RPQ09MX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19DT01QTEVURQBIUEVfQ0JfSEVBREVSX1ZBTFVFX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19FWFRFTlNJT05fVkFMVUVfQ09NUExFVEUASFBFX0NCX0NIVU5LX0VYVEVOU0lPTl9OQU1FX0NPTVBMRVRFAEhQRV9DQl9NRVNTQUdFX0NPTVBMRVRFAEhQRV9DQl9NRVRIT0RfQ09NUExFVEUASFBFX0NCX0hFQURFUl9GSUVMRF9DT01QTEVURQBERUxFVEUASFBFX0lOVkFMSURfRU9GX1NUQVRFAElOVkFMSURfU1NMX0NFUlRJRklDQVRFAFBBVVNFAE5PX1JFU1BPTlNFAFVOU1VQUE9SVEVEX01FRElBX1RZUEUAR09ORQBOT1RfQUNDRVBUQUJMRQBTRVJWSUNFX1VOQVZBSUxBQkxFAFJBTkdFX05PVF9TQVRJU0ZJQUJMRQBPUklHSU5fSVNfVU5SRUFDSEFCTEUAUkVTUE9OU0VfSVNfU1RBTEUAUFVSR0UATUVSR0UAUkVRVUVTVF9IRUFERVJfRklFTERTX1RPT19MQVJHRQBSRVFVRVNUX0hFQURFUl9UT09fTEFSR0UAUEFZTE9BRF9UT09fTEFSR0UASU5TVUZGSUNJRU5UX1NUT1JBR0UASFBFX1BBVVNFRF9VUEdSQURFAEhQRV9QQVVTRURfSDJfVVBHUkFERQBTT1VSQ0UAQU5OT1VOQ0UAVFJBQ0UASFBFX1VORVhQRUNURURfU1BBQ0UAREVTQ1JJQkUAVU5TVUJTQ1JJQkUAUkVDT1JEAEhQRV9JTlZBTElEX01FVEhPRABOT1RfRk9VTkQAUFJPUEZJTkQAVU5CSU5EAFJFQklORABVTkFVVEhPUklaRUQATUVUSE9EX05PVF9BTExPV0VEAEhUVFBfVkVSU0lPTl9OT1RfU1VQUE9SVEVEAEFMUkVBRFlfUkVQT1JURUQAQUNDRVBURUQATk9UX0lNUExFTUVOVEVEAExPT1BfREVURUNURUQASFBFX0NSX0VYUEVDVEVEAEhQRV9MRl9FWFBFQ1RFRABDUkVBVEVEAElNX1VTRUQASFBFX1BBVVNFRABUSU1FT1VUX09DQ1VSRUQAUEFZTUVOVF9SRVFVSVJFRABQUkVDT05ESVRJT05fUkVRVUlSRUQAUFJPWFlfQVVUSEVOVElDQVRJT05fUkVRVUlSRUQATkVUV09SS19BVVRIRU5USUNBVElPTl9SRVFVSVJFRABMRU5HVEhfUkVRVUlSRUQAU1NMX0NFUlRJRklDQVRFX1JFUVVJUkVEAFVQR1JBREVfUkVRVUlSRUQAUEFHRV9FWFBJUkVEAFBSRUNPTkRJVElPTl9GQUlMRUQARVhQRUNUQVRJT05fRkFJTEVEAFJFVkFMSURBVElPTl9GQUlMRUQAU1NMX0hBTkRTSEFLRV9GQUlMRUQATE9DS0VEAFRSQU5TRk9STUFUSU9OX0FQUExJRUQATk9UX01PRElGSUVEAE5PVF9FWFRFTkRFRABCQU5EV0lEVEhfTElNSVRfRVhDRUVERUQAU0lURV9JU19PVkVSTE9BREVEAEhFQUQARXhwZWN0ZWQgSFRUUC8sIFJUU1AvIG9yIElDRS8A5xUAAK8VAACkEgAAkhoAACYWAACeFAAA2xkAAHkVAAB+EgAA/hQAADYVAAALFgAA2BYAAPMSAABCGAAArBYAABIVAAAUFwAA7xcAAEgUAABxFwAAshoAAGsZAAB+GQAANRQAAIIaAABEFwAA/RYAAB4YAACHFwAAqhkAAJMSAAAHGAAALBcAAMoXAACkFwAA5xUAAOcVAABYFwAAOxgAAKASAAAtHAAAwxEAAEgRAADeEgAAQhMAAKQZAAD9EAAA9xUAAKUVAADvFgAA+BkAAEoWAABWFgAA9RUAAAoaAAAIGgAAARoAAKsVAABCEgAA1xAAAEwRAAAFGQAAVBYAAB4RAADKGQAAyBkAAE4WAAD/GAAAcRQAAPAVAADuFQAAlBkAAPwVAAC/GQAAmxkAAHwUAABDEQAAcBgAAJUUAAAnFAAAGRQAANUSAADUGQAARBYAAPcQAEG5OwsBAQBB0DsL4AEBAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBBuj0LBAEAAAIAQdE9C14DBAMDAwMDAAADAwADAwADAwMDAwMDAwMDAAUAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAwADAEG6PwsEAQAAAgBB0T8LXgMAAwMDAwMAAAMDAAMDAAMDAwMDAwMDAwMABAAFAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwADAAMAQbDBAAsNbG9zZWVlcC1hbGl2ZQBBycEACwEBAEHgwQAL4AEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBBycMACwEBAEHgwwAL5wEBAQEBAQEBAQEBAQECAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAWNodW5rZWQAQfHFAAteAQABAQEBAQAAAQEAAQEAAQEBAQEBAQEBAQAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQBB0McACyFlY3Rpb25lbnQtbGVuZ3Rob25yb3h5LWNvbm5lY3Rpb24AQYDIAAsgcmFuc2Zlci1lbmNvZGluZ3BncmFkZQ0KDQpTTQ0KDQoAQanIAAsFAQIAAQMAQcDIAAtfBAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAQanKAAsFAQIAAQMAQcDKAAtfBAUFBgUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAQanMAAsEAQAAAQBBwcwAC14CAgACAgICAgICAgICAgICAgICAgICAgICAgICAgIAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAEGpzgALBQECAAEDAEHAzgALXwQFAAAFBQUFBQUFBQUFBQYFBQUFBQUFBQUFBQUABQAHCAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQAFAAUABQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAAAAFAEGp0AALBQEBAAEBAEHA0AALAQEAQdrQAAtBAgAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAQanSAAsFAQEAAQEAQcDSAAsBAQBBytIACwYCAAAAAAIAQeHSAAs6AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBBoNQAC50BTk9VTkNFRUNLT1VUTkVDVEVURUNSSUJFTFVTSEVURUFEU0VBUkNIUkdFQ1RJVklUWUxFTkRBUlZFT1RJRllQVElPTlNDSFNFQVlTVEFUQ0hHRVVFUllPUkRJUkVDVE9SVFJDSFBBUkFNRVRFUlVSQ0VCU0NSSUJFQVJET1dOQUNFSU5ETktDS1VCU0NSSUJFVFRQQ0VUU1BBRFRQLw=='
-
-let wasmBuffer
-
-Object.defineProperty(module, 'exports', {
- get: () => {
- return wasmBuffer
- ? wasmBuffer
- : (wasmBuffer = Buffer.from(wasmBase64, 'base64'))
- }
-})
diff --git a/vanilla/node_modules/undici/lib/llhttp/utils.d.ts b/vanilla/node_modules/undici/lib/llhttp/utils.d.ts
deleted file mode 100644
index 681b22b..0000000
--- a/vanilla/node_modules/undici/lib/llhttp/utils.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-import type { IntDict } from './constants';
-export declare function enumToMap(obj: IntDict, filter?: readonly number[], exceptions?: readonly number[]): IntDict;
diff --git a/vanilla/node_modules/undici/lib/llhttp/utils.js b/vanilla/node_modules/undici/lib/llhttp/utils.js
deleted file mode 100644
index 95081ea..0000000
--- a/vanilla/node_modules/undici/lib/llhttp/utils.js
+++ /dev/null
@@ -1,12 +0,0 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-exports.enumToMap = enumToMap;
-function enumToMap(obj, filter = [], exceptions = []) {
- const emptyFilter = (filter?.length ?? 0) === 0;
- const emptyExceptions = (exceptions?.length ?? 0) === 0;
- return Object.fromEntries(Object.entries(obj).filter(([, value]) => {
- return (typeof value === 'number' &&
- (emptyFilter || filter.includes(value)) &&
- (emptyExceptions || !exceptions.includes(value)));
- }));
-}
diff --git a/vanilla/node_modules/undici/lib/mock/mock-agent.js b/vanilla/node_modules/undici/lib/mock/mock-agent.js
deleted file mode 100644
index 61449e0..0000000
--- a/vanilla/node_modules/undici/lib/mock/mock-agent.js
+++ /dev/null
@@ -1,232 +0,0 @@
-'use strict'
-
-const { kClients } = require('../core/symbols')
-const Agent = require('../dispatcher/agent')
-const {
- kAgent,
- kMockAgentSet,
- kMockAgentGet,
- kDispatches,
- kIsMockActive,
- kNetConnect,
- kGetNetConnect,
- kOptions,
- kFactory,
- kMockAgentRegisterCallHistory,
- kMockAgentIsCallHistoryEnabled,
- kMockAgentAddCallHistoryLog,
- kMockAgentMockCallHistoryInstance,
- kMockAgentAcceptsNonStandardSearchParameters,
- kMockCallHistoryAddLog,
- kIgnoreTrailingSlash
-} = require('./mock-symbols')
-const MockClient = require('./mock-client')
-const MockPool = require('./mock-pool')
-const { matchValue, normalizeSearchParams, buildAndValidateMockOptions, normalizeOrigin } = require('./mock-utils')
-const { InvalidArgumentError, UndiciError } = require('../core/errors')
-const Dispatcher = require('../dispatcher/dispatcher')
-const PendingInterceptorsFormatter = require('./pending-interceptors-formatter')
-const { MockCallHistory } = require('./mock-call-history')
-
-class MockAgent extends Dispatcher {
- constructor (opts = {}) {
- super(opts)
-
- const mockOptions = buildAndValidateMockOptions(opts)
-
- this[kNetConnect] = true
- this[kIsMockActive] = true
- this[kMockAgentIsCallHistoryEnabled] = mockOptions.enableCallHistory ?? false
- this[kMockAgentAcceptsNonStandardSearchParameters] = mockOptions.acceptNonStandardSearchParameters ?? false
- this[kIgnoreTrailingSlash] = mockOptions.ignoreTrailingSlash ?? false
-
- // Instantiate Agent and encapsulate
- if (opts?.agent && typeof opts.agent.dispatch !== 'function') {
- throw new InvalidArgumentError('Argument opts.agent must implement Agent')
- }
- const agent = opts?.agent ? opts.agent : new Agent(opts)
- this[kAgent] = agent
-
- this[kClients] = agent[kClients]
- this[kOptions] = mockOptions
-
- if (this[kMockAgentIsCallHistoryEnabled]) {
- this[kMockAgentRegisterCallHistory]()
- }
- }
-
- get (origin) {
- // Normalize origin to handle URL objects and case-insensitive hostnames
- const normalizedOrigin = normalizeOrigin(origin)
- const originKey = this[kIgnoreTrailingSlash] ? normalizedOrigin.replace(/\/$/, '') : normalizedOrigin
-
- let dispatcher = this[kMockAgentGet](originKey)
-
- if (!dispatcher) {
- dispatcher = this[kFactory](originKey)
- this[kMockAgentSet](originKey, dispatcher)
- }
- return dispatcher
- }
-
- dispatch (opts, handler) {
- opts.origin = normalizeOrigin(opts.origin)
-
- // Call MockAgent.get to perform additional setup before dispatching as normal
- this.get(opts.origin)
-
- this[kMockAgentAddCallHistoryLog](opts)
-
- const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters]
-
- const dispatchOpts = { ...opts }
-
- if (acceptNonStandardSearchParameters && dispatchOpts.path) {
- const [path, searchParams] = dispatchOpts.path.split('?')
- const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters)
- dispatchOpts.path = `${path}?${normalizedSearchParams}`
- }
-
- return this[kAgent].dispatch(dispatchOpts, handler)
- }
-
- async close () {
- this.clearCallHistory()
- await this[kAgent].close()
- this[kClients].clear()
- }
-
- deactivate () {
- this[kIsMockActive] = false
- }
-
- activate () {
- this[kIsMockActive] = true
- }
-
- enableNetConnect (matcher) {
- if (typeof matcher === 'string' || typeof matcher === 'function' || matcher instanceof RegExp) {
- if (Array.isArray(this[kNetConnect])) {
- this[kNetConnect].push(matcher)
- } else {
- this[kNetConnect] = [matcher]
- }
- } else if (typeof matcher === 'undefined') {
- this[kNetConnect] = true
- } else {
- throw new InvalidArgumentError('Unsupported matcher. Must be one of String|Function|RegExp.')
- }
- }
-
- disableNetConnect () {
- this[kNetConnect] = false
- }
-
- enableCallHistory () {
- this[kMockAgentIsCallHistoryEnabled] = true
-
- return this
- }
-
- disableCallHistory () {
- this[kMockAgentIsCallHistoryEnabled] = false
-
- return this
- }
-
- getCallHistory () {
- return this[kMockAgentMockCallHistoryInstance]
- }
-
- clearCallHistory () {
- if (this[kMockAgentMockCallHistoryInstance] !== undefined) {
- this[kMockAgentMockCallHistoryInstance].clear()
- }
- }
-
- // This is required to bypass issues caused by using global symbols - see:
- // https://github.com/nodejs/undici/issues/1447
- get isMockActive () {
- return this[kIsMockActive]
- }
-
- [kMockAgentRegisterCallHistory] () {
- if (this[kMockAgentMockCallHistoryInstance] === undefined) {
- this[kMockAgentMockCallHistoryInstance] = new MockCallHistory()
- }
- }
-
- [kMockAgentAddCallHistoryLog] (opts) {
- if (this[kMockAgentIsCallHistoryEnabled]) {
- // additional setup when enableCallHistory class method is used after mockAgent instantiation
- this[kMockAgentRegisterCallHistory]()
-
- // add call history log on every call (intercepted or not)
- this[kMockAgentMockCallHistoryInstance][kMockCallHistoryAddLog](opts)
- }
- }
-
- [kMockAgentSet] (origin, dispatcher) {
- this[kClients].set(origin, { count: 0, dispatcher })
- }
-
- [kFactory] (origin) {
- const mockOptions = Object.assign({ agent: this }, this[kOptions])
- return this[kOptions] && this[kOptions].connections === 1
- ? new MockClient(origin, mockOptions)
- : new MockPool(origin, mockOptions)
- }
-
- [kMockAgentGet] (origin) {
- // First check if we can immediately find it
- const result = this[kClients].get(origin)
- if (result?.dispatcher) {
- return result.dispatcher
- }
-
- // If the origin is not a string create a dummy parent pool and return to user
- if (typeof origin !== 'string') {
- const dispatcher = this[kFactory]('http://localhost:9999')
- this[kMockAgentSet](origin, dispatcher)
- return dispatcher
- }
-
- // If we match, create a pool and assign the same dispatches
- for (const [keyMatcher, result] of Array.from(this[kClients])) {
- if (result && typeof keyMatcher !== 'string' && matchValue(keyMatcher, origin)) {
- const dispatcher = this[kFactory](origin)
- this[kMockAgentSet](origin, dispatcher)
- dispatcher[kDispatches] = result.dispatcher[kDispatches]
- return dispatcher
- }
- }
- }
-
- [kGetNetConnect] () {
- return this[kNetConnect]
- }
-
- pendingInterceptors () {
- const mockAgentClients = this[kClients]
-
- return Array.from(mockAgentClients.entries())
- .flatMap(([origin, result]) => result.dispatcher[kDispatches].map(dispatch => ({ ...dispatch, origin })))
- .filter(({ pending }) => pending)
- }
-
- assertNoPendingInterceptors ({ pendingInterceptorsFormatter = new PendingInterceptorsFormatter() } = {}) {
- const pending = this.pendingInterceptors()
-
- if (pending.length === 0) {
- return
- }
-
- throw new UndiciError(
- pending.length === 1
- ? `1 interceptor is pending:\n\n${pendingInterceptorsFormatter.format(pending)}`.trim()
- : `${pending.length} interceptors are pending:\n\n${pendingInterceptorsFormatter.format(pending)}`.trim()
- )
- }
-}
-
-module.exports = MockAgent
diff --git a/vanilla/node_modules/undici/lib/mock/mock-call-history.js b/vanilla/node_modules/undici/lib/mock/mock-call-history.js
deleted file mode 100644
index d4a92b2..0000000
--- a/vanilla/node_modules/undici/lib/mock/mock-call-history.js
+++ /dev/null
@@ -1,248 +0,0 @@
-'use strict'
-
-const { kMockCallHistoryAddLog } = require('./mock-symbols')
-const { InvalidArgumentError } = require('../core/errors')
-
-function handleFilterCallsWithOptions (criteria, options, handler, store) {
- switch (options.operator) {
- case 'OR':
- store.push(...handler(criteria))
-
- return store
- case 'AND':
- return handler.call({ logs: store }, criteria)
- default:
- // guard -- should never happens because buildAndValidateFilterCallsOptions is called before
- throw new InvalidArgumentError('options.operator must to be a case insensitive string equal to \'OR\' or \'AND\'')
- }
-}
-
-function buildAndValidateFilterCallsOptions (options = {}) {
- const finalOptions = {}
-
- if ('operator' in options) {
- if (typeof options.operator !== 'string' || (options.operator.toUpperCase() !== 'OR' && options.operator.toUpperCase() !== 'AND')) {
- throw new InvalidArgumentError('options.operator must to be a case insensitive string equal to \'OR\' or \'AND\'')
- }
-
- return {
- ...finalOptions,
- operator: options.operator.toUpperCase()
- }
- }
-
- return finalOptions
-}
-
-function makeFilterCalls (parameterName) {
- return (parameterValue) => {
- if (typeof parameterValue === 'string' || parameterValue == null) {
- return this.logs.filter((log) => {
- return log[parameterName] === parameterValue
- })
- }
- if (parameterValue instanceof RegExp) {
- return this.logs.filter((log) => {
- return parameterValue.test(log[parameterName])
- })
- }
-
- throw new InvalidArgumentError(`${parameterName} parameter should be one of string, regexp, undefined or null`)
- }
-}
-function computeUrlWithMaybeSearchParameters (requestInit) {
- // path can contains query url parameters
- // or query can contains query url parameters
- try {
- const url = new URL(requestInit.path, requestInit.origin)
-
- // requestInit.path contains query url parameters
- // requestInit.query is then undefined
- if (url.search.length !== 0) {
- return url
- }
-
- // requestInit.query can be populated here
- url.search = new URLSearchParams(requestInit.query).toString()
-
- return url
- } catch (error) {
- throw new InvalidArgumentError('An error occurred when computing MockCallHistoryLog.url', { cause: error })
- }
-}
-
-class MockCallHistoryLog {
- constructor (requestInit = {}) {
- this.body = requestInit.body
- this.headers = requestInit.headers
- this.method = requestInit.method
-
- const url = computeUrlWithMaybeSearchParameters(requestInit)
-
- this.fullUrl = url.toString()
- this.origin = url.origin
- this.path = url.pathname
- this.searchParams = Object.fromEntries(url.searchParams)
- this.protocol = url.protocol
- this.host = url.host
- this.port = url.port
- this.hash = url.hash
- }
-
- toMap () {
- return new Map([
- ['protocol', this.protocol],
- ['host', this.host],
- ['port', this.port],
- ['origin', this.origin],
- ['path', this.path],
- ['hash', this.hash],
- ['searchParams', this.searchParams],
- ['fullUrl', this.fullUrl],
- ['method', this.method],
- ['body', this.body],
- ['headers', this.headers]]
- )
- }
-
- toString () {
- const options = { betweenKeyValueSeparator: '->', betweenPairSeparator: '|' }
- let result = ''
-
- this.toMap().forEach((value, key) => {
- if (typeof value === 'string' || value === undefined || value === null) {
- result = `${result}${key}${options.betweenKeyValueSeparator}${value}${options.betweenPairSeparator}`
- }
- if ((typeof value === 'object' && value !== null) || Array.isArray(value)) {
- result = `${result}${key}${options.betweenKeyValueSeparator}${JSON.stringify(value)}${options.betweenPairSeparator}`
- }
- // maybe miss something for non Record / Array headers and searchParams here
- })
-
- // delete last betweenPairSeparator
- return result.slice(0, -1)
- }
-}
-
-class MockCallHistory {
- logs = []
-
- calls () {
- return this.logs
- }
-
- firstCall () {
- return this.logs.at(0)
- }
-
- lastCall () {
- return this.logs.at(-1)
- }
-
- nthCall (number) {
- if (typeof number !== 'number') {
- throw new InvalidArgumentError('nthCall must be called with a number')
- }
- if (!Number.isInteger(number)) {
- throw new InvalidArgumentError('nthCall must be called with an integer')
- }
- if (Math.sign(number) !== 1) {
- throw new InvalidArgumentError('nthCall must be called with a positive value. use firstCall or lastCall instead')
- }
-
- // non zero based index. this is more human readable
- return this.logs.at(number - 1)
- }
-
- filterCalls (criteria, options) {
- // perf
- if (this.logs.length === 0) {
- return this.logs
- }
- if (typeof criteria === 'function') {
- return this.logs.filter(criteria)
- }
- if (criteria instanceof RegExp) {
- return this.logs.filter((log) => {
- return criteria.test(log.toString())
- })
- }
- if (typeof criteria === 'object' && criteria !== null) {
- // no criteria - returning all logs
- if (Object.keys(criteria).length === 0) {
- return this.logs
- }
-
- const finalOptions = { operator: 'OR', ...buildAndValidateFilterCallsOptions(options) }
-
- let maybeDuplicatedLogsFiltered = []
- if ('protocol' in criteria) {
- maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.protocol, finalOptions, this.filterCallsByProtocol, maybeDuplicatedLogsFiltered)
- }
- if ('host' in criteria) {
- maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.host, finalOptions, this.filterCallsByHost, maybeDuplicatedLogsFiltered)
- }
- if ('port' in criteria) {
- maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.port, finalOptions, this.filterCallsByPort, maybeDuplicatedLogsFiltered)
- }
- if ('origin' in criteria) {
- maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.origin, finalOptions, this.filterCallsByOrigin, maybeDuplicatedLogsFiltered)
- }
- if ('path' in criteria) {
- maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.path, finalOptions, this.filterCallsByPath, maybeDuplicatedLogsFiltered)
- }
- if ('hash' in criteria) {
- maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.hash, finalOptions, this.filterCallsByHash, maybeDuplicatedLogsFiltered)
- }
- if ('fullUrl' in criteria) {
- maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.fullUrl, finalOptions, this.filterCallsByFullUrl, maybeDuplicatedLogsFiltered)
- }
- if ('method' in criteria) {
- maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.method, finalOptions, this.filterCallsByMethod, maybeDuplicatedLogsFiltered)
- }
-
- const uniqLogsFiltered = [...new Set(maybeDuplicatedLogsFiltered)]
-
- return uniqLogsFiltered
- }
-
- throw new InvalidArgumentError('criteria parameter should be one of function, regexp, or object')
- }
-
- filterCallsByProtocol = makeFilterCalls.call(this, 'protocol')
-
- filterCallsByHost = makeFilterCalls.call(this, 'host')
-
- filterCallsByPort = makeFilterCalls.call(this, 'port')
-
- filterCallsByOrigin = makeFilterCalls.call(this, 'origin')
-
- filterCallsByPath = makeFilterCalls.call(this, 'path')
-
- filterCallsByHash = makeFilterCalls.call(this, 'hash')
-
- filterCallsByFullUrl = makeFilterCalls.call(this, 'fullUrl')
-
- filterCallsByMethod = makeFilterCalls.call(this, 'method')
-
- clear () {
- this.logs = []
- }
-
- [kMockCallHistoryAddLog] (requestInit) {
- const log = new MockCallHistoryLog(requestInit)
-
- this.logs.push(log)
-
- return log
- }
-
- * [Symbol.iterator] () {
- for (const log of this.calls()) {
- yield log
- }
- }
-}
-
-module.exports.MockCallHistory = MockCallHistory
-module.exports.MockCallHistoryLog = MockCallHistoryLog
diff --git a/vanilla/node_modules/undici/lib/mock/mock-client.js b/vanilla/node_modules/undici/lib/mock/mock-client.js
deleted file mode 100644
index b3be7ab..0000000
--- a/vanilla/node_modules/undici/lib/mock/mock-client.js
+++ /dev/null
@@ -1,68 +0,0 @@
-'use strict'
-
-const { promisify } = require('node:util')
-const Client = require('../dispatcher/client')
-const { buildMockDispatch } = require('./mock-utils')
-const {
- kDispatches,
- kMockAgent,
- kClose,
- kOriginalClose,
- kOrigin,
- kOriginalDispatch,
- kConnected,
- kIgnoreTrailingSlash
-} = require('./mock-symbols')
-const { MockInterceptor } = require('./mock-interceptor')
-const Symbols = require('../core/symbols')
-const { InvalidArgumentError } = require('../core/errors')
-
-/**
- * MockClient provides an API that extends the Client to influence the mockDispatches.
- */
-class MockClient extends Client {
- constructor (origin, opts) {
- if (!opts || !opts.agent || typeof opts.agent.dispatch !== 'function') {
- throw new InvalidArgumentError('Argument opts.agent must implement Agent')
- }
-
- super(origin, opts)
-
- this[kMockAgent] = opts.agent
- this[kOrigin] = origin
- this[kIgnoreTrailingSlash] = opts.ignoreTrailingSlash ?? false
- this[kDispatches] = []
- this[kConnected] = 1
- this[kOriginalDispatch] = this.dispatch
- this[kOriginalClose] = this.close.bind(this)
-
- this.dispatch = buildMockDispatch.call(this)
- this.close = this[kClose]
- }
-
- get [Symbols.kConnected] () {
- return this[kConnected]
- }
-
- /**
- * Sets up the base interceptor for mocking replies from undici.
- */
- intercept (opts) {
- return new MockInterceptor(
- opts && { ignoreTrailingSlash: this[kIgnoreTrailingSlash], ...opts },
- this[kDispatches]
- )
- }
-
- cleanMocks () {
- this[kDispatches] = []
- }
-
- async [kClose] () {
- await promisify(this[kOriginalClose])()
- this[kConnected] = 0
- this[kMockAgent][Symbols.kClients].delete(this[kOrigin])
- }
-}
-
-module.exports = MockClient
diff --git a/vanilla/node_modules/undici/lib/mock/mock-errors.js b/vanilla/node_modules/undici/lib/mock/mock-errors.js
deleted file mode 100644
index 69e4f9c..0000000
--- a/vanilla/node_modules/undici/lib/mock/mock-errors.js
+++ /dev/null
@@ -1,29 +0,0 @@
-'use strict'
-
-const { UndiciError } = require('../core/errors')
-
-const kMockNotMatchedError = Symbol.for('undici.error.UND_MOCK_ERR_MOCK_NOT_MATCHED')
-
-/**
- * The request does not match any registered mock dispatches.
- */
-class MockNotMatchedError extends UndiciError {
- constructor (message) {
- super(message)
- this.name = 'MockNotMatchedError'
- this.message = message || 'The request does not match any registered mock dispatches'
- this.code = 'UND_MOCK_ERR_MOCK_NOT_MATCHED'
- }
-
- static [Symbol.hasInstance] (instance) {
- return instance && instance[kMockNotMatchedError] === true
- }
-
- get [kMockNotMatchedError] () {
- return true
- }
-}
-
-module.exports = {
- MockNotMatchedError
-}
diff --git a/vanilla/node_modules/undici/lib/mock/mock-interceptor.js b/vanilla/node_modules/undici/lib/mock/mock-interceptor.js
deleted file mode 100644
index 1ea7aac..0000000
--- a/vanilla/node_modules/undici/lib/mock/mock-interceptor.js
+++ /dev/null
@@ -1,209 +0,0 @@
-'use strict'
-
-const { getResponseData, buildKey, addMockDispatch } = require('./mock-utils')
-const {
- kDispatches,
- kDispatchKey,
- kDefaultHeaders,
- kDefaultTrailers,
- kContentLength,
- kMockDispatch,
- kIgnoreTrailingSlash
-} = require('./mock-symbols')
-const { InvalidArgumentError } = require('../core/errors')
-const { serializePathWithQuery } = require('../core/util')
-
-/**
- * Defines the scope API for an interceptor reply
- */
-class MockScope {
- constructor (mockDispatch) {
- this[kMockDispatch] = mockDispatch
- }
-
- /**
- * Delay a reply by a set amount in ms.
- */
- delay (waitInMs) {
- if (typeof waitInMs !== 'number' || !Number.isInteger(waitInMs) || waitInMs <= 0) {
- throw new InvalidArgumentError('waitInMs must be a valid integer > 0')
- }
-
- this[kMockDispatch].delay = waitInMs
- return this
- }
-
- /**
- * For a defined reply, never mark as consumed.
- */
- persist () {
- this[kMockDispatch].persist = true
- return this
- }
-
- /**
- * Allow one to define a reply for a set amount of matching requests.
- */
- times (repeatTimes) {
- if (typeof repeatTimes !== 'number' || !Number.isInteger(repeatTimes) || repeatTimes <= 0) {
- throw new InvalidArgumentError('repeatTimes must be a valid integer > 0')
- }
-
- this[kMockDispatch].times = repeatTimes
- return this
- }
-}
-
-/**
- * Defines an interceptor for a Mock
- */
-class MockInterceptor {
- constructor (opts, mockDispatches) {
- if (typeof opts !== 'object') {
- throw new InvalidArgumentError('opts must be an object')
- }
- if (typeof opts.path === 'undefined') {
- throw new InvalidArgumentError('opts.path must be defined')
- }
- if (typeof opts.method === 'undefined') {
- opts.method = 'GET'
- }
- // See https://github.com/nodejs/undici/issues/1245
- // As per RFC 3986, clients are not supposed to send URI
- // fragments to servers when they retrieve a document,
- if (typeof opts.path === 'string') {
- if (opts.query) {
- opts.path = serializePathWithQuery(opts.path, opts.query)
- } else {
- // Matches https://github.com/nodejs/undici/blob/main/lib/web/fetch/index.js#L1811
- const parsedURL = new URL(opts.path, 'data://')
- opts.path = parsedURL.pathname + parsedURL.search
- }
- }
- if (typeof opts.method === 'string') {
- opts.method = opts.method.toUpperCase()
- }
-
- this[kDispatchKey] = buildKey(opts)
- this[kDispatches] = mockDispatches
- this[kIgnoreTrailingSlash] = opts.ignoreTrailingSlash ?? false
- this[kDefaultHeaders] = {}
- this[kDefaultTrailers] = {}
- this[kContentLength] = false
- }
-
- createMockScopeDispatchData ({ statusCode, data, responseOptions }) {
- const responseData = getResponseData(data)
- const contentLength = this[kContentLength] ? { 'content-length': responseData.length } : {}
- const headers = { ...this[kDefaultHeaders], ...contentLength, ...responseOptions.headers }
- const trailers = { ...this[kDefaultTrailers], ...responseOptions.trailers }
-
- return { statusCode, data, headers, trailers }
- }
-
- validateReplyParameters (replyParameters) {
- if (typeof replyParameters.statusCode === 'undefined') {
- throw new InvalidArgumentError('statusCode must be defined')
- }
- if (typeof replyParameters.responseOptions !== 'object' || replyParameters.responseOptions === null) {
- throw new InvalidArgumentError('responseOptions must be an object')
- }
- }
-
- /**
- * Mock an undici request with a defined reply.
- */
- reply (replyOptionsCallbackOrStatusCode) {
- // Values of reply aren't available right now as they
- // can only be available when the reply callback is invoked.
- if (typeof replyOptionsCallbackOrStatusCode === 'function') {
- // We'll first wrap the provided callback in another function,
- // this function will properly resolve the data from the callback
- // when invoked.
- const wrappedDefaultsCallback = (opts) => {
- // Our reply options callback contains the parameter for statusCode, data and options.
- const resolvedData = replyOptionsCallbackOrStatusCode(opts)
-
- // Check if it is in the right format
- if (typeof resolvedData !== 'object' || resolvedData === null) {
- throw new InvalidArgumentError('reply options callback must return an object')
- }
-
- const replyParameters = { data: '', responseOptions: {}, ...resolvedData }
- this.validateReplyParameters(replyParameters)
- // Since the values can be obtained immediately we return them
- // from this higher order function that will be resolved later.
- return {
- ...this.createMockScopeDispatchData(replyParameters)
- }
- }
-
- // Add usual dispatch data, but this time set the data parameter to function that will eventually provide data.
- const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], wrappedDefaultsCallback, { ignoreTrailingSlash: this[kIgnoreTrailingSlash] })
- return new MockScope(newMockDispatch)
- }
-
- // We can have either one or three parameters, if we get here,
- // we should have 1-3 parameters. So we spread the arguments of
- // this function to obtain the parameters, since replyData will always
- // just be the statusCode.
- const replyParameters = {
- statusCode: replyOptionsCallbackOrStatusCode,
- data: arguments[1] === undefined ? '' : arguments[1],
- responseOptions: arguments[2] === undefined ? {} : arguments[2]
- }
- this.validateReplyParameters(replyParameters)
-
- // Send in-already provided data like usual
- const dispatchData = this.createMockScopeDispatchData(replyParameters)
- const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], dispatchData, { ignoreTrailingSlash: this[kIgnoreTrailingSlash] })
- return new MockScope(newMockDispatch)
- }
-
- /**
- * Mock an undici request with a defined error.
- */
- replyWithError (error) {
- if (typeof error === 'undefined') {
- throw new InvalidArgumentError('error must be defined')
- }
-
- const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], { error }, { ignoreTrailingSlash: this[kIgnoreTrailingSlash] })
- return new MockScope(newMockDispatch)
- }
-
- /**
- * Set default reply headers on the interceptor for subsequent replies
- */
- defaultReplyHeaders (headers) {
- if (typeof headers === 'undefined') {
- throw new InvalidArgumentError('headers must be defined')
- }
-
- this[kDefaultHeaders] = headers
- return this
- }
-
- /**
- * Set default reply trailers on the interceptor for subsequent replies
- */
- defaultReplyTrailers (trailers) {
- if (typeof trailers === 'undefined') {
- throw new InvalidArgumentError('trailers must be defined')
- }
-
- this[kDefaultTrailers] = trailers
- return this
- }
-
- /**
- * Set reply content length header for replies on the interceptor
- */
- replyContentLength () {
- this[kContentLength] = true
- return this
- }
-}
-
-module.exports.MockInterceptor = MockInterceptor
-module.exports.MockScope = MockScope
diff --git a/vanilla/node_modules/undici/lib/mock/mock-pool.js b/vanilla/node_modules/undici/lib/mock/mock-pool.js
deleted file mode 100644
index 2121e3c..0000000
--- a/vanilla/node_modules/undici/lib/mock/mock-pool.js
+++ /dev/null
@@ -1,68 +0,0 @@
-'use strict'
-
-const { promisify } = require('node:util')
-const Pool = require('../dispatcher/pool')
-const { buildMockDispatch } = require('./mock-utils')
-const {
- kDispatches,
- kMockAgent,
- kClose,
- kOriginalClose,
- kOrigin,
- kOriginalDispatch,
- kConnected,
- kIgnoreTrailingSlash
-} = require('./mock-symbols')
-const { MockInterceptor } = require('./mock-interceptor')
-const Symbols = require('../core/symbols')
-const { InvalidArgumentError } = require('../core/errors')
-
-/**
- * MockPool provides an API that extends the Pool to influence the mockDispatches.
- */
-class MockPool extends Pool {
- constructor (origin, opts) {
- if (!opts || !opts.agent || typeof opts.agent.dispatch !== 'function') {
- throw new InvalidArgumentError('Argument opts.agent must implement Agent')
- }
-
- super(origin, opts)
-
- this[kMockAgent] = opts.agent
- this[kOrigin] = origin
- this[kIgnoreTrailingSlash] = opts.ignoreTrailingSlash ?? false
- this[kDispatches] = []
- this[kConnected] = 1
- this[kOriginalDispatch] = this.dispatch
- this[kOriginalClose] = this.close.bind(this)
-
- this.dispatch = buildMockDispatch.call(this)
- this.close = this[kClose]
- }
-
- get [Symbols.kConnected] () {
- return this[kConnected]
- }
-
- /**
- * Sets up the base interceptor for mocking replies from undici.
- */
- intercept (opts) {
- return new MockInterceptor(
- opts && { ignoreTrailingSlash: this[kIgnoreTrailingSlash], ...opts },
- this[kDispatches]
- )
- }
-
- cleanMocks () {
- this[kDispatches] = []
- }
-
- async [kClose] () {
- await promisify(this[kOriginalClose])()
- this[kConnected] = 0
- this[kMockAgent][Symbols.kClients].delete(this[kOrigin])
- }
-}
-
-module.exports = MockPool
diff --git a/vanilla/node_modules/undici/lib/mock/mock-symbols.js b/vanilla/node_modules/undici/lib/mock/mock-symbols.js
deleted file mode 100644
index 940dbe6..0000000
--- a/vanilla/node_modules/undici/lib/mock/mock-symbols.js
+++ /dev/null
@@ -1,31 +0,0 @@
-'use strict'
-
-module.exports = {
- kAgent: Symbol('agent'),
- kOptions: Symbol('options'),
- kFactory: Symbol('factory'),
- kDispatches: Symbol('dispatches'),
- kDispatchKey: Symbol('dispatch key'),
- kDefaultHeaders: Symbol('default headers'),
- kDefaultTrailers: Symbol('default trailers'),
- kContentLength: Symbol('content length'),
- kMockAgent: Symbol('mock agent'),
- kMockAgentSet: Symbol('mock agent set'),
- kMockAgentGet: Symbol('mock agent get'),
- kMockDispatch: Symbol('mock dispatch'),
- kClose: Symbol('close'),
- kOriginalClose: Symbol('original agent close'),
- kOriginalDispatch: Symbol('original dispatch'),
- kOrigin: Symbol('origin'),
- kIsMockActive: Symbol('is mock active'),
- kNetConnect: Symbol('net connect'),
- kGetNetConnect: Symbol('get net connect'),
- kConnected: Symbol('connected'),
- kIgnoreTrailingSlash: Symbol('ignore trailing slash'),
- kMockAgentMockCallHistoryInstance: Symbol('mock agent mock call history name'),
- kMockAgentRegisterCallHistory: Symbol('mock agent register mock call history'),
- kMockAgentAddCallHistoryLog: Symbol('mock agent add call history log'),
- kMockAgentIsCallHistoryEnabled: Symbol('mock agent is call history enabled'),
- kMockAgentAcceptsNonStandardSearchParameters: Symbol('mock agent accepts non standard search parameters'),
- kMockCallHistoryAddLog: Symbol('mock call history add log')
-}
diff --git a/vanilla/node_modules/undici/lib/mock/mock-utils.js b/vanilla/node_modules/undici/lib/mock/mock-utils.js
deleted file mode 100644
index 291a857..0000000
--- a/vanilla/node_modules/undici/lib/mock/mock-utils.js
+++ /dev/null
@@ -1,480 +0,0 @@
-'use strict'
-
-const { MockNotMatchedError } = require('./mock-errors')
-const {
- kDispatches,
- kMockAgent,
- kOriginalDispatch,
- kOrigin,
- kGetNetConnect
-} = require('./mock-symbols')
-const { serializePathWithQuery } = require('../core/util')
-const { STATUS_CODES } = require('node:http')
-const {
- types: {
- isPromise
- }
-} = require('node:util')
-const { InvalidArgumentError } = require('../core/errors')
-
-function matchValue (match, value) {
- if (typeof match === 'string') {
- return match === value
- }
- if (match instanceof RegExp) {
- return match.test(value)
- }
- if (typeof match === 'function') {
- return match(value) === true
- }
- return false
-}
-
-function lowerCaseEntries (headers) {
- return Object.fromEntries(
- Object.entries(headers).map(([headerName, headerValue]) => {
- return [headerName.toLocaleLowerCase(), headerValue]
- })
- )
-}
-
-/**
- * @param {import('../../index').Headers|string[]|Record<string, string>} headers
- * @param {string} key
- */
-function getHeaderByName (headers, key) {
- if (Array.isArray(headers)) {
- for (let i = 0; i < headers.length; i += 2) {
- if (headers[i].toLocaleLowerCase() === key.toLocaleLowerCase()) {
- return headers[i + 1]
- }
- }
-
- return undefined
- } else if (typeof headers.get === 'function') {
- return headers.get(key)
- } else {
- return lowerCaseEntries(headers)[key.toLocaleLowerCase()]
- }
-}
-
-/** @param {string[]} headers */
-function buildHeadersFromArray (headers) { // fetch HeadersList
- const clone = headers.slice()
- const entries = []
- for (let index = 0; index < clone.length; index += 2) {
- entries.push([clone[index], clone[index + 1]])
- }
- return Object.fromEntries(entries)
-}
-
-function matchHeaders (mockDispatch, headers) {
- if (typeof mockDispatch.headers === 'function') {
- if (Array.isArray(headers)) { // fetch HeadersList
- headers = buildHeadersFromArray(headers)
- }
- return mockDispatch.headers(headers ? lowerCaseEntries(headers) : {})
- }
- if (typeof mockDispatch.headers === 'undefined') {
- return true
- }
- if (typeof headers !== 'object' || typeof mockDispatch.headers !== 'object') {
- return false
- }
-
- for (const [matchHeaderName, matchHeaderValue] of Object.entries(mockDispatch.headers)) {
- const headerValue = getHeaderByName(headers, matchHeaderName)
-
- if (!matchValue(matchHeaderValue, headerValue)) {
- return false
- }
- }
- return true
-}
-
-function normalizeSearchParams (query) {
- if (typeof query !== 'string') {
- return query
- }
-
- const originalQp = new URLSearchParams(query)
- const normalizedQp = new URLSearchParams()
-
- for (let [key, value] of originalQp.entries()) {
- key = key.replace('[]', '')
-
- const valueRepresentsString = /^(['"]).*\1$/.test(value)
- if (valueRepresentsString) {
- normalizedQp.append(key, value)
- continue
- }
-
- if (value.includes(',')) {
- const values = value.split(',')
- for (const v of values) {
- normalizedQp.append(key, v)
- }
- continue
- }
-
- normalizedQp.append(key, value)
- }
-
- return normalizedQp
-}
-
-function safeUrl (path) {
- if (typeof path !== 'string') {
- return path
- }
- const pathSegments = path.split('?', 3)
- if (pathSegments.length !== 2) {
- return path
- }
-
- const qp = new URLSearchParams(pathSegments.pop())
- qp.sort()
- return [...pathSegments, qp.toString()].join('?')
-}
-
-function matchKey (mockDispatch, { path, method, body, headers }) {
- const pathMatch = matchValue(mockDispatch.path, path)
- const methodMatch = matchValue(mockDispatch.method, method)
- const bodyMatch = typeof mockDispatch.body !== 'undefined' ? matchValue(mockDispatch.body, body) : true
- const headersMatch = matchHeaders(mockDispatch, headers)
- return pathMatch && methodMatch && bodyMatch && headersMatch
-}
-
-function getResponseData (data) {
- if (Buffer.isBuffer(data)) {
- return data
- } else if (data instanceof Uint8Array) {
- return data
- } else if (data instanceof ArrayBuffer) {
- return data
- } else if (typeof data === 'object') {
- return JSON.stringify(data)
- } else if (data) {
- return data.toString()
- } else {
- return ''
- }
-}
-
-function getMockDispatch (mockDispatches, key) {
- const basePath = key.query ? serializePathWithQuery(key.path, key.query) : key.path
- const resolvedPath = typeof basePath === 'string' ? safeUrl(basePath) : basePath
-
- const resolvedPathWithoutTrailingSlash = removeTrailingSlash(resolvedPath)
-
- // Match path
- let matchedMockDispatches = mockDispatches
- .filter(({ consumed }) => !consumed)
- .filter(({ path, ignoreTrailingSlash }) => {
- return ignoreTrailingSlash
- ? matchValue(removeTrailingSlash(safeUrl(path)), resolvedPathWithoutTrailingSlash)
- : matchValue(safeUrl(path), resolvedPath)
- })
- if (matchedMockDispatches.length === 0) {
- throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`)
- }
-
- // Match method
- matchedMockDispatches = matchedMockDispatches.filter(({ method }) => matchValue(method, key.method))
- if (matchedMockDispatches.length === 0) {
- throw new MockNotMatchedError(`Mock dispatch not matched for method '${key.method}' on path '${resolvedPath}'`)
- }
-
- // Match body
- matchedMockDispatches = matchedMockDispatches.filter(({ body }) => typeof body !== 'undefined' ? matchValue(body, key.body) : true)
- if (matchedMockDispatches.length === 0) {
- throw new MockNotMatchedError(`Mock dispatch not matched for body '${key.body}' on path '${resolvedPath}'`)
- }
-
- // Match headers
- matchedMockDispatches = matchedMockDispatches.filter((mockDispatch) => matchHeaders(mockDispatch, key.headers))
- if (matchedMockDispatches.length === 0) {
- const headers = typeof key.headers === 'object' ? JSON.stringify(key.headers) : key.headers
- throw new MockNotMatchedError(`Mock dispatch not matched for headers '${headers}' on path '${resolvedPath}'`)
- }
-
- return matchedMockDispatches[0]
-}
-
-function addMockDispatch (mockDispatches, key, data, opts) {
- const baseData = { timesInvoked: 0, times: 1, persist: false, consumed: false, ...opts }
- const replyData = typeof data === 'function' ? { callback: data } : { ...data }
- const newMockDispatch = { ...baseData, ...key, pending: true, data: { error: null, ...replyData } }
- mockDispatches.push(newMockDispatch)
- return newMockDispatch
-}
-
-function deleteMockDispatch (mockDispatches, key) {
- const index = mockDispatches.findIndex(dispatch => {
- if (!dispatch.consumed) {
- return false
- }
- return matchKey(dispatch, key)
- })
- if (index !== -1) {
- mockDispatches.splice(index, 1)
- }
-}
-
-/**
- * @param {string} path Path to remove trailing slash from
- */
-function removeTrailingSlash (path) {
- while (path.endsWith('/')) {
- path = path.slice(0, -1)
- }
-
- if (path.length === 0) {
- path = '/'
- }
-
- return path
-}
-
-function buildKey (opts) {
- const { path, method, body, headers, query } = opts
-
- return {
- path,
- method,
- body,
- headers,
- query
- }
-}
-
-function generateKeyValues (data) {
- const keys = Object.keys(data)
- const result = []
- for (let i = 0; i < keys.length; ++i) {
- const key = keys[i]
- const value = data[key]
- const name = Buffer.from(`${key}`)
- if (Array.isArray(value)) {
- for (let j = 0; j < value.length; ++j) {
- result.push(name, Buffer.from(`${value[j]}`))
- }
- } else {
- result.push(name, Buffer.from(`${value}`))
- }
- }
- return result
-}
-
-/**
- * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
- * @param {number} statusCode
- */
-function getStatusText (statusCode) {
- return STATUS_CODES[statusCode] || 'unknown'
-}
-
-async function getResponse (body) {
- const buffers = []
- for await (const data of body) {
- buffers.push(data)
- }
- return Buffer.concat(buffers).toString('utf8')
-}
-
-/**
- * Mock dispatch function used to simulate undici dispatches
- */
-function mockDispatch (opts, handler) {
- // Get mock dispatch from built key
- const key = buildKey(opts)
- const mockDispatch = getMockDispatch(this[kDispatches], key)
-
- mockDispatch.timesInvoked++
-
- // Here's where we resolve a callback if a callback is present for the dispatch data.
- if (mockDispatch.data.callback) {
- mockDispatch.data = { ...mockDispatch.data, ...mockDispatch.data.callback(opts) }
- }
-
- // Parse mockDispatch data
- const { data: { statusCode, data, headers, trailers, error }, delay, persist } = mockDispatch
- const { timesInvoked, times } = mockDispatch
-
- // If it's used up and not persistent, mark as consumed
- mockDispatch.consumed = !persist && timesInvoked >= times
- mockDispatch.pending = timesInvoked < times
-
- // If specified, trigger dispatch error
- if (error !== null) {
- deleteMockDispatch(this[kDispatches], key)
- handler.onError(error)
- return true
- }
-
- // Track whether the request has been aborted
- let aborted = false
- let timer = null
-
- function abort (err) {
- if (aborted) {
- return
- }
- aborted = true
-
- // Clear the pending delayed response if any
- if (timer !== null) {
- clearTimeout(timer)
- timer = null
- }
-
- // Notify the handler of the abort
- handler.onError(err)
- }
-
- // Call onConnect to allow the handler to register the abort callback
- handler.onConnect?.(abort, null)
-
- // Handle the request with a delay if necessary
- if (typeof delay === 'number' && delay > 0) {
- timer = setTimeout(() => {
- timer = null
- handleReply(this[kDispatches])
- }, delay)
- } else {
- handleReply(this[kDispatches])
- }
-
- function handleReply (mockDispatches, _data = data) {
- // Don't send response if the request was aborted
- if (aborted) {
- return
- }
-
- // fetch's HeadersList is a 1D string array
- const optsHeaders = Array.isArray(opts.headers)
- ? buildHeadersFromArray(opts.headers)
- : opts.headers
- const body = typeof _data === 'function'
- ? _data({ ...opts, headers: optsHeaders })
- : _data
-
- // util.types.isPromise is likely needed for jest.
- if (isPromise(body)) {
- // If handleReply is asynchronous, throwing an error
- // in the callback will reject the promise, rather than
- // synchronously throw the error, which breaks some tests.
- // Rather, we wait for the callback to resolve if it is a
- // promise, and then re-run handleReply with the new body.
- return body.then((newData) => handleReply(mockDispatches, newData))
- }
-
- // Check again if aborted after async body resolution
- if (aborted) {
- return
- }
-
- const responseData = getResponseData(body)
- const responseHeaders = generateKeyValues(headers)
- const responseTrailers = generateKeyValues(trailers)
-
- handler.onHeaders?.(statusCode, responseHeaders, resume, getStatusText(statusCode))
- handler.onData?.(Buffer.from(responseData))
- handler.onComplete?.(responseTrailers)
- deleteMockDispatch(mockDispatches, key)
- }
-
- function resume () {}
-
- return true
-}
-
-function buildMockDispatch () {
- const agent = this[kMockAgent]
- const origin = this[kOrigin]
- const originalDispatch = this[kOriginalDispatch]
-
- return function dispatch (opts, handler) {
- if (agent.isMockActive) {
- try {
- mockDispatch.call(this, opts, handler)
- } catch (error) {
- if (error.code === 'UND_MOCK_ERR_MOCK_NOT_MATCHED') {
- const netConnect = agent[kGetNetConnect]()
- if (netConnect === false) {
- throw new MockNotMatchedError(`${error.message}: subsequent request to origin ${origin} was not allowed (net.connect disabled)`)
- }
- if (checkNetConnect(netConnect, origin)) {
- originalDispatch.call(this, opts, handler)
- } else {
- throw new MockNotMatchedError(`${error.message}: subsequent request to origin ${origin} was not allowed (net.connect is not enabled for this origin)`)
- }
- } else {
- throw error
- }
- }
- } else {
- originalDispatch.call(this, opts, handler)
- }
- }
-}
-
-function checkNetConnect (netConnect, origin) {
- const url = new URL(origin)
- if (netConnect === true) {
- return true
- } else if (Array.isArray(netConnect) && netConnect.some((matcher) => matchValue(matcher, url.host))) {
- return true
- }
- return false
-}
-
-function normalizeOrigin (origin) {
- if (typeof origin !== 'string' && !(origin instanceof URL)) {
- return origin
- }
-
- if (origin instanceof URL) {
- return origin.origin
- }
-
- return origin.toLowerCase()
-}
-
-function buildAndValidateMockOptions (opts) {
- const { agent, ...mockOptions } = opts
-
- if ('enableCallHistory' in mockOptions && typeof mockOptions.enableCallHistory !== 'boolean') {
- throw new InvalidArgumentError('options.enableCallHistory must to be a boolean')
- }
-
- if ('acceptNonStandardSearchParameters' in mockOptions && typeof mockOptions.acceptNonStandardSearchParameters !== 'boolean') {
- throw new InvalidArgumentError('options.acceptNonStandardSearchParameters must to be a boolean')
- }
-
- if ('ignoreTrailingSlash' in mockOptions && typeof mockOptions.ignoreTrailingSlash !== 'boolean') {
- throw new InvalidArgumentError('options.ignoreTrailingSlash must to be a boolean')
- }
-
- return mockOptions
-}
-
-module.exports = {
- getResponseData,
- getMockDispatch,
- addMockDispatch,
- deleteMockDispatch,
- buildKey,
- generateKeyValues,
- matchValue,
- getResponse,
- getStatusText,
- mockDispatch,
- buildMockDispatch,
- checkNetConnect,
- buildAndValidateMockOptions,
- getHeaderByName,
- buildHeadersFromArray,
- normalizeSearchParams,
- normalizeOrigin
-}
diff --git a/vanilla/node_modules/undici/lib/mock/pending-interceptors-formatter.js b/vanilla/node_modules/undici/lib/mock/pending-interceptors-formatter.js
deleted file mode 100644
index ccca951..0000000
--- a/vanilla/node_modules/undici/lib/mock/pending-interceptors-formatter.js
+++ /dev/null
@@ -1,43 +0,0 @@
-'use strict'
-
-const { Transform } = require('node:stream')
-const { Console } = require('node:console')
-
-const PERSISTENT = process.versions.icu ? '✅' : 'Y '
-const NOT_PERSISTENT = process.versions.icu ? '❌' : 'N '
-
-/**
- * Gets the output of `console.table(…)` as a string.
- */
-module.exports = class PendingInterceptorsFormatter {
- constructor ({ disableColors } = {}) {
- this.transform = new Transform({
- transform (chunk, _enc, cb) {
- cb(null, chunk)
- }
- })
-
- this.logger = new Console({
- stdout: this.transform,
- inspectOptions: {
- colors: !disableColors && !process.env.CI
- }
- })
- }
-
- format (pendingInterceptors) {
- const withPrettyHeaders = pendingInterceptors.map(
- ({ method, path, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
- Method: method,
- Origin: origin,
- Path: path,
- 'Status code': statusCode,
- Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
- Invocations: timesInvoked,
- Remaining: persist ? Infinity : times - timesInvoked
- }))
-
- this.logger.table(withPrettyHeaders)
- return this.transform.read().toString()
- }
-}
diff --git a/vanilla/node_modules/undici/lib/mock/snapshot-agent.js b/vanilla/node_modules/undici/lib/mock/snapshot-agent.js
deleted file mode 100644
index 8028011..0000000
--- a/vanilla/node_modules/undici/lib/mock/snapshot-agent.js
+++ /dev/null
@@ -1,353 +0,0 @@
-'use strict'
-
-const Agent = require('../dispatcher/agent')
-const MockAgent = require('./mock-agent')
-const { SnapshotRecorder } = require('./snapshot-recorder')
-const WrapHandler = require('../handler/wrap-handler')
-const { InvalidArgumentError, UndiciError } = require('../core/errors')
-const { validateSnapshotMode } = require('./snapshot-utils')
-
-const kSnapshotRecorder = Symbol('kSnapshotRecorder')
-const kSnapshotMode = Symbol('kSnapshotMode')
-const kSnapshotPath = Symbol('kSnapshotPath')
-const kSnapshotLoaded = Symbol('kSnapshotLoaded')
-const kRealAgent = Symbol('kRealAgent')
-
-// Static flag to ensure warning is only emitted once per process
-let warningEmitted = false
-
-class SnapshotAgent extends MockAgent {
- constructor (opts = {}) {
- // Emit experimental warning only once
- if (!warningEmitted) {
- process.emitWarning(
- 'SnapshotAgent is experimental and subject to change',
- 'ExperimentalWarning'
- )
- warningEmitted = true
- }
-
- const {
- mode = 'record',
- snapshotPath = null,
- ...mockAgentOpts
- } = opts
-
- super(mockAgentOpts)
-
- validateSnapshotMode(mode)
-
- // Validate snapshotPath is provided when required
- if ((mode === 'playback' || mode === 'update') && !snapshotPath) {
- throw new InvalidArgumentError(`snapshotPath is required when mode is '${mode}'`)
- }
-
- this[kSnapshotMode] = mode
- this[kSnapshotPath] = snapshotPath
-
- this[kSnapshotRecorder] = new SnapshotRecorder({
- snapshotPath: this[kSnapshotPath],
- mode: this[kSnapshotMode],
- maxSnapshots: opts.maxSnapshots,
- autoFlush: opts.autoFlush,
- flushInterval: opts.flushInterval,
- matchHeaders: opts.matchHeaders,
- ignoreHeaders: opts.ignoreHeaders,
- excludeHeaders: opts.excludeHeaders,
- matchBody: opts.matchBody,
- matchQuery: opts.matchQuery,
- caseSensitive: opts.caseSensitive,
- shouldRecord: opts.shouldRecord,
- shouldPlayback: opts.shouldPlayback,
- excludeUrls: opts.excludeUrls
- })
- this[kSnapshotLoaded] = false
-
- // For recording/update mode, we need a real agent to make actual requests
- // For playback mode, we need a real agent if there are excluded URLs
- if (this[kSnapshotMode] === 'record' || this[kSnapshotMode] === 'update' ||
- (this[kSnapshotMode] === 'playback' && opts.excludeUrls && opts.excludeUrls.length > 0)) {
- this[kRealAgent] = new Agent(opts)
- }
-
- // Auto-load snapshots in playback/update mode
- if ((this[kSnapshotMode] === 'playback' || this[kSnapshotMode] === 'update') && this[kSnapshotPath]) {
- this.loadSnapshots().catch(() => {
- // Ignore load errors - file might not exist yet
- })
- }
- }
-
- dispatch (opts, handler) {
- handler = WrapHandler.wrap(handler)
- const mode = this[kSnapshotMode]
-
- // Check if URL should be excluded (pass through without mocking/recording)
- if (this[kSnapshotRecorder].isUrlExcluded(opts)) {
- // Real agent is guaranteed by constructor when excludeUrls is configured
- return this[kRealAgent].dispatch(opts, handler)
- }
-
- if (mode === 'playback' || mode === 'update') {
- // Ensure snapshots are loaded
- if (!this[kSnapshotLoaded]) {
- // Need to load asynchronously, delegate to async version
- return this.#asyncDispatch(opts, handler)
- }
-
- // Try to find existing snapshot (synchronous)
- const snapshot = this[kSnapshotRecorder].findSnapshot(opts)
-
- if (snapshot) {
- // Use recorded response (synchronous)
- return this.#replaySnapshot(snapshot, handler)
- } else if (mode === 'update') {
- // Make real request and record it (async required)
- return this.#recordAndReplay(opts, handler)
- } else {
- // Playback mode but no snapshot found
- const error = new UndiciError(`No snapshot found for ${opts.method || 'GET'} ${opts.path}`)
- if (handler.onError) {
- handler.onError(error)
- return
- }
- throw error
- }
- } else if (mode === 'record') {
- // Record mode - make real request and save response (async required)
- return this.#recordAndReplay(opts, handler)
- }
- }
-
- /**
- * Async version of dispatch for when we need to load snapshots first
- */
- async #asyncDispatch (opts, handler) {
- await this.loadSnapshots()
- return this.dispatch(opts, handler)
- }
-
- /**
- * Records a real request and replays the response
- */
- #recordAndReplay (opts, handler) {
- const responseData = {
- statusCode: null,
- headers: {},
- trailers: {},
- body: []
- }
-
- const self = this // Capture 'this' context for use within nested handler callbacks
-
- const recordingHandler = {
- onRequestStart (controller, context) {
- return handler.onRequestStart(controller, { ...context, history: this.history })
- },
-
- onRequestUpgrade (controller, statusCode, headers, socket) {
- return handler.onRequestUpgrade(controller, statusCode, headers, socket)
- },
-
- onResponseStart (controller, statusCode, headers, statusMessage) {
- responseData.statusCode = statusCode
- responseData.headers = headers
- return handler.onResponseStart(controller, statusCode, headers, statusMessage)
- },
-
- onResponseData (controller, chunk) {
- responseData.body.push(chunk)
- return handler.onResponseData(controller, chunk)
- },
-
- onResponseEnd (controller, trailers) {
- responseData.trailers = trailers
-
- // Record the interaction using captured 'self' context (fire and forget)
- const responseBody = Buffer.concat(responseData.body)
- self[kSnapshotRecorder].record(opts, {
- statusCode: responseData.statusCode,
- headers: responseData.headers,
- body: responseBody,
- trailers: responseData.trailers
- })
- .then(() => handler.onResponseEnd(controller, trailers))
- .catch((error) => handler.onResponseError(controller, error))
- }
- }
-
- // Use composed agent if available (includes interceptors), otherwise use real agent
- const agent = this[kRealAgent]
- return agent.dispatch(opts, recordingHandler)
- }
-
- /**
- * Replays a recorded response
- *
- * @param {Object} snapshot - The recorded snapshot to replay.
- * @param {Object} handler - The handler to call with the response data.
- * @returns {void}
- */
- #replaySnapshot (snapshot, handler) {
- try {
- const { response } = snapshot
-
- const controller = {
- pause () { },
- resume () { },
- abort (reason) {
- this.aborted = true
- this.reason = reason
- },
-
- aborted: false,
- paused: false
- }
-
- handler.onRequestStart(controller)
-
- handler.onResponseStart(controller, response.statusCode, response.headers)
-
- // Body is always stored as base64 string
- const body = Buffer.from(response.body, 'base64')
- handler.onResponseData(controller, body)
-
- handler.onResponseEnd(controller, response.trailers)
- } catch (error) {
- handler.onError?.(error)
- }
- }
-
- /**
- * Loads snapshots from file
- *
- * @param {string} [filePath] - Optional file path to load snapshots from.
- * @returns {Promise<void>} - Resolves when snapshots are loaded.
- */
- async loadSnapshots (filePath) {
- await this[kSnapshotRecorder].loadSnapshots(filePath || this[kSnapshotPath])
- this[kSnapshotLoaded] = true
-
- // In playback mode, set up MockAgent interceptors for all snapshots
- if (this[kSnapshotMode] === 'playback') {
- this.#setupMockInterceptors()
- }
- }
-
- /**
- * Saves snapshots to file
- *
- * @param {string} [filePath] - Optional file path to save snapshots to.
- * @returns {Promise<void>} - Resolves when snapshots are saved.
- */
- async saveSnapshots (filePath) {
- return this[kSnapshotRecorder].saveSnapshots(filePath || this[kSnapshotPath])
- }
-
- /**
- * Sets up MockAgent interceptors based on recorded snapshots.
- *
- * This method creates MockAgent interceptors for each recorded snapshot,
- * allowing the SnapshotAgent to fall back to MockAgent's standard intercept
- * mechanism in playback mode. Each interceptor is configured to persist
- * (remain active for multiple requests) and responds with the recorded
- * response data.
- *
- * Called automatically when loading snapshots in playback mode.
- *
- * @returns {void}
- */
- #setupMockInterceptors () {
- for (const snapshot of this[kSnapshotRecorder].getSnapshots()) {
- const { request, responses, response } = snapshot
- const url = new URL(request.url)
-
- const mockPool = this.get(url.origin)
-
- // Handle both new format (responses array) and legacy format (response object)
- const responseData = responses ? responses[0] : response
- if (!responseData) continue
-
- mockPool.intercept({
- path: url.pathname + url.search,
- method: request.method,
- headers: request.headers,
- body: request.body
- }).reply(responseData.statusCode, responseData.body, {
- headers: responseData.headers,
- trailers: responseData.trailers
- }).persist()
- }
- }
-
- /**
- * Gets the snapshot recorder
- * @return {SnapshotRecorder} - The snapshot recorder instance
- */
- getRecorder () {
- return this[kSnapshotRecorder]
- }
-
- /**
- * Gets the current mode
- * @return {import('./snapshot-utils').SnapshotMode} - The current snapshot mode
- */
- getMode () {
- return this[kSnapshotMode]
- }
-
- /**
- * Clears all snapshots
- * @returns {void}
- */
- clearSnapshots () {
- this[kSnapshotRecorder].clear()
- }
-
- /**
- * Resets call counts for all snapshots (useful for test cleanup)
- * @returns {void}
- */
- resetCallCounts () {
- this[kSnapshotRecorder].resetCallCounts()
- }
-
- /**
- * Deletes a specific snapshot by request options
- * @param {import('./snapshot-recorder').SnapshotRequestOptions} requestOpts - Request options to identify the snapshot
- * @return {Promise<boolean>} - Returns true if the snapshot was deleted, false if not found
- */
- deleteSnapshot (requestOpts) {
- return this[kSnapshotRecorder].deleteSnapshot(requestOpts)
- }
-
- /**
- * Gets information about a specific snapshot
- * @returns {import('./snapshot-recorder').SnapshotInfo|null} - Snapshot information or null if not found
- */
- getSnapshotInfo (requestOpts) {
- return this[kSnapshotRecorder].getSnapshotInfo(requestOpts)
- }
-
- /**
- * Replaces all snapshots with new data (full replacement)
- * @param {Array<{hash: string; snapshot: import('./snapshot-recorder').SnapshotEntryshotEntry}>|Record<string, import('./snapshot-recorder').SnapshotEntry>} snapshotData - New snapshot data to replace existing snapshots
- * @returns {void}
- */
- replaceSnapshots (snapshotData) {
- this[kSnapshotRecorder].replaceSnapshots(snapshotData)
- }
-
- /**
- * Closes the agent, saving snapshots and cleaning up resources.
- *
- * @returns {Promise<void>}
- */
- async close () {
- await this[kSnapshotRecorder].close()
- await this[kRealAgent]?.close()
- await super.close()
- }
-}
-
-module.exports = SnapshotAgent
diff --git a/vanilla/node_modules/undici/lib/mock/snapshot-recorder.js b/vanilla/node_modules/undici/lib/mock/snapshot-recorder.js
deleted file mode 100644
index b5d07fa..0000000
--- a/vanilla/node_modules/undici/lib/mock/snapshot-recorder.js
+++ /dev/null
@@ -1,588 +0,0 @@
-'use strict'
-
-const { writeFile, readFile, mkdir } = require('node:fs/promises')
-const { dirname, resolve } = require('node:path')
-const { setTimeout, clearTimeout } = require('node:timers')
-const { InvalidArgumentError, UndiciError } = require('../core/errors')
-const { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require('./snapshot-utils')
-
-/**
- * @typedef {Object} SnapshotRequestOptions
- * @property {string} method - HTTP method (e.g. 'GET', 'POST', etc.)
- * @property {string} path - Request path
- * @property {string} origin - Request origin (base URL)
- * @property {import('./snapshot-utils').Headers|import('./snapshot-utils').UndiciHeaders} headers - Request headers
- * @property {import('./snapshot-utils').NormalizedHeaders} _normalizedHeaders - Request headers as a lowercase object
- * @property {string|Buffer} [body] - Request body (optional)
- */
-
-/**
- * @typedef {Object} SnapshotEntryRequest
- * @property {string} method - HTTP method (e.g. 'GET', 'POST', etc.)
- * @property {string} url - Full URL of the request
- * @property {import('./snapshot-utils').NormalizedHeaders} headers - Normalized headers as a lowercase object
- * @property {string|Buffer} [body] - Request body (optional)
- */
-
-/**
- * @typedef {Object} SnapshotEntryResponse
- * @property {number} statusCode - HTTP status code of the response
- * @property {import('./snapshot-utils').NormalizedHeaders} headers - Normalized response headers as a lowercase object
- * @property {string} body - Response body as a base64url encoded string
- * @property {Object} [trailers] - Optional response trailers
- */
-
-/**
- * @typedef {Object} SnapshotEntry
- * @property {SnapshotEntryRequest} request - The request object
- * @property {Array<SnapshotEntryResponse>} responses - Array of response objects
- * @property {number} callCount - Number of times this snapshot has been called
- * @property {string} timestamp - ISO timestamp of when the snapshot was created
- */
-
-/**
- * @typedef {Object} SnapshotRecorderMatchOptions
- * @property {Array<string>} [matchHeaders=[]] - Headers to match (empty array means match all headers)
- * @property {Array<string>} [ignoreHeaders=[]] - Headers to ignore for matching
- * @property {Array<string>} [excludeHeaders=[]] - Headers to exclude from matching
- * @property {boolean} [matchBody=true] - Whether to match request body
- * @property {boolean} [matchQuery=true] - Whether to match query properties
- * @property {boolean} [caseSensitive=false] - Whether header matching is case-sensitive
- */
-
-/**
- * @typedef {Object} SnapshotRecorderOptions
- * @property {string} [snapshotPath] - Path to save/load snapshots
- * @property {import('./snapshot-utils').SnapshotMode} [mode='record'] - Mode: 'record' or 'playback'
- * @property {number} [maxSnapshots=Infinity] - Maximum number of snapshots to keep
- * @property {boolean} [autoFlush=false] - Whether to automatically flush snapshots to disk
- * @property {number} [flushInterval=30000] - Auto-flush interval in milliseconds (default: 30 seconds)
- * @property {Array<string|RegExp>} [excludeUrls=[]] - URLs to exclude from recording
- * @property {function} [shouldRecord=null] - Function to filter requests for recording
- * @property {function} [shouldPlayback=null] - Function to filter requests
- */
-
-/**
- * @typedef {Object} SnapshotFormattedRequest
- * @property {string} method - HTTP method (e.g. 'GET', 'POST', etc.)
- * @property {string} url - Full URL of the request (with query parameters if matchQuery is true)
- * @property {import('./snapshot-utils').NormalizedHeaders} headers - Normalized headers as a lowercase object
- * @property {string} body - Request body (optional, only if matchBody is true)
- */
-
-/**
- * @typedef {Object} SnapshotInfo
- * @property {string} hash - Hash key for the snapshot
- * @property {SnapshotEntryRequest} request - The request object
- * @property {number} responseCount - Number of responses recorded for this request
- * @property {number} callCount - Number of times this snapshot has been called
- * @property {string} timestamp - ISO timestamp of when the snapshot was created
- */
-
-/**
- * Formats a request for consistent snapshot storage
- * Caches normalized headers to avoid repeated processing
- *
- * @param {SnapshotRequestOptions} opts - Request options
- * @param {import('./snapshot-utils').HeaderFilters} headerFilters - Cached header sets for performance
- * @param {SnapshotRecorderMatchOptions} [matchOptions] - Matching options for headers and body
- * @returns {SnapshotFormattedRequest} - Formatted request object
- */
-function formatRequestKey (opts, headerFilters, matchOptions = {}) {
- const url = new URL(opts.path, opts.origin)
-
- // Cache normalized headers if not already done
- const normalized = opts._normalizedHeaders || normalizeHeaders(opts.headers)
- if (!opts._normalizedHeaders) {
- opts._normalizedHeaders = normalized
- }
-
- return {
- method: opts.method || 'GET',
- url: matchOptions.matchQuery !== false ? url.toString() : `${url.origin}${url.pathname}`,
- headers: filterHeadersForMatching(normalized, headerFilters, matchOptions),
- body: matchOptions.matchBody !== false && opts.body ? String(opts.body) : ''
- }
-}
-
-/**
- * Filters headers based on matching configuration
- *
- * @param {import('./snapshot-utils').Headers} headers - Headers to filter
- * @param {import('./snapshot-utils').HeaderFilters} headerFilters - Cached sets for ignore, exclude, and match headers
- * @param {SnapshotRecorderMatchOptions} [matchOptions] - Matching options for headers
- */
-function filterHeadersForMatching (headers, headerFilters, matchOptions = {}) {
- if (!headers || typeof headers !== 'object') return {}
-
- const {
- caseSensitive = false
- } = matchOptions
-
- const filtered = {}
- const { ignore, exclude, match } = headerFilters
-
- for (const [key, value] of Object.entries(headers)) {
- const headerKey = caseSensitive ? key : key.toLowerCase()
-
- // Skip if in exclude list (for security)
- if (exclude.has(headerKey)) continue
-
- // Skip if in ignore list (for matching)
- if (ignore.has(headerKey)) continue
-
- // If matchHeaders is specified, only include those headers
- if (match.size !== 0) {
- if (!match.has(headerKey)) continue
- }
-
- filtered[headerKey] = value
- }
-
- return filtered
-}
-
-/**
- * Filters headers for storage (only excludes sensitive headers)
- *
- * @param {import('./snapshot-utils').Headers} headers - Headers to filter
- * @param {import('./snapshot-utils').HeaderFilters} headerFilters - Cached sets for ignore, exclude, and match headers
- * @param {SnapshotRecorderMatchOptions} [matchOptions] - Matching options for headers
- */
-function filterHeadersForStorage (headers, headerFilters, matchOptions = {}) {
- if (!headers || typeof headers !== 'object') return {}
-
- const {
- caseSensitive = false
- } = matchOptions
-
- const filtered = {}
- const { exclude: excludeSet } = headerFilters
-
- for (const [key, value] of Object.entries(headers)) {
- const headerKey = caseSensitive ? key : key.toLowerCase()
-
- // Skip if in exclude list (for security)
- if (excludeSet.has(headerKey)) continue
-
- filtered[headerKey] = value
- }
-
- return filtered
-}
-
-/**
- * Creates a hash key for request matching
- * Properly orders headers to avoid conflicts and uses crypto hashing when available
- *
- * @param {SnapshotFormattedRequest} formattedRequest - Request object
- * @returns {string} - Base64url encoded hash of the request
- */
-function createRequestHash (formattedRequest) {
- const parts = [
- formattedRequest.method,
- formattedRequest.url
- ]
-
- // Process headers in a deterministic way to avoid conflicts
- if (formattedRequest.headers && typeof formattedRequest.headers === 'object') {
- const headerKeys = Object.keys(formattedRequest.headers).sort()
- for (const key of headerKeys) {
- const values = Array.isArray(formattedRequest.headers[key])
- ? formattedRequest.headers[key]
- : [formattedRequest.headers[key]]
-
- // Add header name
- parts.push(key)
-
- // Add all values for this header, sorted for consistency
- for (const value of values.sort()) {
- parts.push(String(value))
- }
- }
- }
-
- // Add body
- parts.push(formattedRequest.body)
-
- const content = parts.join('|')
-
- return hashId(content)
-}
-
-class SnapshotRecorder {
- /** @type {NodeJS.Timeout | null} */
- #flushTimeout
-
- /** @type {import('./snapshot-utils').IsUrlExcluded} */
- #isUrlExcluded
-
- /** @type {Map<string, SnapshotEntry>} */
- #snapshots = new Map()
-
- /** @type {string|undefined} */
- #snapshotPath
-
- /** @type {number} */
- #maxSnapshots = Infinity
-
- /** @type {boolean} */
- #autoFlush = false
-
- /** @type {import('./snapshot-utils').HeaderFilters} */
- #headerFilters
-
- /**
- * Creates a new SnapshotRecorder instance
- * @param {SnapshotRecorderOptions&SnapshotRecorderMatchOptions} [options={}] - Configuration options for the recorder
- */
- constructor (options = {}) {
- this.#snapshotPath = options.snapshotPath
- this.#maxSnapshots = options.maxSnapshots || Infinity
- this.#autoFlush = options.autoFlush || false
- this.flushInterval = options.flushInterval || 30000 // 30 seconds default
- this._flushTimer = null
-
- // Matching configuration
- /** @type {Required<SnapshotRecorderMatchOptions>} */
- this.matchOptions = {
- matchHeaders: options.matchHeaders || [], // empty means match all headers
- ignoreHeaders: options.ignoreHeaders || [],
- excludeHeaders: options.excludeHeaders || [],
- matchBody: options.matchBody !== false, // default: true
- matchQuery: options.matchQuery !== false, // default: true
- caseSensitive: options.caseSensitive || false
- }
-
- // Cache processed header sets to avoid recreating them on every request
- this.#headerFilters = createHeaderFilters(this.matchOptions)
-
- // Request filtering callbacks
- this.shouldRecord = options.shouldRecord || (() => true) // function(requestOpts) -> boolean
- this.shouldPlayback = options.shouldPlayback || (() => true) // function(requestOpts) -> boolean
-
- // URL pattern filtering
- this.#isUrlExcluded = isUrlExcludedFactory(options.excludeUrls) // Array of regex patterns or strings
-
- // Start auto-flush timer if enabled
- if (this.#autoFlush && this.#snapshotPath) {
- this.#startAutoFlush()
- }
- }
-
- /**
- * Records a request-response interaction
- * @param {SnapshotRequestOptions} requestOpts - Request options
- * @param {SnapshotEntryResponse} response - Response data to record
- * @return {Promise<void>} - Resolves when the recording is complete
- */
- async record (requestOpts, response) {
- // Check if recording should be filtered out
- if (!this.shouldRecord(requestOpts)) {
- return // Skip recording
- }
-
- // Check URL exclusion patterns
- if (this.isUrlExcluded(requestOpts)) {
- return // Skip recording
- }
-
- const request = formatRequestKey(requestOpts, this.#headerFilters, this.matchOptions)
- const hash = createRequestHash(request)
-
- // Extract response data - always store body as base64
- const normalizedHeaders = normalizeHeaders(response.headers)
-
- /** @type {SnapshotEntryResponse} */
- const responseData = {
- statusCode: response.statusCode,
- headers: filterHeadersForStorage(normalizedHeaders, this.#headerFilters, this.matchOptions),
- body: Buffer.isBuffer(response.body)
- ? response.body.toString('base64')
- : Buffer.from(String(response.body || '')).toString('base64'),
- trailers: response.trailers
- }
-
- // Remove oldest snapshot if we exceed maxSnapshots limit
- if (this.#snapshots.size >= this.#maxSnapshots && !this.#snapshots.has(hash)) {
- const oldestKey = this.#snapshots.keys().next().value
- this.#snapshots.delete(oldestKey)
- }
-
- // Support sequential responses - if snapshot exists, add to responses array
- const existingSnapshot = this.#snapshots.get(hash)
- if (existingSnapshot && existingSnapshot.responses) {
- existingSnapshot.responses.push(responseData)
- existingSnapshot.timestamp = new Date().toISOString()
- } else {
- this.#snapshots.set(hash, {
- request,
- responses: [responseData], // Always store as array for consistency
- callCount: 0,
- timestamp: new Date().toISOString()
- })
- }
-
- // Auto-flush if enabled
- if (this.#autoFlush && this.#snapshotPath) {
- this.#scheduleFlush()
- }
- }
-
- /**
- * Checks if a URL should be excluded from recording/playback
- * @param {SnapshotRequestOptions} requestOpts - Request options to check
- * @returns {boolean} - True if URL is excluded
- */
- isUrlExcluded (requestOpts) {
- const url = new URL(requestOpts.path, requestOpts.origin).toString()
- return this.#isUrlExcluded(url)
- }
-
- /**
- * Finds a matching snapshot for the given request
- * Returns the appropriate response based on call count for sequential responses
- *
- * @param {SnapshotRequestOptions} requestOpts - Request options to match
- * @returns {SnapshotEntry&Record<'response', SnapshotEntryResponse>|undefined} - Matching snapshot response or undefined if not found
- */
- findSnapshot (requestOpts) {
- // Check if playback should be filtered out
- if (!this.shouldPlayback(requestOpts)) {
- return undefined // Skip playback
- }
-
- // Check URL exclusion patterns
- if (this.isUrlExcluded(requestOpts)) {
- return undefined // Skip playback
- }
-
- const request = formatRequestKey(requestOpts, this.#headerFilters, this.matchOptions)
- const hash = createRequestHash(request)
- const snapshot = this.#snapshots.get(hash)
-
- if (!snapshot) return undefined
-
- // Handle sequential responses
- const currentCallCount = snapshot.callCount || 0
- const responseIndex = Math.min(currentCallCount, snapshot.responses.length - 1)
- snapshot.callCount = currentCallCount + 1
-
- return {
- ...snapshot,
- response: snapshot.responses[responseIndex]
- }
- }
-
- /**
- * Loads snapshots from file
- * @param {string} [filePath] - Optional file path to load snapshots from
- * @return {Promise<void>} - Resolves when snapshots are loaded
- */
- async loadSnapshots (filePath) {
- const path = filePath || this.#snapshotPath
- if (!path) {
- throw new InvalidArgumentError('Snapshot path is required')
- }
-
- try {
- const data = await readFile(resolve(path), 'utf8')
- const parsed = JSON.parse(data)
-
- // Convert array format back to Map
- if (Array.isArray(parsed)) {
- this.#snapshots.clear()
- for (const { hash, snapshot } of parsed) {
- this.#snapshots.set(hash, snapshot)
- }
- } else {
- // Legacy object format
- this.#snapshots = new Map(Object.entries(parsed))
- }
- } catch (error) {
- if (error.code === 'ENOENT') {
- // File doesn't exist yet - that's ok for recording mode
- this.#snapshots.clear()
- } else {
- throw new UndiciError(`Failed to load snapshots from ${path}`, { cause: error })
- }
- }
- }
-
- /**
- * Saves snapshots to file
- *
- * @param {string} [filePath] - Optional file path to save snapshots
- * @returns {Promise<void>} - Resolves when snapshots are saved
- */
- async saveSnapshots (filePath) {
- const path = filePath || this.#snapshotPath
- if (!path) {
- throw new InvalidArgumentError('Snapshot path is required')
- }
-
- const resolvedPath = resolve(path)
-
- // Ensure directory exists
- await mkdir(dirname(resolvedPath), { recursive: true })
-
- // Convert Map to serializable format
- const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
- hash,
- snapshot
- }))
-
- await writeFile(resolvedPath, JSON.stringify(data, null, 2), { flush: true })
- }
-
- /**
- * Clears all recorded snapshots
- * @returns {void}
- */
- clear () {
- this.#snapshots.clear()
- }
-
- /**
- * Gets all recorded snapshots
- * @return {Array<SnapshotEntry>} - Array of all recorded snapshots
- */
- getSnapshots () {
- return Array.from(this.#snapshots.values())
- }
-
- /**
- * Gets snapshot count
- * @return {number} - Number of recorded snapshots
- */
- size () {
- return this.#snapshots.size
- }
-
- /**
- * Resets call counts for all snapshots (useful for test cleanup)
- * @returns {void}
- */
- resetCallCounts () {
- for (const snapshot of this.#snapshots.values()) {
- snapshot.callCount = 0
- }
- }
-
- /**
- * Deletes a specific snapshot by request options
- * @param {SnapshotRequestOptions} requestOpts - Request options to match
- * @returns {boolean} - True if snapshot was deleted, false if not found
- */
- deleteSnapshot (requestOpts) {
- const request = formatRequestKey(requestOpts, this.#headerFilters, this.matchOptions)
- const hash = createRequestHash(request)
- return this.#snapshots.delete(hash)
- }
-
- /**
- * Gets information about a specific snapshot
- * @param {SnapshotRequestOptions} requestOpts - Request options to match
- * @returns {SnapshotInfo|null} - Snapshot information or null if not found
- */
- getSnapshotInfo (requestOpts) {
- const request = formatRequestKey(requestOpts, this.#headerFilters, this.matchOptions)
- const hash = createRequestHash(request)
- const snapshot = this.#snapshots.get(hash)
-
- if (!snapshot) return null
-
- return {
- hash,
- request: snapshot.request,
- responseCount: snapshot.responses ? snapshot.responses.length : (snapshot.response ? 1 : 0), // .response for legacy snapshots
- callCount: snapshot.callCount || 0,
- timestamp: snapshot.timestamp
- }
- }
-
- /**
- * Replaces all snapshots with new data (full replacement)
- * @param {Array<{hash: string; snapshot: SnapshotEntry}>|Record<string, SnapshotEntry>} snapshotData - New snapshot data to replace existing ones
- * @returns {void}
- */
- replaceSnapshots (snapshotData) {
- this.#snapshots.clear()
-
- if (Array.isArray(snapshotData)) {
- for (const { hash, snapshot } of snapshotData) {
- this.#snapshots.set(hash, snapshot)
- }
- } else if (snapshotData && typeof snapshotData === 'object') {
- // Legacy object format
- this.#snapshots = new Map(Object.entries(snapshotData))
- }
- }
-
- /**
- * Starts the auto-flush timer
- * @returns {void}
- */
- #startAutoFlush () {
- return this.#scheduleFlush()
- }
-
- /**
- * Stops the auto-flush timer
- * @returns {void}
- */
- #stopAutoFlush () {
- if (this.#flushTimeout) {
- clearTimeout(this.#flushTimeout)
- // Ensure any pending flush is completed
- this.saveSnapshots().catch(() => {
- // Ignore flush errors
- })
- this.#flushTimeout = null
- }
- }
-
- /**
- * Schedules a flush (debounced to avoid excessive writes)
- */
- #scheduleFlush () {
- this.#flushTimeout = setTimeout(() => {
- this.saveSnapshots().catch(() => {
- // Ignore flush errors
- })
- if (this.#autoFlush) {
- this.#flushTimeout?.refresh()
- } else {
- this.#flushTimeout = null
- }
- }, 1000) // 1 second debounce
- }
-
- /**
- * Cleanup method to stop timers
- * @returns {void}
- */
- destroy () {
- this.#stopAutoFlush()
- if (this.#flushTimeout) {
- clearTimeout(this.#flushTimeout)
- this.#flushTimeout = null
- }
- }
-
- /**
- * Async close method that saves all recordings and performs cleanup
- * @returns {Promise<void>}
- */
- async close () {
- // Save any pending recordings if we have a snapshot path
- if (this.#snapshotPath && this.#snapshots.size !== 0) {
- await this.saveSnapshots()
- }
-
- // Perform cleanup
- this.destroy()
- }
-}
-
-module.exports = { SnapshotRecorder, formatRequestKey, createRequestHash, filterHeadersForMatching, filterHeadersForStorage, createHeaderFilters }
diff --git a/vanilla/node_modules/undici/lib/mock/snapshot-utils.js b/vanilla/node_modules/undici/lib/mock/snapshot-utils.js
deleted file mode 100644
index a14b69c..0000000
--- a/vanilla/node_modules/undici/lib/mock/snapshot-utils.js
+++ /dev/null
@@ -1,158 +0,0 @@
-'use strict'
-
-const { InvalidArgumentError } = require('../core/errors')
-const { runtimeFeatures } = require('../util/runtime-features.js')
-
-/**
- * @typedef {Object} HeaderFilters
- * @property {Set<string>} ignore - Set of headers to ignore for matching
- * @property {Set<string>} exclude - Set of headers to exclude from matching
- * @property {Set<string>} match - Set of headers to match (empty means match
- */
-
-/**
- * Creates cached header sets for performance
- *
- * @param {import('./snapshot-recorder').SnapshotRecorderMatchOptions} matchOptions - Matching options for headers
- * @returns {HeaderFilters} - Cached sets for ignore, exclude, and match headers
- */
-function createHeaderFilters (matchOptions = {}) {
- const { ignoreHeaders = [], excludeHeaders = [], matchHeaders = [], caseSensitive = false } = matchOptions
-
- return {
- ignore: new Set(ignoreHeaders.map(header => caseSensitive ? header : header.toLowerCase())),
- exclude: new Set(excludeHeaders.map(header => caseSensitive ? header : header.toLowerCase())),
- match: new Set(matchHeaders.map(header => caseSensitive ? header : header.toLowerCase()))
- }
-}
-
-const crypto = runtimeFeatures.has('crypto')
- ? require('node:crypto')
- : null
-
-/**
- * @callback HashIdFunction
- * @param {string} value - The value to hash
- * @returns {string} - The base64url encoded hash of the value
- */
-
-/**
- * Generates a hash for a given value
- * @type {HashIdFunction}
- */
-const hashId = crypto?.hash
- ? (value) => crypto.hash('sha256', value, 'base64url')
- : (value) => Buffer.from(value).toString('base64url')
-
-/**
- * @typedef {(url: string) => boolean} IsUrlExcluded Checks if a URL matches any of the exclude patterns
- */
-
-/** @typedef {{[key: Lowercase<string>]: string}} NormalizedHeaders */
-/** @typedef {Array<string>} UndiciHeaders */
-/** @typedef {Record<string, string|string[]>} Headers */
-
-/**
- * @param {*} headers
- * @returns {headers is UndiciHeaders}
- */
-function isUndiciHeaders (headers) {
- return Array.isArray(headers) && (headers.length & 1) === 0
-}
-
-/**
- * Factory function to create a URL exclusion checker
- * @param {Array<string| RegExp>} [excludePatterns=[]] - Array of patterns to exclude
- * @returns {IsUrlExcluded} - A function that checks if a URL matches any of the exclude patterns
- */
-function isUrlExcludedFactory (excludePatterns = []) {
- if (excludePatterns.length === 0) {
- return () => false
- }
-
- return function isUrlExcluded (url) {
- let urlLowerCased
-
- for (const pattern of excludePatterns) {
- if (typeof pattern === 'string') {
- if (!urlLowerCased) {
- // Convert URL to lowercase only once
- urlLowerCased = url.toLowerCase()
- }
- // Simple string match (case-insensitive)
- if (urlLowerCased.includes(pattern.toLowerCase())) {
- return true
- }
- } else if (pattern instanceof RegExp) {
- // Regex pattern match
- if (pattern.test(url)) {
- return true
- }
- }
- }
-
- return false
- }
-}
-
-/**
- * Normalizes headers for consistent comparison
- *
- * @param {Object|UndiciHeaders} headers - Headers to normalize
- * @returns {NormalizedHeaders} - Normalized headers as a lowercase object
- */
-function normalizeHeaders (headers) {
- /** @type {NormalizedHeaders} */
- const normalizedHeaders = {}
-
- if (!headers) return normalizedHeaders
-
- // Handle array format (undici internal format: [name, value, name, value, ...])
- if (isUndiciHeaders(headers)) {
- for (let i = 0; i < headers.length; i += 2) {
- const key = headers[i]
- const value = headers[i + 1]
- if (key && value !== undefined) {
- // Convert Buffers to strings if needed
- const keyStr = Buffer.isBuffer(key) ? key.toString() : key
- const valueStr = Buffer.isBuffer(value) ? value.toString() : value
- normalizedHeaders[keyStr.toLowerCase()] = valueStr
- }
- }
- return normalizedHeaders
- }
-
- // Handle object format
- if (headers && typeof headers === 'object') {
- for (const [key, value] of Object.entries(headers)) {
- if (key && typeof key === 'string') {
- normalizedHeaders[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : String(value)
- }
- }
- }
-
- return normalizedHeaders
-}
-
-const validSnapshotModes = /** @type {const} */ (['record', 'playback', 'update'])
-
-/** @typedef {typeof validSnapshotModes[number]} SnapshotMode */
-
-/**
- * @param {*} mode - The snapshot mode to validate
- * @returns {asserts mode is SnapshotMode}
- */
-function validateSnapshotMode (mode) {
- if (!validSnapshotModes.includes(mode)) {
- throw new InvalidArgumentError(`Invalid snapshot mode: ${mode}. Must be one of: ${validSnapshotModes.join(', ')}`)
- }
-}
-
-module.exports = {
- createHeaderFilters,
- hashId,
- isUndiciHeaders,
- normalizeHeaders,
- isUrlExcludedFactory,
- validateSnapshotMode
-}
diff --git a/vanilla/node_modules/undici/lib/util/cache.js b/vanilla/node_modules/undici/lib/util/cache.js
deleted file mode 100644
index b7627d0..0000000
--- a/vanilla/node_modules/undici/lib/util/cache.js
+++ /dev/null
@@ -1,405 +0,0 @@
-'use strict'
-
-const {
- safeHTTPMethods,
- pathHasQueryOrFragment
-} = require('../core/util')
-
-const { serializePathWithQuery } = require('../core/util')
-
-/**
- * @param {import('../../types/dispatcher.d.ts').default.DispatchOptions} opts
- */
-function makeCacheKey (opts) {
- if (!opts.origin) {
- throw new Error('opts.origin is undefined')
- }
-
- let fullPath = opts.path || '/'
-
- if (opts.query && !pathHasQueryOrFragment(opts.path)) {
- fullPath = serializePathWithQuery(fullPath, opts.query)
- }
-
- return {
- origin: opts.origin.toString(),
- method: opts.method,
- path: fullPath,
- headers: opts.headers
- }
-}
-
-/**
- * @param {Record<string, string[] | string>}
- * @returns {Record<string, string[] | string>}
- */
-function normalizeHeaders (opts) {
- let headers
- if (opts.headers == null) {
- headers = {}
- } else if (typeof opts.headers[Symbol.iterator] === 'function') {
- headers = {}
- for (const x of opts.headers) {
- if (!Array.isArray(x)) {
- throw new Error('opts.headers is not a valid header map')
- }
- const [key, val] = x
- if (typeof key !== 'string' || typeof val !== 'string') {
- throw new Error('opts.headers is not a valid header map')
- }
- headers[key.toLowerCase()] = val
- }
- } else if (typeof opts.headers === 'object') {
- headers = {}
-
- for (const key of Object.keys(opts.headers)) {
- headers[key.toLowerCase()] = opts.headers[key]
- }
- } else {
- throw new Error('opts.headers is not an object')
- }
-
- return headers
-}
-
-/**
- * @param {any} key
- */
-function assertCacheKey (key) {
- if (typeof key !== 'object') {
- throw new TypeError(`expected key to be object, got ${typeof key}`)
- }
-
- for (const property of ['origin', 'method', 'path']) {
- if (typeof key[property] !== 'string') {
- throw new TypeError(`expected key.${property} to be string, got ${typeof key[property]}`)
- }
- }
-
- if (key.headers !== undefined && typeof key.headers !== 'object') {
- throw new TypeError(`expected headers to be object, got ${typeof key}`)
- }
-}
-
-/**
- * @param {any} value
- */
-function assertCacheValue (value) {
- if (typeof value !== 'object') {
- throw new TypeError(`expected value to be object, got ${typeof value}`)
- }
-
- for (const property of ['statusCode', 'cachedAt', 'staleAt', 'deleteAt']) {
- if (typeof value[property] !== 'number') {
- throw new TypeError(`expected value.${property} to be number, got ${typeof value[property]}`)
- }
- }
-
- if (typeof value.statusMessage !== 'string') {
- throw new TypeError(`expected value.statusMessage to be string, got ${typeof value.statusMessage}`)
- }
-
- if (value.headers != null && typeof value.headers !== 'object') {
- throw new TypeError(`expected value.rawHeaders to be object, got ${typeof value.headers}`)
- }
-
- if (value.vary !== undefined && typeof value.vary !== 'object') {
- throw new TypeError(`expected value.vary to be object, got ${typeof value.vary}`)
- }
-
- if (value.etag !== undefined && typeof value.etag !== 'string') {
- throw new TypeError(`expected value.etag to be string, got ${typeof value.etag}`)
- }
-}
-
-/**
- * @see https://www.rfc-editor.org/rfc/rfc9111.html#name-cache-control
- * @see https://www.iana.org/assignments/http-cache-directives/http-cache-directives.xhtml
-
- * @param {string | string[]} header
- * @returns {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives}
- */
-function parseCacheControlHeader (header) {
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives}
- */
- const output = {}
-
- let directives
- if (Array.isArray(header)) {
- directives = []
-
- for (const directive of header) {
- directives.push(...directive.split(','))
- }
- } else {
- directives = header.split(',')
- }
-
- for (let i = 0; i < directives.length; i++) {
- const directive = directives[i].toLowerCase()
- const keyValueDelimiter = directive.indexOf('=')
-
- let key
- let value
- if (keyValueDelimiter !== -1) {
- key = directive.substring(0, keyValueDelimiter).trimStart()
- value = directive.substring(keyValueDelimiter + 1)
- } else {
- key = directive.trim()
- }
-
- switch (key) {
- case 'min-fresh':
- case 'max-stale':
- case 'max-age':
- case 's-maxage':
- case 'stale-while-revalidate':
- case 'stale-if-error': {
- if (value === undefined || value[0] === ' ') {
- continue
- }
-
- if (
- value.length >= 2 &&
- value[0] === '"' &&
- value[value.length - 1] === '"'
- ) {
- value = value.substring(1, value.length - 1)
- }
-
- const parsedValue = parseInt(value, 10)
- // eslint-disable-next-line no-self-compare
- if (parsedValue !== parsedValue) {
- continue
- }
-
- if (key === 'max-age' && key in output && output[key] >= parsedValue) {
- continue
- }
-
- output[key] = parsedValue
-
- break
- }
- case 'private':
- case 'no-cache': {
- if (value) {
- // The private and no-cache directives can be unqualified (aka just
- // `private` or `no-cache`) or qualified (w/ a value). When they're
- // qualified, it's a list of headers like `no-cache=header1`,
- // `no-cache="header1"`, or `no-cache="header1, header2"`
- // If we're given multiple headers, the comma messes us up since
- // we split the full header by commas. So, let's loop through the
- // remaining parts in front of us until we find one that ends in a
- // quote. We can then just splice all of the parts in between the
- // starting quote and the ending quote out of the directives array
- // and continue parsing like normal.
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-no-cache-2
- if (value[0] === '"') {
- // Something like `no-cache="some-header"` OR `no-cache="some-header, another-header"`.
-
- // Add the first header on and cut off the leading quote
- const headers = [value.substring(1)]
-
- let foundEndingQuote = value[value.length - 1] === '"'
- if (!foundEndingQuote) {
- // Something like `no-cache="some-header, another-header"`
- // This can still be something invalid, e.g. `no-cache="some-header, ...`
- for (let j = i + 1; j < directives.length; j++) {
- const nextPart = directives[j]
- const nextPartLength = nextPart.length
-
- headers.push(nextPart.trim())
-
- if (nextPartLength !== 0 && nextPart[nextPartLength - 1] === '"') {
- foundEndingQuote = true
- break
- }
- }
- }
-
- if (foundEndingQuote) {
- let lastHeader = headers[headers.length - 1]
- if (lastHeader[lastHeader.length - 1] === '"') {
- lastHeader = lastHeader.substring(0, lastHeader.length - 1)
- headers[headers.length - 1] = lastHeader
- }
-
- if (key in output) {
- output[key] = output[key].concat(headers)
- } else {
- output[key] = headers
- }
- }
- } else {
- // Something like `no-cache="some-header"`
- if (key in output) {
- output[key] = output[key].concat(value)
- } else {
- output[key] = [value]
- }
- }
-
- break
- }
- }
- // eslint-disable-next-line no-fallthrough
- case 'public':
- case 'no-store':
- case 'must-revalidate':
- case 'proxy-revalidate':
- case 'immutable':
- case 'no-transform':
- case 'must-understand':
- case 'only-if-cached':
- if (value) {
- // These are qualified (something like `public=...`) when they aren't
- // allowed to be, skip
- continue
- }
-
- output[key] = true
- break
- default:
- // Ignore unknown directives as per https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.3-1
- continue
- }
- }
-
- return output
-}
-
-/**
- * @param {string | string[]} varyHeader Vary header from the server
- * @param {Record<string, string | string[]>} headers Request headers
- * @returns {Record<string, string | string[]>}
- */
-function parseVaryHeader (varyHeader, headers) {
- if (typeof varyHeader === 'string' && varyHeader.includes('*')) {
- return headers
- }
-
- const output = /** @type {Record<string, string | string[] | null>} */ ({})
-
- const varyingHeaders = typeof varyHeader === 'string'
- ? varyHeader.split(',')
- : varyHeader
-
- for (const header of varyingHeaders) {
- const trimmedHeader = header.trim().toLowerCase()
-
- output[trimmedHeader] = headers[trimmedHeader] ?? null
- }
-
- return output
-}
-
-/**
- * Note: this deviates from the spec a little. Empty etags ("", W/"") are valid,
- * however, including them in cached resposnes serves little to no purpose.
- *
- * @see https://www.rfc-editor.org/rfc/rfc9110.html#name-etag
- *
- * @param {string} etag
- * @returns {boolean}
- */
-function isEtagUsable (etag) {
- if (etag.length <= 2) {
- // Shortest an etag can be is two chars (just ""). This is where we deviate
- // from the spec requiring a min of 3 chars however
- return false
- }
-
- if (etag[0] === '"' && etag[etag.length - 1] === '"') {
- // ETag: ""asd123"" or ETag: "W/"asd123"", kinda undefined behavior in the
- // spec. Some servers will accept these while others don't.
- // ETag: "asd123"
- return !(etag[1] === '"' || etag.startsWith('"W/'))
- }
-
- if (etag.startsWith('W/"') && etag[etag.length - 1] === '"') {
- // ETag: W/"", also where we deviate from the spec & require a min of 3
- // chars
- // ETag: for W/"", W/"asd123"
- return etag.length !== 4
- }
-
- // Anything else
- return false
-}
-
-/**
- * @param {unknown} store
- * @returns {asserts store is import('../../types/cache-interceptor.d.ts').default.CacheStore}
- */
-function assertCacheStore (store, name = 'CacheStore') {
- if (typeof store !== 'object' || store === null) {
- throw new TypeError(`expected type of ${name} to be a CacheStore, got ${store === null ? 'null' : typeof store}`)
- }
-
- for (const fn of ['get', 'createWriteStream', 'delete']) {
- if (typeof store[fn] !== 'function') {
- throw new TypeError(`${name} needs to have a \`${fn}()\` function`)
- }
- }
-}
-/**
- * @param {unknown} methods
- * @returns {asserts methods is import('../../types/cache-interceptor.d.ts').default.CacheMethods[]}
- */
-function assertCacheMethods (methods, name = 'CacheMethods') {
- if (!Array.isArray(methods)) {
- throw new TypeError(`expected type of ${name} needs to be an array, got ${methods === null ? 'null' : typeof methods}`)
- }
-
- if (methods.length === 0) {
- throw new TypeError(`${name} needs to have at least one method`)
- }
-
- for (const method of methods) {
- if (!safeHTTPMethods.includes(method)) {
- throw new TypeError(`element of ${name}-array needs to be one of following values: ${safeHTTPMethods.join(', ')}, got ${method}`)
- }
- }
-}
-
-/**
- * Creates a string key for request deduplication purposes.
- * This key is used to identify in-flight requests that can be shared.
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey
- * @param {Set<string>} [excludeHeaders] Set of lowercase header names to exclude from the key
- * @returns {string}
- */
-function makeDeduplicationKey (cacheKey, excludeHeaders) {
- // Create a deterministic string key from the cache key
- // Include origin, method, path, and sorted headers
- let key = `${cacheKey.origin}:${cacheKey.method}:${cacheKey.path}`
-
- if (cacheKey.headers) {
- const sortedHeaders = Object.keys(cacheKey.headers).sort()
- for (const header of sortedHeaders) {
- // Skip excluded headers
- if (excludeHeaders?.has(header.toLowerCase())) {
- continue
- }
- const value = cacheKey.headers[header]
- key += `:${header}=${Array.isArray(value) ? value.join(',') : value}`
- }
- }
-
- return key
-}
-
-module.exports = {
- makeCacheKey,
- normalizeHeaders,
- assertCacheKey,
- assertCacheValue,
- parseCacheControlHeader,
- parseVaryHeader,
- isEtagUsable,
- assertCacheMethods,
- assertCacheStore,
- makeDeduplicationKey
-}
diff --git a/vanilla/node_modules/undici/lib/util/date.js b/vanilla/node_modules/undici/lib/util/date.js
deleted file mode 100644
index 99dd150..0000000
--- a/vanilla/node_modules/undici/lib/util/date.js
+++ /dev/null
@@ -1,653 +0,0 @@
-'use strict'
-
-/**
- * @see https://www.rfc-editor.org/rfc/rfc9110.html#name-date-time-formats
- *
- * @param {string} date
- * @returns {Date | undefined}
- */
-function parseHttpDate (date) {
- // Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate
- // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
- // Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format
-
- switch (date[3]) {
- case ',': return parseImfDate(date)
- case ' ': return parseAscTimeDate(date)
- default: return parseRfc850Date(date)
- }
-}
-
-/**
- * @see https://httpwg.org/specs/rfc9110.html#preferred.date.format
- *
- * @param {string} date
- * @returns {Date | undefined}
- */
-function parseImfDate (date) {
- if (
- date.length !== 29 ||
- date[4] !== ' ' ||
- date[7] !== ' ' ||
- date[11] !== ' ' ||
- date[16] !== ' ' ||
- date[19] !== ':' ||
- date[22] !== ':' ||
- date[25] !== ' ' ||
- date[26] !== 'G' ||
- date[27] !== 'M' ||
- date[28] !== 'T'
- ) {
- return undefined
- }
-
- let weekday = -1
- if (date[0] === 'S' && date[1] === 'u' && date[2] === 'n') { // Sunday
- weekday = 0
- } else if (date[0] === 'M' && date[1] === 'o' && date[2] === 'n') { // Monday
- weekday = 1
- } else if (date[0] === 'T' && date[1] === 'u' && date[2] === 'e') { // Tuesday
- weekday = 2
- } else if (date[0] === 'W' && date[1] === 'e' && date[2] === 'd') { // Wednesday
- weekday = 3
- } else if (date[0] === 'T' && date[1] === 'h' && date[2] === 'u') { // Thursday
- weekday = 4
- } else if (date[0] === 'F' && date[1] === 'r' && date[2] === 'i') { // Friday
- weekday = 5
- } else if (date[0] === 'S' && date[1] === 'a' && date[2] === 't') { // Saturday
- weekday = 6
- } else {
- return undefined // Not a valid day of the week
- }
-
- let day = 0
- if (date[5] === '0') {
- // Single digit day, e.g. "Sun Nov 6 08:49:37 1994"
- const code = date.charCodeAt(6)
- if (code < 49 || code > 57) {
- return undefined // Not a digit
- }
- day = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(5)
- if (code1 < 49 || code1 > 51) {
- return undefined // Not a digit between 1 and 3
- }
- const code2 = date.charCodeAt(6)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- day = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- let monthIdx = -1
- if (
- (date[8] === 'J' && date[9] === 'a' && date[10] === 'n')
- ) {
- monthIdx = 0 // Jan
- } else if (
- (date[8] === 'F' && date[9] === 'e' && date[10] === 'b')
- ) {
- monthIdx = 1 // Feb
- } else if (
- (date[8] === 'M' && date[9] === 'a')
- ) {
- if (date[10] === 'r') {
- monthIdx = 2 // Mar
- } else if (date[10] === 'y') {
- monthIdx = 4 // May
- } else {
- return undefined // Invalid month
- }
- } else if (
- (date[8] === 'J')
- ) {
- if (date[9] === 'a' && date[10] === 'n') {
- monthIdx = 0 // Jan
- } else if (date[9] === 'u') {
- if (date[10] === 'n') {
- monthIdx = 5 // Jun
- } else if (date[10] === 'l') {
- monthIdx = 6 // Jul
- } else {
- return undefined // Invalid month
- }
- } else {
- return undefined // Invalid month
- }
- } else if (
- (date[8] === 'A')
- ) {
- if (date[9] === 'p' && date[10] === 'r') {
- monthIdx = 3 // Apr
- } else if (date[9] === 'u' && date[10] === 'g') {
- monthIdx = 7 // Aug
- } else {
- return undefined // Invalid month
- }
- } else if (
- (date[8] === 'S' && date[9] === 'e' && date[10] === 'p')
- ) {
- monthIdx = 8 // Sep
- } else if (
- (date[8] === 'O' && date[9] === 'c' && date[10] === 't')
- ) {
- monthIdx = 9 // Oct
- } else if (
- (date[8] === 'N' && date[9] === 'o' && date[10] === 'v')
- ) {
- monthIdx = 10 // Nov
- } else if (
- (date[8] === 'D' && date[9] === 'e' && date[10] === 'c')
- ) {
- monthIdx = 11 // Dec
- } else {
- // Not a valid month
- return undefined
- }
-
- const yearDigit1 = date.charCodeAt(12)
- if (yearDigit1 < 48 || yearDigit1 > 57) {
- return undefined // Not a digit
- }
- const yearDigit2 = date.charCodeAt(13)
- if (yearDigit2 < 48 || yearDigit2 > 57) {
- return undefined // Not a digit
- }
- const yearDigit3 = date.charCodeAt(14)
- if (yearDigit3 < 48 || yearDigit3 > 57) {
- return undefined // Not a digit
- }
- const yearDigit4 = date.charCodeAt(15)
- if (yearDigit4 < 48 || yearDigit4 > 57) {
- return undefined // Not a digit
- }
- const year = (yearDigit1 - 48) * 1000 + (yearDigit2 - 48) * 100 + (yearDigit3 - 48) * 10 + (yearDigit4 - 48)
-
- let hour = 0
- if (date[17] === '0') {
- const code = date.charCodeAt(18)
- if (code < 48 || code > 57) {
- return undefined // Not a digit
- }
- hour = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(17)
- if (code1 < 48 || code1 > 50) {
- return undefined // Not a digit between 0 and 2
- }
- const code2 = date.charCodeAt(18)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- if (code1 === 50 && code2 > 51) {
- return undefined // Hour cannot be greater than 23
- }
- hour = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- let minute = 0
- if (date[20] === '0') {
- const code = date.charCodeAt(21)
- if (code < 48 || code > 57) {
- return undefined // Not a digit
- }
- minute = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(20)
- if (code1 < 48 || code1 > 53) {
- return undefined // Not a digit between 0 and 5
- }
- const code2 = date.charCodeAt(21)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- minute = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- let second = 0
- if (date[23] === '0') {
- const code = date.charCodeAt(24)
- if (code < 48 || code > 57) {
- return undefined // Not a digit
- }
- second = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(23)
- if (code1 < 48 || code1 > 53) {
- return undefined // Not a digit between 0 and 5
- }
- const code2 = date.charCodeAt(24)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- second = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second))
- return result.getUTCDay() === weekday ? result : undefined
-}
-
-/**
- * @see https://httpwg.org/specs/rfc9110.html#obsolete.date.formats
- *
- * @param {string} date
- * @returns {Date | undefined}
- */
-function parseAscTimeDate (date) {
- // This is assumed to be in UTC
-
- if (
- date.length !== 24 ||
- date[7] !== ' ' ||
- date[10] !== ' ' ||
- date[19] !== ' '
- ) {
- return undefined
- }
-
- let weekday = -1
- if (date[0] === 'S' && date[1] === 'u' && date[2] === 'n') { // Sunday
- weekday = 0
- } else if (date[0] === 'M' && date[1] === 'o' && date[2] === 'n') { // Monday
- weekday = 1
- } else if (date[0] === 'T' && date[1] === 'u' && date[2] === 'e') { // Tuesday
- weekday = 2
- } else if (date[0] === 'W' && date[1] === 'e' && date[2] === 'd') { // Wednesday
- weekday = 3
- } else if (date[0] === 'T' && date[1] === 'h' && date[2] === 'u') { // Thursday
- weekday = 4
- } else if (date[0] === 'F' && date[1] === 'r' && date[2] === 'i') { // Friday
- weekday = 5
- } else if (date[0] === 'S' && date[1] === 'a' && date[2] === 't') { // Saturday
- weekday = 6
- } else {
- return undefined // Not a valid day of the week
- }
-
- let monthIdx = -1
- if (
- (date[4] === 'J' && date[5] === 'a' && date[6] === 'n')
- ) {
- monthIdx = 0 // Jan
- } else if (
- (date[4] === 'F' && date[5] === 'e' && date[6] === 'b')
- ) {
- monthIdx = 1 // Feb
- } else if (
- (date[4] === 'M' && date[5] === 'a')
- ) {
- if (date[6] === 'r') {
- monthIdx = 2 // Mar
- } else if (date[6] === 'y') {
- monthIdx = 4 // May
- } else {
- return undefined // Invalid month
- }
- } else if (
- (date[4] === 'J')
- ) {
- if (date[5] === 'a' && date[6] === 'n') {
- monthIdx = 0 // Jan
- } else if (date[5] === 'u') {
- if (date[6] === 'n') {
- monthIdx = 5 // Jun
- } else if (date[6] === 'l') {
- monthIdx = 6 // Jul
- } else {
- return undefined // Invalid month
- }
- } else {
- return undefined // Invalid month
- }
- } else if (
- (date[4] === 'A')
- ) {
- if (date[5] === 'p' && date[6] === 'r') {
- monthIdx = 3 // Apr
- } else if (date[5] === 'u' && date[6] === 'g') {
- monthIdx = 7 // Aug
- } else {
- return undefined // Invalid month
- }
- } else if (
- (date[4] === 'S' && date[5] === 'e' && date[6] === 'p')
- ) {
- monthIdx = 8 // Sep
- } else if (
- (date[4] === 'O' && date[5] === 'c' && date[6] === 't')
- ) {
- monthIdx = 9 // Oct
- } else if (
- (date[4] === 'N' && date[5] === 'o' && date[6] === 'v')
- ) {
- monthIdx = 10 // Nov
- } else if (
- (date[4] === 'D' && date[5] === 'e' && date[6] === 'c')
- ) {
- monthIdx = 11 // Dec
- } else {
- // Not a valid month
- return undefined
- }
-
- let day = 0
- if (date[8] === ' ') {
- // Single digit day, e.g. "Sun Nov 6 08:49:37 1994"
- const code = date.charCodeAt(9)
- if (code < 49 || code > 57) {
- return undefined // Not a digit
- }
- day = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(8)
- if (code1 < 49 || code1 > 51) {
- return undefined // Not a digit between 1 and 3
- }
- const code2 = date.charCodeAt(9)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- day = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- let hour = 0
- if (date[11] === '0') {
- const code = date.charCodeAt(12)
- if (code < 48 || code > 57) {
- return undefined // Not a digit
- }
- hour = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(11)
- if (code1 < 48 || code1 > 50) {
- return undefined // Not a digit between 0 and 2
- }
- const code2 = date.charCodeAt(12)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- if (code1 === 50 && code2 > 51) {
- return undefined // Hour cannot be greater than 23
- }
- hour = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- let minute = 0
- if (date[14] === '0') {
- const code = date.charCodeAt(15)
- if (code < 48 || code > 57) {
- return undefined // Not a digit
- }
- minute = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(14)
- if (code1 < 48 || code1 > 53) {
- return undefined // Not a digit between 0 and 5
- }
- const code2 = date.charCodeAt(15)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- minute = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- let second = 0
- if (date[17] === '0') {
- const code = date.charCodeAt(18)
- if (code < 48 || code > 57) {
- return undefined // Not a digit
- }
- second = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(17)
- if (code1 < 48 || code1 > 53) {
- return undefined // Not a digit between 0 and 5
- }
- const code2 = date.charCodeAt(18)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- second = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- const yearDigit1 = date.charCodeAt(20)
- if (yearDigit1 < 48 || yearDigit1 > 57) {
- return undefined // Not a digit
- }
- const yearDigit2 = date.charCodeAt(21)
- if (yearDigit2 < 48 || yearDigit2 > 57) {
- return undefined // Not a digit
- }
- const yearDigit3 = date.charCodeAt(22)
- if (yearDigit3 < 48 || yearDigit3 > 57) {
- return undefined // Not a digit
- }
- const yearDigit4 = date.charCodeAt(23)
- if (yearDigit4 < 48 || yearDigit4 > 57) {
- return undefined // Not a digit
- }
- const year = (yearDigit1 - 48) * 1000 + (yearDigit2 - 48) * 100 + (yearDigit3 - 48) * 10 + (yearDigit4 - 48)
-
- const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second))
- return result.getUTCDay() === weekday ? result : undefined
-}
-
-/**
- * @see https://httpwg.org/specs/rfc9110.html#obsolete.date.formats
- *
- * @param {string} date
- * @returns {Date | undefined}
- */
-function parseRfc850Date (date) {
- let commaIndex = -1
-
- let weekday = -1
- if (date[0] === 'S') {
- if (date[1] === 'u' && date[2] === 'n' && date[3] === 'd' && date[4] === 'a' && date[5] === 'y') {
- weekday = 0 // Sunday
- commaIndex = 6
- } else if (date[1] === 'a' && date[2] === 't' && date[3] === 'u' && date[4] === 'r' && date[5] === 'd' && date[6] === 'a' && date[7] === 'y') {
- weekday = 6 // Saturday
- commaIndex = 8
- }
- } else if (date[0] === 'M' && date[1] === 'o' && date[2] === 'n' && date[3] === 'd' && date[4] === 'a' && date[5] === 'y') {
- weekday = 1 // Monday
- commaIndex = 6
- } else if (date[0] === 'T') {
- if (date[1] === 'u' && date[2] === 'e' && date[3] === 's' && date[4] === 'd' && date[5] === 'a' && date[6] === 'y') {
- weekday = 2 // Tuesday
- commaIndex = 7
- } else if (date[1] === 'h' && date[2] === 'u' && date[3] === 'r' && date[4] === 's' && date[5] === 'd' && date[6] === 'a' && date[7] === 'y') {
- weekday = 4 // Thursday
- commaIndex = 8
- }
- } else if (date[0] === 'W' && date[1] === 'e' && date[2] === 'd' && date[3] === 'n' && date[4] === 'e' && date[5] === 's' && date[6] === 'd' && date[7] === 'a' && date[8] === 'y') {
- weekday = 3 // Wednesday
- commaIndex = 9
- } else if (date[0] === 'F' && date[1] === 'r' && date[2] === 'i' && date[3] === 'd' && date[4] === 'a' && date[5] === 'y') {
- weekday = 5 // Friday
- commaIndex = 6
- } else {
- // Not a valid day name
- return undefined
- }
-
- if (
- date[commaIndex] !== ',' ||
- (date.length - commaIndex - 1) !== 23 ||
- date[commaIndex + 1] !== ' ' ||
- date[commaIndex + 4] !== '-' ||
- date[commaIndex + 8] !== '-' ||
- date[commaIndex + 11] !== ' ' ||
- date[commaIndex + 14] !== ':' ||
- date[commaIndex + 17] !== ':' ||
- date[commaIndex + 20] !== ' ' ||
- date[commaIndex + 21] !== 'G' ||
- date[commaIndex + 22] !== 'M' ||
- date[commaIndex + 23] !== 'T'
- ) {
- return undefined
- }
-
- let day = 0
- if (date[commaIndex + 2] === '0') {
- // Single digit day, e.g. "Sun Nov 6 08:49:37 1994"
- const code = date.charCodeAt(commaIndex + 3)
- if (code < 49 || code > 57) {
- return undefined // Not a digit
- }
- day = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(commaIndex + 2)
- if (code1 < 49 || code1 > 51) {
- return undefined // Not a digit between 1 and 3
- }
- const code2 = date.charCodeAt(commaIndex + 3)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- day = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- let monthIdx = -1
- if (
- (date[commaIndex + 5] === 'J' && date[commaIndex + 6] === 'a' && date[commaIndex + 7] === 'n')
- ) {
- monthIdx = 0 // Jan
- } else if (
- (date[commaIndex + 5] === 'F' && date[commaIndex + 6] === 'e' && date[commaIndex + 7] === 'b')
- ) {
- monthIdx = 1 // Feb
- } else if (
- (date[commaIndex + 5] === 'M' && date[commaIndex + 6] === 'a' && date[commaIndex + 7] === 'r')
- ) {
- monthIdx = 2 // Mar
- } else if (
- (date[commaIndex + 5] === 'A' && date[commaIndex + 6] === 'p' && date[commaIndex + 7] === 'r')
- ) {
- monthIdx = 3 // Apr
- } else if (
- (date[commaIndex + 5] === 'M' && date[commaIndex + 6] === 'a' && date[commaIndex + 7] === 'y')
- ) {
- monthIdx = 4 // May
- } else if (
- (date[commaIndex + 5] === 'J' && date[commaIndex + 6] === 'u' && date[commaIndex + 7] === 'n')
- ) {
- monthIdx = 5 // Jun
- } else if (
- (date[commaIndex + 5] === 'J' && date[commaIndex + 6] === 'u' && date[commaIndex + 7] === 'l')
- ) {
- monthIdx = 6 // Jul
- } else if (
- (date[commaIndex + 5] === 'A' && date[commaIndex + 6] === 'u' && date[commaIndex + 7] === 'g')
- ) {
- monthIdx = 7 // Aug
- } else if (
- (date[commaIndex + 5] === 'S' && date[commaIndex + 6] === 'e' && date[commaIndex + 7] === 'p')
- ) {
- monthIdx = 8 // Sep
- } else if (
- (date[commaIndex + 5] === 'O' && date[commaIndex + 6] === 'c' && date[commaIndex + 7] === 't')
- ) {
- monthIdx = 9 // Oct
- } else if (
- (date[commaIndex + 5] === 'N' && date[commaIndex + 6] === 'o' && date[commaIndex + 7] === 'v')
- ) {
- monthIdx = 10 // Nov
- } else if (
- (date[commaIndex + 5] === 'D' && date[commaIndex + 6] === 'e' && date[commaIndex + 7] === 'c')
- ) {
- monthIdx = 11 // Dec
- } else {
- // Not a valid month
- return undefined
- }
-
- const yearDigit1 = date.charCodeAt(commaIndex + 9)
- if (yearDigit1 < 48 || yearDigit1 > 57) {
- return undefined // Not a digit
- }
- const yearDigit2 = date.charCodeAt(commaIndex + 10)
- if (yearDigit2 < 48 || yearDigit2 > 57) {
- return undefined // Not a digit
- }
-
- let year = (yearDigit1 - 48) * 10 + (yearDigit2 - 48) // Convert ASCII codes to number
-
- // RFC 6265 states that the year is in the range 1970-2069.
- // @see https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.1
- //
- // 3. If the year-value is greater than or equal to 70 and less than or
- // equal to 99, increment the year-value by 1900.
- // 4. If the year-value is greater than or equal to 0 and less than or
- // equal to 69, increment the year-value by 2000.
- year += year < 70 ? 2000 : 1900
-
- let hour = 0
- if (date[commaIndex + 12] === '0') {
- const code = date.charCodeAt(commaIndex + 13)
- if (code < 48 || code > 57) {
- return undefined // Not a digit
- }
- hour = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(commaIndex + 12)
- if (code1 < 48 || code1 > 50) {
- return undefined // Not a digit between 0 and 2
- }
- const code2 = date.charCodeAt(commaIndex + 13)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- if (code1 === 50 && code2 > 51) {
- return undefined // Hour cannot be greater than 23
- }
- hour = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- let minute = 0
- if (date[commaIndex + 15] === '0') {
- const code = date.charCodeAt(commaIndex + 16)
- if (code < 48 || code > 57) {
- return undefined // Not a digit
- }
- minute = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(commaIndex + 15)
- if (code1 < 48 || code1 > 53) {
- return undefined // Not a digit between 0 and 5
- }
- const code2 = date.charCodeAt(commaIndex + 16)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- minute = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- let second = 0
- if (date[commaIndex + 18] === '0') {
- const code = date.charCodeAt(commaIndex + 19)
- if (code < 48 || code > 57) {
- return undefined // Not a digit
- }
- second = code - 48 // Convert ASCII code to number
- } else {
- const code1 = date.charCodeAt(commaIndex + 18)
- if (code1 < 48 || code1 > 53) {
- return undefined // Not a digit between 0 and 5
- }
- const code2 = date.charCodeAt(commaIndex + 19)
- if (code2 < 48 || code2 > 57) {
- return undefined // Not a digit
- }
- second = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number
- }
-
- const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second))
- return result.getUTCDay() === weekday ? result : undefined
-}
-
-module.exports = {
- parseHttpDate
-}
diff --git a/vanilla/node_modules/undici/lib/util/promise.js b/vanilla/node_modules/undici/lib/util/promise.js
deleted file mode 100644
index 048f86e..0000000
--- a/vanilla/node_modules/undici/lib/util/promise.js
+++ /dev/null
@@ -1,28 +0,0 @@
-'use strict'
-
-/**
- * @template {*} T
- * @typedef {Object} DeferredPromise
- * @property {Promise<T>} promise
- * @property {(value?: T) => void} resolve
- * @property {(reason?: any) => void} reject
- */
-
-/**
- * @template {*} T
- * @returns {DeferredPromise<T>} An object containing a promise and its resolve/reject methods.
- */
-function createDeferredPromise () {
- let res
- let rej
- const promise = new Promise((resolve, reject) => {
- res = resolve
- rej = reject
- })
-
- return { promise, resolve: res, reject: rej }
-}
-
-module.exports = {
- createDeferredPromise
-}
diff --git a/vanilla/node_modules/undici/lib/util/runtime-features.js b/vanilla/node_modules/undici/lib/util/runtime-features.js
deleted file mode 100644
index 3e62dc0..0000000
--- a/vanilla/node_modules/undici/lib/util/runtime-features.js
+++ /dev/null
@@ -1,124 +0,0 @@
-'use strict'
-
-/** @typedef {`node:${string}`} NodeModuleName */
-
-/** @type {Record<NodeModuleName, () => any>} */
-const lazyLoaders = {
- __proto__: null,
- 'node:crypto': () => require('node:crypto'),
- 'node:sqlite': () => require('node:sqlite'),
- 'node:worker_threads': () => require('node:worker_threads'),
- 'node:zlib': () => require('node:zlib')
-}
-
-/**
- * @param {NodeModuleName} moduleName
- * @returns {boolean}
- */
-function detectRuntimeFeatureByNodeModule (moduleName) {
- try {
- lazyLoaders[moduleName]()
- return true
- } catch (err) {
- if (err.code !== 'ERR_UNKNOWN_BUILTIN_MODULE' && err.code !== 'ERR_NO_CRYPTO') {
- throw err
- }
- return false
- }
-}
-
-/**
- * @param {NodeModuleName} moduleName
- * @param {string} property
- * @returns {boolean}
- */
-function detectRuntimeFeatureByExportedProperty (moduleName, property) {
- const module = lazyLoaders[moduleName]()
- return typeof module[property] !== 'undefined'
-}
-
-const runtimeFeaturesByExportedProperty = /** @type {const} */ (['markAsUncloneable', 'zstd'])
-
-/** @type {Record<RuntimeFeatureByExportedProperty, [NodeModuleName, string]>} */
-const exportedPropertyLookup = {
- markAsUncloneable: ['node:worker_threads', 'markAsUncloneable'],
- zstd: ['node:zlib', 'createZstdDecompress']
-}
-
-/** @typedef {typeof runtimeFeaturesByExportedProperty[number]} RuntimeFeatureByExportedProperty */
-
-const runtimeFeaturesAsNodeModule = /** @type {const} */ (['crypto', 'sqlite'])
-/** @typedef {typeof runtimeFeaturesAsNodeModule[number]} RuntimeFeatureByNodeModule */
-
-const features = /** @type {const} */ ([
- ...runtimeFeaturesAsNodeModule,
- ...runtimeFeaturesByExportedProperty
-])
-
-/** @typedef {typeof features[number]} Feature */
-
-/**
- * @param {Feature} feature
- * @returns {boolean}
- */
-function detectRuntimeFeature (feature) {
- if (runtimeFeaturesAsNodeModule.includes(/** @type {RuntimeFeatureByNodeModule} */ (feature))) {
- return detectRuntimeFeatureByNodeModule(`node:${feature}`)
- } else if (runtimeFeaturesByExportedProperty.includes(/** @type {RuntimeFeatureByExportedProperty} */ (feature))) {
- const [moduleName, property] = exportedPropertyLookup[feature]
- return detectRuntimeFeatureByExportedProperty(moduleName, property)
- }
- throw new TypeError(`unknown feature: ${feature}`)
-}
-
-/**
- * @class
- * @name RuntimeFeatures
- */
-class RuntimeFeatures {
- /** @type {Map<Feature, boolean>} */
- #map = new Map()
-
- /**
- * Clears all cached feature detections.
- */
- clear () {
- this.#map.clear()
- }
-
- /**
- * @param {Feature} feature
- * @returns {boolean}
- */
- has (feature) {
- return (
- this.#map.get(feature) ?? this.#detectRuntimeFeature(feature)
- )
- }
-
- /**
- * @param {Feature} feature
- * @param {boolean} value
- */
- set (feature, value) {
- if (features.includes(feature) === false) {
- throw new TypeError(`unknown feature: ${feature}`)
- }
- this.#map.set(feature, value)
- }
-
- /**
- * @param {Feature} feature
- * @returns {boolean}
- */
- #detectRuntimeFeature (feature) {
- const result = detectRuntimeFeature(feature)
- this.#map.set(feature, result)
- return result
- }
-}
-
-const instance = new RuntimeFeatures()
-
-module.exports.runtimeFeatures = instance
-module.exports.default = instance
diff --git a/vanilla/node_modules/undici/lib/util/stats.js b/vanilla/node_modules/undici/lib/util/stats.js
deleted file mode 100644
index a13132e..0000000
--- a/vanilla/node_modules/undici/lib/util/stats.js
+++ /dev/null
@@ -1,32 +0,0 @@
-'use strict'
-
-const {
- kConnected,
- kPending,
- kRunning,
- kSize,
- kFree,
- kQueued
-} = require('../core/symbols')
-
-class ClientStats {
- constructor (client) {
- this.connected = client[kConnected]
- this.pending = client[kPending]
- this.running = client[kRunning]
- this.size = client[kSize]
- }
-}
-
-class PoolStats {
- constructor (pool) {
- this.connected = pool[kConnected]
- this.free = pool[kFree]
- this.pending = pool[kPending]
- this.queued = pool[kQueued]
- this.running = pool[kRunning]
- this.size = pool[kSize]
- }
-}
-
-module.exports = { ClientStats, PoolStats }
diff --git a/vanilla/node_modules/undici/lib/util/timers.js b/vanilla/node_modules/undici/lib/util/timers.js
deleted file mode 100644
index 14984d4..0000000
--- a/vanilla/node_modules/undici/lib/util/timers.js
+++ /dev/null
@@ -1,425 +0,0 @@
-'use strict'
-
-/**
- * This module offers an optimized timer implementation designed for scenarios
- * where high precision is not critical.
- *
- * The timer achieves faster performance by using a low-resolution approach,
- * with an accuracy target of within 500ms. This makes it particularly useful
- * for timers with delays of 1 second or more, where exact timing is less
- * crucial.
- *
- * It's important to note that Node.js timers are inherently imprecise, as
- * delays can occur due to the event loop being blocked by other operations.
- * Consequently, timers may trigger later than their scheduled time.
- */
-
-/**
- * The fastNow variable contains the internal fast timer clock value.
- *
- * @type {number}
- */
-let fastNow = 0
-
-/**
- * RESOLUTION_MS represents the target resolution time in milliseconds.
- *
- * @type {number}
- * @default 1000
- */
-const RESOLUTION_MS = 1e3
-
-/**
- * TICK_MS defines the desired interval in milliseconds between each tick.
- * The target value is set to half the resolution time, minus 1 ms, to account
- * for potential event loop overhead.
- *
- * @type {number}
- * @default 499
- */
-const TICK_MS = (RESOLUTION_MS >> 1) - 1
-
-/**
- * fastNowTimeout is a Node.js timer used to manage and process
- * the FastTimers stored in the `fastTimers` array.
- *
- * @type {NodeJS.Timeout}
- */
-let fastNowTimeout
-
-/**
- * The kFastTimer symbol is used to identify FastTimer instances.
- *
- * @type {Symbol}
- */
-const kFastTimer = Symbol('kFastTimer')
-
-/**
- * The fastTimers array contains all active FastTimers.
- *
- * @type {FastTimer[]}
- */
-const fastTimers = []
-
-/**
- * These constants represent the various states of a FastTimer.
- */
-
-/**
- * The `NOT_IN_LIST` constant indicates that the FastTimer is not included
- * in the `fastTimers` array. Timers with this status will not be processed
- * during the next tick by the `onTick` function.
- *
- * A FastTimer can be re-added to the `fastTimers` array by invoking the
- * `refresh` method on the FastTimer instance.
- *
- * @type {-2}
- */
-const NOT_IN_LIST = -2
-
-/**
- * The `TO_BE_CLEARED` constant indicates that the FastTimer is scheduled
- * for removal from the `fastTimers` array. A FastTimer in this state will
- * be removed in the next tick by the `onTick` function and will no longer
- * be processed.
- *
- * This status is also set when the `clear` method is called on the FastTimer instance.
- *
- * @type {-1}
- */
-const TO_BE_CLEARED = -1
-
-/**
- * The `PENDING` constant signifies that the FastTimer is awaiting processing
- * in the next tick by the `onTick` function. Timers with this status will have
- * their `_idleStart` value set and their status updated to `ACTIVE` in the next tick.
- *
- * @type {0}
- */
-const PENDING = 0
-
-/**
- * The `ACTIVE` constant indicates that the FastTimer is active and waiting
- * for its timer to expire. During the next tick, the `onTick` function will
- * check if the timer has expired, and if so, it will execute the associated callback.
- *
- * @type {1}
- */
-const ACTIVE = 1
-
-/**
- * The onTick function processes the fastTimers array.
- *
- * @returns {void}
- */
-function onTick () {
- /**
- * Increment the fastNow value by the TICK_MS value, despite the actual time
- * that has passed since the last tick. This approach ensures independence
- * from the system clock and delays caused by a blocked event loop.
- *
- * @type {number}
- */
- fastNow += TICK_MS
-
- /**
- * The `idx` variable is used to iterate over the `fastTimers` array.
- * Expired timers are removed by replacing them with the last element in the array.
- * Consequently, `idx` is only incremented when the current element is not removed.
- *
- * @type {number}
- */
- let idx = 0
-
- /**
- * The len variable will contain the length of the fastTimers array
- * and will be decremented when a FastTimer should be removed from the
- * fastTimers array.
- *
- * @type {number}
- */
- let len = fastTimers.length
-
- while (idx < len) {
- /**
- * @type {FastTimer}
- */
- const timer = fastTimers[idx]
-
- // If the timer is in the ACTIVE state and the timer has expired, it will
- // be processed in the next tick.
- if (timer._state === PENDING) {
- // Set the _idleStart value to the fastNow value minus the TICK_MS value
- // to account for the time the timer was in the PENDING state.
- timer._idleStart = fastNow - TICK_MS
- timer._state = ACTIVE
- } else if (
- timer._state === ACTIVE &&
- fastNow >= timer._idleStart + timer._idleTimeout
- ) {
- timer._state = TO_BE_CLEARED
- timer._idleStart = -1
- timer._onTimeout(timer._timerArg)
- }
-
- if (timer._state === TO_BE_CLEARED) {
- timer._state = NOT_IN_LIST
-
- // Move the last element to the current index and decrement len if it is
- // not the only element in the array.
- if (--len !== 0) {
- fastTimers[idx] = fastTimers[len]
- }
- } else {
- ++idx
- }
- }
-
- // Set the length of the fastTimers array to the new length and thus
- // removing the excess FastTimers elements from the array.
- fastTimers.length = len
-
- // If there are still active FastTimers in the array, refresh the Timer.
- // If there are no active FastTimers, the timer will be refreshed again
- // when a new FastTimer is instantiated.
- if (fastTimers.length !== 0) {
- refreshTimeout()
- }
-}
-
-function refreshTimeout () {
- // If the fastNowTimeout is already set and the Timer has the refresh()-
- // method available, call it to refresh the timer.
- // Some timer objects returned by setTimeout may not have a .refresh()
- // method (e.g. mocked timers in tests).
- if (fastNowTimeout?.refresh) {
- fastNowTimeout.refresh()
- // fastNowTimeout is not instantiated yet or refresh is not availabe,
- // create a new Timer.
- } else {
- clearTimeout(fastNowTimeout)
- fastNowTimeout = setTimeout(onTick, TICK_MS)
- // If the Timer has an unref method, call it to allow the process to exit,
- // if there are no other active handles. When using fake timers or mocked
- // environments (like Jest), .unref() may not be defined,
- fastNowTimeout?.unref()
- }
-}
-
-/**
- * The `FastTimer` class is a data structure designed to store and manage
- * timer information.
- */
-class FastTimer {
- [kFastTimer] = true
-
- /**
- * The state of the timer, which can be one of the following:
- * - NOT_IN_LIST (-2)
- * - TO_BE_CLEARED (-1)
- * - PENDING (0)
- * - ACTIVE (1)
- *
- * @type {-2|-1|0|1}
- * @private
- */
- _state = NOT_IN_LIST
-
- /**
- * The number of milliseconds to wait before calling the callback.
- *
- * @type {number}
- * @private
- */
- _idleTimeout = -1
-
- /**
- * The time in milliseconds when the timer was started. This value is used to
- * calculate when the timer should expire.
- *
- * @type {number}
- * @default -1
- * @private
- */
- _idleStart = -1
-
- /**
- * The function to be executed when the timer expires.
- * @type {Function}
- * @private
- */
- _onTimeout
-
- /**
- * The argument to be passed to the callback when the timer expires.
- *
- * @type {*}
- * @private
- */
- _timerArg
-
- /**
- * @constructor
- * @param {Function} callback A function to be executed after the timer
- * expires.
- * @param {number} delay The time, in milliseconds that the timer should wait
- * before the specified function or code is executed.
- * @param {*} arg
- */
- constructor (callback, delay, arg) {
- this._onTimeout = callback
- this._idleTimeout = delay
- this._timerArg = arg
-
- this.refresh()
- }
-
- /**
- * Sets the timer's start time to the current time, and reschedules the timer
- * to call its callback at the previously specified duration adjusted to the
- * current time.
- * Using this on a timer that has already called its callback will reactivate
- * the timer.
- *
- * @returns {void}
- */
- refresh () {
- // In the special case that the timer is not in the list of active timers,
- // add it back to the array to be processed in the next tick by the onTick
- // function.
- if (this._state === NOT_IN_LIST) {
- fastTimers.push(this)
- }
-
- // If the timer is the only active timer, refresh the fastNowTimeout for
- // better resolution.
- if (!fastNowTimeout || fastTimers.length === 1) {
- refreshTimeout()
- }
-
- // Setting the state to PENDING will cause the timer to be reset in the
- // next tick by the onTick function.
- this._state = PENDING
- }
-
- /**
- * The `clear` method cancels the timer, preventing it from executing.
- *
- * @returns {void}
- * @private
- */
- clear () {
- // Set the state to TO_BE_CLEARED to mark the timer for removal in the next
- // tick by the onTick function.
- this._state = TO_BE_CLEARED
-
- // Reset the _idleStart value to -1 to indicate that the timer is no longer
- // active.
- this._idleStart = -1
- }
-}
-
-/**
- * This module exports a setTimeout and clearTimeout function that can be
- * used as a drop-in replacement for the native functions.
- */
-module.exports = {
- /**
- * The setTimeout() method sets a timer which executes a function once the
- * timer expires.
- * @param {Function} callback A function to be executed after the timer
- * expires.
- * @param {number} delay The time, in milliseconds that the timer should
- * wait before the specified function or code is executed.
- * @param {*} [arg] An optional argument to be passed to the callback function
- * when the timer expires.
- * @returns {NodeJS.Timeout|FastTimer}
- */
- setTimeout (callback, delay, arg) {
- // If the delay is less than or equal to the RESOLUTION_MS value return a
- // native Node.js Timer instance.
- return delay <= RESOLUTION_MS
- ? setTimeout(callback, delay, arg)
- : new FastTimer(callback, delay, arg)
- },
- /**
- * The clearTimeout method cancels an instantiated Timer previously created
- * by calling setTimeout.
- *
- * @param {NodeJS.Timeout|FastTimer} timeout
- */
- clearTimeout (timeout) {
- // If the timeout is a FastTimer, call its own clear method.
- if (timeout[kFastTimer]) {
- /**
- * @type {FastTimer}
- */
- timeout.clear()
- // Otherwise it is an instance of a native NodeJS.Timeout, so call the
- // Node.js native clearTimeout function.
- } else {
- clearTimeout(timeout)
- }
- },
- /**
- * The setFastTimeout() method sets a fastTimer which executes a function once
- * the timer expires.
- * @param {Function} callback A function to be executed after the timer
- * expires.
- * @param {number} delay The time, in milliseconds that the timer should
- * wait before the specified function or code is executed.
- * @param {*} [arg] An optional argument to be passed to the callback function
- * when the timer expires.
- * @returns {FastTimer}
- */
- setFastTimeout (callback, delay, arg) {
- return new FastTimer(callback, delay, arg)
- },
- /**
- * The clearTimeout method cancels an instantiated FastTimer previously
- * created by calling setFastTimeout.
- *
- * @param {FastTimer} timeout
- */
- clearFastTimeout (timeout) {
- timeout.clear()
- },
- /**
- * The now method returns the value of the internal fast timer clock.
- *
- * @returns {number}
- */
- now () {
- return fastNow
- },
- /**
- * Trigger the onTick function to process the fastTimers array.
- * Exported for testing purposes only.
- * Marking as deprecated to discourage any use outside of testing.
- * @deprecated
- * @param {number} [delay=0] The delay in milliseconds to add to the now value.
- */
- tick (delay = 0) {
- fastNow += delay - RESOLUTION_MS + 1
- onTick()
- onTick()
- },
- /**
- * Reset FastTimers.
- * Exported for testing purposes only.
- * Marking as deprecated to discourage any use outside of testing.
- * @deprecated
- */
- reset () {
- fastNow = 0
- fastTimers.length = 0
- clearTimeout(fastNowTimeout)
- fastNowTimeout = null
- },
- /**
- * Exporting for testing purposes only.
- * Marking as deprecated to discourage any use outside of testing.
- * @deprecated
- */
- kFastTimer
-}
diff --git a/vanilla/node_modules/undici/lib/web/cache/cache.js b/vanilla/node_modules/undici/lib/web/cache/cache.js
deleted file mode 100644
index 10decbe..0000000
--- a/vanilla/node_modules/undici/lib/web/cache/cache.js
+++ /dev/null
@@ -1,864 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-
-const { kConstruct } = require('../../core/symbols')
-const { urlEquals, getFieldValues } = require('./util')
-const { kEnumerableProperty, isDisturbed } = require('../../core/util')
-const { webidl } = require('../webidl')
-const { cloneResponse, fromInnerResponse, getResponseState } = require('../fetch/response')
-const { Request, fromInnerRequest, getRequestState } = require('../fetch/request')
-const { fetching } = require('../fetch/index')
-const { urlIsHttpHttpsScheme, readAllBytes } = require('../fetch/util')
-const { createDeferredPromise } = require('../../util/promise')
-
-/**
- * @see https://w3c.github.io/ServiceWorker/#dfn-cache-batch-operation
- * @typedef {Object} CacheBatchOperation
- * @property {'delete' | 'put'} type
- * @property {any} request
- * @property {any} response
- * @property {import('../../../types/cache').CacheQueryOptions} options
- */
-
-/**
- * @see https://w3c.github.io/ServiceWorker/#dfn-request-response-list
- * @typedef {[any, any][]} requestResponseList
- */
-
-class Cache {
- /**
- * @see https://w3c.github.io/ServiceWorker/#dfn-relevant-request-response-list
- * @type {requestResponseList}
- */
- #relevantRequestResponseList
-
- constructor () {
- if (arguments[0] !== kConstruct) {
- webidl.illegalConstructor()
- }
-
- webidl.util.markAsUncloneable(this)
- this.#relevantRequestResponseList = arguments[1]
- }
-
- async match (request, options = {}) {
- webidl.brandCheck(this, Cache)
-
- const prefix = 'Cache.match'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- request = webidl.converters.RequestInfo(request)
- options = webidl.converters.CacheQueryOptions(options, prefix, 'options')
-
- const p = this.#internalMatchAll(request, options, 1)
-
- if (p.length === 0) {
- return
- }
-
- return p[0]
- }
-
- async matchAll (request = undefined, options = {}) {
- webidl.brandCheck(this, Cache)
-
- const prefix = 'Cache.matchAll'
- if (request !== undefined) request = webidl.converters.RequestInfo(request)
- options = webidl.converters.CacheQueryOptions(options, prefix, 'options')
-
- return this.#internalMatchAll(request, options)
- }
-
- async add (request) {
- webidl.brandCheck(this, Cache)
-
- const prefix = 'Cache.add'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- request = webidl.converters.RequestInfo(request)
-
- // 1.
- const requests = [request]
-
- // 2.
- const responseArrayPromise = this.addAll(requests)
-
- // 3.
- return await responseArrayPromise
- }
-
- async addAll (requests) {
- webidl.brandCheck(this, Cache)
-
- const prefix = 'Cache.addAll'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- // 1.
- const responsePromises = []
-
- // 2.
- const requestList = []
-
- // 3.
- for (let request of requests) {
- if (request === undefined) {
- throw webidl.errors.conversionFailed({
- prefix,
- argument: 'Argument 1',
- types: ['undefined is not allowed']
- })
- }
-
- request = webidl.converters.RequestInfo(request)
-
- if (typeof request === 'string') {
- continue
- }
-
- // 3.1
- const r = getRequestState(request)
-
- // 3.2
- if (!urlIsHttpHttpsScheme(r.url) || r.method !== 'GET') {
- throw webidl.errors.exception({
- header: prefix,
- message: 'Expected http/s scheme when method is not GET.'
- })
- }
- }
-
- // 4.
- /** @type {ReturnType<typeof fetching>[]} */
- const fetchControllers = []
-
- // 5.
- for (const request of requests) {
- // 5.1
- const r = getRequestState(new Request(request))
-
- // 5.2
- if (!urlIsHttpHttpsScheme(r.url)) {
- throw webidl.errors.exception({
- header: prefix,
- message: 'Expected http/s scheme.'
- })
- }
-
- // 5.4
- r.initiator = 'fetch'
- r.destination = 'subresource'
-
- // 5.5
- requestList.push(r)
-
- // 5.6
- const responsePromise = createDeferredPromise()
-
- // 5.7
- fetchControllers.push(fetching({
- request: r,
- processResponse (response) {
- // 1.
- if (response.type === 'error' || response.status === 206 || response.status < 200 || response.status > 299) {
- responsePromise.reject(webidl.errors.exception({
- header: 'Cache.addAll',
- message: 'Received an invalid status code or the request failed.'
- }))
- } else if (response.headersList.contains('vary')) { // 2.
- // 2.1
- const fieldValues = getFieldValues(response.headersList.get('vary'))
-
- // 2.2
- for (const fieldValue of fieldValues) {
- // 2.2.1
- if (fieldValue === '*') {
- responsePromise.reject(webidl.errors.exception({
- header: 'Cache.addAll',
- message: 'invalid vary field value'
- }))
-
- for (const controller of fetchControllers) {
- controller.abort()
- }
-
- return
- }
- }
- }
- },
- processResponseEndOfBody (response) {
- // 1.
- if (response.aborted) {
- responsePromise.reject(new DOMException('aborted', 'AbortError'))
- return
- }
-
- // 2.
- responsePromise.resolve(response)
- }
- }))
-
- // 5.8
- responsePromises.push(responsePromise.promise)
- }
-
- // 6.
- const p = Promise.all(responsePromises)
-
- // 7.
- const responses = await p
-
- // 7.1
- const operations = []
-
- // 7.2
- let index = 0
-
- // 7.3
- for (const response of responses) {
- // 7.3.1
- /** @type {CacheBatchOperation} */
- const operation = {
- type: 'put', // 7.3.2
- request: requestList[index], // 7.3.3
- response // 7.3.4
- }
-
- operations.push(operation) // 7.3.5
-
- index++ // 7.3.6
- }
-
- // 7.5
- const cacheJobPromise = createDeferredPromise()
-
- // 7.6.1
- let errorData = null
-
- // 7.6.2
- try {
- this.#batchCacheOperations(operations)
- } catch (e) {
- errorData = e
- }
-
- // 7.6.3
- queueMicrotask(() => {
- // 7.6.3.1
- if (errorData === null) {
- cacheJobPromise.resolve(undefined)
- } else {
- // 7.6.3.2
- cacheJobPromise.reject(errorData)
- }
- })
-
- // 7.7
- return cacheJobPromise.promise
- }
-
- async put (request, response) {
- webidl.brandCheck(this, Cache)
-
- const prefix = 'Cache.put'
- webidl.argumentLengthCheck(arguments, 2, prefix)
-
- request = webidl.converters.RequestInfo(request)
- response = webidl.converters.Response(response, prefix, 'response')
-
- // 1.
- let innerRequest = null
-
- // 2.
- if (webidl.is.Request(request)) {
- innerRequest = getRequestState(request)
- } else { // 3.
- innerRequest = getRequestState(new Request(request))
- }
-
- // 4.
- if (!urlIsHttpHttpsScheme(innerRequest.url) || innerRequest.method !== 'GET') {
- throw webidl.errors.exception({
- header: prefix,
- message: 'Expected an http/s scheme when method is not GET'
- })
- }
-
- // 5.
- const innerResponse = getResponseState(response)
-
- // 6.
- if (innerResponse.status === 206) {
- throw webidl.errors.exception({
- header: prefix,
- message: 'Got 206 status'
- })
- }
-
- // 7.
- if (innerResponse.headersList.contains('vary')) {
- // 7.1.
- const fieldValues = getFieldValues(innerResponse.headersList.get('vary'))
-
- // 7.2.
- for (const fieldValue of fieldValues) {
- // 7.2.1
- if (fieldValue === '*') {
- throw webidl.errors.exception({
- header: prefix,
- message: 'Got * vary field value'
- })
- }
- }
- }
-
- // 8.
- if (innerResponse.body && (isDisturbed(innerResponse.body.stream) || innerResponse.body.stream.locked)) {
- throw webidl.errors.exception({
- header: prefix,
- message: 'Response body is locked or disturbed'
- })
- }
-
- // 9.
- const clonedResponse = cloneResponse(innerResponse)
-
- // 10.
- const bodyReadPromise = createDeferredPromise()
-
- // 11.
- if (innerResponse.body != null) {
- // 11.1
- const stream = innerResponse.body.stream
-
- // 11.2
- const reader = stream.getReader()
-
- // 11.3
- readAllBytes(reader, bodyReadPromise.resolve, bodyReadPromise.reject)
- } else {
- bodyReadPromise.resolve(undefined)
- }
-
- // 12.
- /** @type {CacheBatchOperation[]} */
- const operations = []
-
- // 13.
- /** @type {CacheBatchOperation} */
- const operation = {
- type: 'put', // 14.
- request: innerRequest, // 15.
- response: clonedResponse // 16.
- }
-
- // 17.
- operations.push(operation)
-
- // 19.
- const bytes = await bodyReadPromise.promise
-
- if (clonedResponse.body != null) {
- clonedResponse.body.source = bytes
- }
-
- // 19.1
- const cacheJobPromise = createDeferredPromise()
-
- // 19.2.1
- let errorData = null
-
- // 19.2.2
- try {
- this.#batchCacheOperations(operations)
- } catch (e) {
- errorData = e
- }
-
- // 19.2.3
- queueMicrotask(() => {
- // 19.2.3.1
- if (errorData === null) {
- cacheJobPromise.resolve()
- } else { // 19.2.3.2
- cacheJobPromise.reject(errorData)
- }
- })
-
- return cacheJobPromise.promise
- }
-
- async delete (request, options = {}) {
- webidl.brandCheck(this, Cache)
-
- const prefix = 'Cache.delete'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- request = webidl.converters.RequestInfo(request)
- options = webidl.converters.CacheQueryOptions(options, prefix, 'options')
-
- /**
- * @type {Request}
- */
- let r = null
-
- if (webidl.is.Request(request)) {
- r = getRequestState(request)
-
- if (r.method !== 'GET' && !options.ignoreMethod) {
- return false
- }
- } else {
- assert(typeof request === 'string')
-
- r = getRequestState(new Request(request))
- }
-
- /** @type {CacheBatchOperation[]} */
- const operations = []
-
- /** @type {CacheBatchOperation} */
- const operation = {
- type: 'delete',
- request: r,
- options
- }
-
- operations.push(operation)
-
- const cacheJobPromise = createDeferredPromise()
-
- let errorData = null
- let requestResponses
-
- try {
- requestResponses = this.#batchCacheOperations(operations)
- } catch (e) {
- errorData = e
- }
-
- queueMicrotask(() => {
- if (errorData === null) {
- cacheJobPromise.resolve(!!requestResponses?.length)
- } else {
- cacheJobPromise.reject(errorData)
- }
- })
-
- return cacheJobPromise.promise
- }
-
- /**
- * @see https://w3c.github.io/ServiceWorker/#dom-cache-keys
- * @param {any} request
- * @param {import('../../../types/cache').CacheQueryOptions} options
- * @returns {Promise<readonly Request[]>}
- */
- async keys (request = undefined, options = {}) {
- webidl.brandCheck(this, Cache)
-
- const prefix = 'Cache.keys'
-
- if (request !== undefined) request = webidl.converters.RequestInfo(request)
- options = webidl.converters.CacheQueryOptions(options, prefix, 'options')
-
- // 1.
- let r = null
-
- // 2.
- if (request !== undefined) {
- // 2.1
- if (webidl.is.Request(request)) {
- // 2.1.1
- r = getRequestState(request)
-
- // 2.1.2
- if (r.method !== 'GET' && !options.ignoreMethod) {
- return []
- }
- } else if (typeof request === 'string') { // 2.2
- r = getRequestState(new Request(request))
- }
- }
-
- // 4.
- const promise = createDeferredPromise()
-
- // 5.
- // 5.1
- const requests = []
-
- // 5.2
- if (request === undefined) {
- // 5.2.1
- for (const requestResponse of this.#relevantRequestResponseList) {
- // 5.2.1.1
- requests.push(requestResponse[0])
- }
- } else { // 5.3
- // 5.3.1
- const requestResponses = this.#queryCache(r, options)
-
- // 5.3.2
- for (const requestResponse of requestResponses) {
- // 5.3.2.1
- requests.push(requestResponse[0])
- }
- }
-
- // 5.4
- queueMicrotask(() => {
- // 5.4.1
- const requestList = []
-
- // 5.4.2
- for (const request of requests) {
- const requestObject = fromInnerRequest(
- request,
- undefined,
- new AbortController().signal,
- 'immutable'
- )
- // 5.4.2.1
- requestList.push(requestObject)
- }
-
- // 5.4.3
- promise.resolve(Object.freeze(requestList))
- })
-
- return promise.promise
- }
-
- /**
- * @see https://w3c.github.io/ServiceWorker/#batch-cache-operations-algorithm
- * @param {CacheBatchOperation[]} operations
- * @returns {requestResponseList}
- */
- #batchCacheOperations (operations) {
- // 1.
- const cache = this.#relevantRequestResponseList
-
- // 2.
- const backupCache = [...cache]
-
- // 3.
- const addedItems = []
-
- // 4.1
- const resultList = []
-
- try {
- // 4.2
- for (const operation of operations) {
- // 4.2.1
- if (operation.type !== 'delete' && operation.type !== 'put') {
- throw webidl.errors.exception({
- header: 'Cache.#batchCacheOperations',
- message: 'operation type does not match "delete" or "put"'
- })
- }
-
- // 4.2.2
- if (operation.type === 'delete' && operation.response != null) {
- throw webidl.errors.exception({
- header: 'Cache.#batchCacheOperations',
- message: 'delete operation should not have an associated response'
- })
- }
-
- // 4.2.3
- if (this.#queryCache(operation.request, operation.options, addedItems).length) {
- throw new DOMException('???', 'InvalidStateError')
- }
-
- // 4.2.4
- let requestResponses
-
- // 4.2.5
- if (operation.type === 'delete') {
- // 4.2.5.1
- requestResponses = this.#queryCache(operation.request, operation.options)
-
- // TODO: the spec is wrong, this is needed to pass WPTs
- if (requestResponses.length === 0) {
- return []
- }
-
- // 4.2.5.2
- for (const requestResponse of requestResponses) {
- const idx = cache.indexOf(requestResponse)
- assert(idx !== -1)
-
- // 4.2.5.2.1
- cache.splice(idx, 1)
- }
- } else if (operation.type === 'put') { // 4.2.6
- // 4.2.6.1
- if (operation.response == null) {
- throw webidl.errors.exception({
- header: 'Cache.#batchCacheOperations',
- message: 'put operation should have an associated response'
- })
- }
-
- // 4.2.6.2
- const r = operation.request
-
- // 4.2.6.3
- if (!urlIsHttpHttpsScheme(r.url)) {
- throw webidl.errors.exception({
- header: 'Cache.#batchCacheOperations',
- message: 'expected http or https scheme'
- })
- }
-
- // 4.2.6.4
- if (r.method !== 'GET') {
- throw webidl.errors.exception({
- header: 'Cache.#batchCacheOperations',
- message: 'not get method'
- })
- }
-
- // 4.2.6.5
- if (operation.options != null) {
- throw webidl.errors.exception({
- header: 'Cache.#batchCacheOperations',
- message: 'options must not be defined'
- })
- }
-
- // 4.2.6.6
- requestResponses = this.#queryCache(operation.request)
-
- // 4.2.6.7
- for (const requestResponse of requestResponses) {
- const idx = cache.indexOf(requestResponse)
- assert(idx !== -1)
-
- // 4.2.6.7.1
- cache.splice(idx, 1)
- }
-
- // 4.2.6.8
- cache.push([operation.request, operation.response])
-
- // 4.2.6.10
- addedItems.push([operation.request, operation.response])
- }
-
- // 4.2.7
- resultList.push([operation.request, operation.response])
- }
-
- // 4.3
- return resultList
- } catch (e) { // 5.
- // 5.1
- this.#relevantRequestResponseList.length = 0
-
- // 5.2
- this.#relevantRequestResponseList = backupCache
-
- // 5.3
- throw e
- }
- }
-
- /**
- * @see https://w3c.github.io/ServiceWorker/#query-cache
- * @param {any} requestQuery
- * @param {import('../../../types/cache').CacheQueryOptions} options
- * @param {requestResponseList} targetStorage
- * @returns {requestResponseList}
- */
- #queryCache (requestQuery, options, targetStorage) {
- /** @type {requestResponseList} */
- const resultList = []
-
- const storage = targetStorage ?? this.#relevantRequestResponseList
-
- for (const requestResponse of storage) {
- const [cachedRequest, cachedResponse] = requestResponse
- if (this.#requestMatchesCachedItem(requestQuery, cachedRequest, cachedResponse, options)) {
- resultList.push(requestResponse)
- }
- }
-
- return resultList
- }
-
- /**
- * @see https://w3c.github.io/ServiceWorker/#request-matches-cached-item-algorithm
- * @param {any} requestQuery
- * @param {any} request
- * @param {any | null} response
- * @param {import('../../../types/cache').CacheQueryOptions | undefined} options
- * @returns {boolean}
- */
- #requestMatchesCachedItem (requestQuery, request, response = null, options) {
- // if (options?.ignoreMethod === false && request.method === 'GET') {
- // return false
- // }
-
- const queryURL = new URL(requestQuery.url)
-
- const cachedURL = new URL(request.url)
-
- if (options?.ignoreSearch) {
- cachedURL.search = ''
-
- queryURL.search = ''
- }
-
- if (!urlEquals(queryURL, cachedURL, true)) {
- return false
- }
-
- if (
- response == null ||
- options?.ignoreVary ||
- !response.headersList.contains('vary')
- ) {
- return true
- }
-
- const fieldValues = getFieldValues(response.headersList.get('vary'))
-
- for (const fieldValue of fieldValues) {
- if (fieldValue === '*') {
- return false
- }
-
- const requestValue = request.headersList.get(fieldValue)
- const queryValue = requestQuery.headersList.get(fieldValue)
-
- // If one has the header and the other doesn't, or one has
- // a different value than the other, return false
- if (requestValue !== queryValue) {
- return false
- }
- }
-
- return true
- }
-
- #internalMatchAll (request, options, maxResponses = Infinity) {
- // 1.
- let r = null
-
- // 2.
- if (request !== undefined) {
- if (webidl.is.Request(request)) {
- // 2.1.1
- r = getRequestState(request)
-
- // 2.1.2
- if (r.method !== 'GET' && !options.ignoreMethod) {
- return []
- }
- } else if (typeof request === 'string') {
- // 2.2.1
- r = getRequestState(new Request(request))
- }
- }
-
- // 5.
- // 5.1
- const responses = []
-
- // 5.2
- if (request === undefined) {
- // 5.2.1
- for (const requestResponse of this.#relevantRequestResponseList) {
- responses.push(requestResponse[1])
- }
- } else { // 5.3
- // 5.3.1
- const requestResponses = this.#queryCache(r, options)
-
- // 5.3.2
- for (const requestResponse of requestResponses) {
- responses.push(requestResponse[1])
- }
- }
-
- // 5.4
- // We don't implement CORs so we don't need to loop over the responses, yay!
-
- // 5.5.1
- const responseList = []
-
- // 5.5.2
- for (const response of responses) {
- // 5.5.2.1
- const responseObject = fromInnerResponse(cloneResponse(response), 'immutable')
-
- responseList.push(responseObject)
-
- if (responseList.length >= maxResponses) {
- break
- }
- }
-
- // 6.
- return Object.freeze(responseList)
- }
-}
-
-Object.defineProperties(Cache.prototype, {
- [Symbol.toStringTag]: {
- value: 'Cache',
- configurable: true
- },
- match: kEnumerableProperty,
- matchAll: kEnumerableProperty,
- add: kEnumerableProperty,
- addAll: kEnumerableProperty,
- put: kEnumerableProperty,
- delete: kEnumerableProperty,
- keys: kEnumerableProperty
-})
-
-const cacheQueryOptionConverters = [
- {
- key: 'ignoreSearch',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- },
- {
- key: 'ignoreMethod',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- },
- {
- key: 'ignoreVary',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- }
-]
-
-webidl.converters.CacheQueryOptions = webidl.dictionaryConverter(cacheQueryOptionConverters)
-
-webidl.converters.MultiCacheQueryOptions = webidl.dictionaryConverter([
- ...cacheQueryOptionConverters,
- {
- key: 'cacheName',
- converter: webidl.converters.DOMString
- }
-])
-
-webidl.converters.Response = webidl.interfaceConverter(
- webidl.is.Response,
- 'Response'
-)
-
-webidl.converters['sequence<RequestInfo>'] = webidl.sequenceConverter(
- webidl.converters.RequestInfo
-)
-
-module.exports = {
- Cache
-}
diff --git a/vanilla/node_modules/undici/lib/web/cache/cachestorage.js b/vanilla/node_modules/undici/lib/web/cache/cachestorage.js
deleted file mode 100644
index c49b1e8..0000000
--- a/vanilla/node_modules/undici/lib/web/cache/cachestorage.js
+++ /dev/null
@@ -1,152 +0,0 @@
-'use strict'
-
-const { Cache } = require('./cache')
-const { webidl } = require('../webidl')
-const { kEnumerableProperty } = require('../../core/util')
-const { kConstruct } = require('../../core/symbols')
-
-class CacheStorage {
- /**
- * @see https://w3c.github.io/ServiceWorker/#dfn-relevant-name-to-cache-map
- * @type {Map<string, import('./cache').requestResponseList}
- */
- #caches = new Map()
-
- constructor () {
- if (arguments[0] !== kConstruct) {
- webidl.illegalConstructor()
- }
-
- webidl.util.markAsUncloneable(this)
- }
-
- async match (request, options = {}) {
- webidl.brandCheck(this, CacheStorage)
- webidl.argumentLengthCheck(arguments, 1, 'CacheStorage.match')
-
- request = webidl.converters.RequestInfo(request)
- options = webidl.converters.MultiCacheQueryOptions(options)
-
- // 1.
- if (options.cacheName != null) {
- // 1.1.1.1
- if (this.#caches.has(options.cacheName)) {
- // 1.1.1.1.1
- const cacheList = this.#caches.get(options.cacheName)
- const cache = new Cache(kConstruct, cacheList)
-
- return await cache.match(request, options)
- }
- } else { // 2.
- // 2.2
- for (const cacheList of this.#caches.values()) {
- const cache = new Cache(kConstruct, cacheList)
-
- // 2.2.1.2
- const response = await cache.match(request, options)
-
- if (response !== undefined) {
- return response
- }
- }
- }
- }
-
- /**
- * @see https://w3c.github.io/ServiceWorker/#cache-storage-has
- * @param {string} cacheName
- * @returns {Promise<boolean>}
- */
- async has (cacheName) {
- webidl.brandCheck(this, CacheStorage)
-
- const prefix = 'CacheStorage.has'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- cacheName = webidl.converters.DOMString(cacheName, prefix, 'cacheName')
-
- // 2.1.1
- // 2.2
- return this.#caches.has(cacheName)
- }
-
- /**
- * @see https://w3c.github.io/ServiceWorker/#dom-cachestorage-open
- * @param {string} cacheName
- * @returns {Promise<Cache>}
- */
- async open (cacheName) {
- webidl.brandCheck(this, CacheStorage)
-
- const prefix = 'CacheStorage.open'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- cacheName = webidl.converters.DOMString(cacheName, prefix, 'cacheName')
-
- // 2.1
- if (this.#caches.has(cacheName)) {
- // await caches.open('v1') !== await caches.open('v1')
-
- // 2.1.1
- const cache = this.#caches.get(cacheName)
-
- // 2.1.1.1
- return new Cache(kConstruct, cache)
- }
-
- // 2.2
- const cache = []
-
- // 2.3
- this.#caches.set(cacheName, cache)
-
- // 2.4
- return new Cache(kConstruct, cache)
- }
-
- /**
- * @see https://w3c.github.io/ServiceWorker/#cache-storage-delete
- * @param {string} cacheName
- * @returns {Promise<boolean>}
- */
- async delete (cacheName) {
- webidl.brandCheck(this, CacheStorage)
-
- const prefix = 'CacheStorage.delete'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- cacheName = webidl.converters.DOMString(cacheName, prefix, 'cacheName')
-
- return this.#caches.delete(cacheName)
- }
-
- /**
- * @see https://w3c.github.io/ServiceWorker/#cache-storage-keys
- * @returns {Promise<string[]>}
- */
- async keys () {
- webidl.brandCheck(this, CacheStorage)
-
- // 2.1
- const keys = this.#caches.keys()
-
- // 2.2
- return [...keys]
- }
-}
-
-Object.defineProperties(CacheStorage.prototype, {
- [Symbol.toStringTag]: {
- value: 'CacheStorage',
- configurable: true
- },
- match: kEnumerableProperty,
- has: kEnumerableProperty,
- open: kEnumerableProperty,
- delete: kEnumerableProperty,
- keys: kEnumerableProperty
-})
-
-module.exports = {
- CacheStorage
-}
diff --git a/vanilla/node_modules/undici/lib/web/cache/util.js b/vanilla/node_modules/undici/lib/web/cache/util.js
deleted file mode 100644
index 5ac9d84..0000000
--- a/vanilla/node_modules/undici/lib/web/cache/util.js
+++ /dev/null
@@ -1,45 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { URLSerializer } = require('../fetch/data-url')
-const { isValidHeaderName } = require('../fetch/util')
-
-/**
- * @see https://url.spec.whatwg.org/#concept-url-equals
- * @param {URL} A
- * @param {URL} B
- * @param {boolean | undefined} excludeFragment
- * @returns {boolean}
- */
-function urlEquals (A, B, excludeFragment = false) {
- const serializedA = URLSerializer(A, excludeFragment)
-
- const serializedB = URLSerializer(B, excludeFragment)
-
- return serializedA === serializedB
-}
-
-/**
- * @see https://github.com/chromium/chromium/blob/694d20d134cb553d8d89e5500b9148012b1ba299/content/browser/cache_storage/cache_storage_cache.cc#L260-L262
- * @param {string} header
- */
-function getFieldValues (header) {
- assert(header !== null)
-
- const values = []
-
- for (let value of header.split(',')) {
- value = value.trim()
-
- if (isValidHeaderName(value)) {
- values.push(value)
- }
- }
-
- return values
-}
-
-module.exports = {
- urlEquals,
- getFieldValues
-}
diff --git a/vanilla/node_modules/undici/lib/web/cookies/constants.js b/vanilla/node_modules/undici/lib/web/cookies/constants.js
deleted file mode 100644
index 85f1fec..0000000
--- a/vanilla/node_modules/undici/lib/web/cookies/constants.js
+++ /dev/null
@@ -1,12 +0,0 @@
-'use strict'
-
-// https://wicg.github.io/cookie-store/#cookie-maximum-attribute-value-size
-const maxAttributeValueSize = 1024
-
-// https://wicg.github.io/cookie-store/#cookie-maximum-name-value-pair-size
-const maxNameValuePairSize = 4096
-
-module.exports = {
- maxAttributeValueSize,
- maxNameValuePairSize
-}
diff --git a/vanilla/node_modules/undici/lib/web/cookies/index.js b/vanilla/node_modules/undici/lib/web/cookies/index.js
deleted file mode 100644
index be99f6f..0000000
--- a/vanilla/node_modules/undici/lib/web/cookies/index.js
+++ /dev/null
@@ -1,199 +0,0 @@
-'use strict'
-
-const { parseSetCookie } = require('./parse')
-const { stringify } = require('./util')
-const { webidl } = require('../webidl')
-const { Headers } = require('../fetch/headers')
-
-const brandChecks = webidl.brandCheckMultiple([Headers, globalThis.Headers].filter(Boolean))
-
-/**
- * @typedef {Object} Cookie
- * @property {string} name
- * @property {string} value
- * @property {Date|number} [expires]
- * @property {number} [maxAge]
- * @property {string} [domain]
- * @property {string} [path]
- * @property {boolean} [secure]
- * @property {boolean} [httpOnly]
- * @property {'Strict'|'Lax'|'None'} [sameSite]
- * @property {string[]} [unparsed]
- */
-
-/**
- * @param {Headers} headers
- * @returns {Record<string, string>}
- */
-function getCookies (headers) {
- webidl.argumentLengthCheck(arguments, 1, 'getCookies')
-
- brandChecks(headers)
-
- const cookie = headers.get('cookie')
-
- /** @type {Record<string, string>} */
- const out = {}
-
- if (!cookie) {
- return out
- }
-
- for (const piece of cookie.split(';')) {
- const [name, ...value] = piece.split('=')
-
- out[name.trim()] = value.join('=')
- }
-
- return out
-}
-
-/**
- * @param {Headers} headers
- * @param {string} name
- * @param {{ path?: string, domain?: string }|undefined} attributes
- * @returns {void}
- */
-function deleteCookie (headers, name, attributes) {
- brandChecks(headers)
-
- const prefix = 'deleteCookie'
- webidl.argumentLengthCheck(arguments, 2, prefix)
-
- name = webidl.converters.DOMString(name, prefix, 'name')
- attributes = webidl.converters.DeleteCookieAttributes(attributes)
-
- // Matches behavior of
- // https://github.com/denoland/deno_std/blob/63827b16330b82489a04614027c33b7904e08be5/http/cookie.ts#L278
- setCookie(headers, {
- name,
- value: '',
- expires: new Date(0),
- ...attributes
- })
-}
-
-/**
- * @param {Headers} headers
- * @returns {Cookie[]}
- */
-function getSetCookies (headers) {
- webidl.argumentLengthCheck(arguments, 1, 'getSetCookies')
-
- brandChecks(headers)
-
- const cookies = headers.getSetCookie()
-
- if (!cookies) {
- return []
- }
-
- return cookies.map((pair) => parseSetCookie(pair))
-}
-
-/**
- * Parses a cookie string
- * @param {string} cookie
- */
-function parseCookie (cookie) {
- cookie = webidl.converters.DOMString(cookie)
-
- return parseSetCookie(cookie)
-}
-
-/**
- * @param {Headers} headers
- * @param {Cookie} cookie
- * @returns {void}
- */
-function setCookie (headers, cookie) {
- webidl.argumentLengthCheck(arguments, 2, 'setCookie')
-
- brandChecks(headers)
-
- cookie = webidl.converters.Cookie(cookie)
-
- const str = stringify(cookie)
-
- if (str) {
- headers.append('set-cookie', str, true)
- }
-}
-
-webidl.converters.DeleteCookieAttributes = webidl.dictionaryConverter([
- {
- converter: webidl.nullableConverter(webidl.converters.DOMString),
- key: 'path',
- defaultValue: () => null
- },
- {
- converter: webidl.nullableConverter(webidl.converters.DOMString),
- key: 'domain',
- defaultValue: () => null
- }
-])
-
-webidl.converters.Cookie = webidl.dictionaryConverter([
- {
- converter: webidl.converters.DOMString,
- key: 'name'
- },
- {
- converter: webidl.converters.DOMString,
- key: 'value'
- },
- {
- converter: webidl.nullableConverter((value) => {
- if (typeof value === 'number') {
- return webidl.converters['unsigned long long'](value)
- }
-
- return new Date(value)
- }),
- key: 'expires',
- defaultValue: () => null
- },
- {
- converter: webidl.nullableConverter(webidl.converters['long long']),
- key: 'maxAge',
- defaultValue: () => null
- },
- {
- converter: webidl.nullableConverter(webidl.converters.DOMString),
- key: 'domain',
- defaultValue: () => null
- },
- {
- converter: webidl.nullableConverter(webidl.converters.DOMString),
- key: 'path',
- defaultValue: () => null
- },
- {
- converter: webidl.nullableConverter(webidl.converters.boolean),
- key: 'secure',
- defaultValue: () => null
- },
- {
- converter: webidl.nullableConverter(webidl.converters.boolean),
- key: 'httpOnly',
- defaultValue: () => null
- },
- {
- converter: webidl.converters.USVString,
- key: 'sameSite',
- allowedValues: ['Strict', 'Lax', 'None']
- },
- {
- converter: webidl.sequenceConverter(webidl.converters.DOMString),
- key: 'unparsed',
- defaultValue: () => []
- }
-])
-
-module.exports = {
- getCookies,
- deleteCookie,
- getSetCookies,
- setCookie,
- parseCookie
-}
diff --git a/vanilla/node_modules/undici/lib/web/cookies/parse.js b/vanilla/node_modules/undici/lib/web/cookies/parse.js
deleted file mode 100644
index 74edf7e..0000000
--- a/vanilla/node_modules/undici/lib/web/cookies/parse.js
+++ /dev/null
@@ -1,322 +0,0 @@
-'use strict'
-
-const { collectASequenceOfCodePointsFast } = require('../infra')
-const { maxNameValuePairSize, maxAttributeValueSize } = require('./constants')
-const { isCTLExcludingHtab } = require('./util')
-const assert = require('node:assert')
-const { unescape: qsUnescape } = require('node:querystring')
-
-/**
- * @description Parses the field-value attributes of a set-cookie header string.
- * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4
- * @param {string} header
- * @returns {import('./index').Cookie|null} if the header is invalid, null will be returned
- */
-function parseSetCookie (header) {
- // 1. If the set-cookie-string contains a %x00-08 / %x0A-1F / %x7F
- // character (CTL characters excluding HTAB): Abort these steps and
- // ignore the set-cookie-string entirely.
- if (isCTLExcludingHtab(header)) {
- return null
- }
-
- let nameValuePair = ''
- let unparsedAttributes = ''
- let name = ''
- let value = ''
-
- // 2. If the set-cookie-string contains a %x3B (";") character:
- if (header.includes(';')) {
- // 1. The name-value-pair string consists of the characters up to,
- // but not including, the first %x3B (";"), and the unparsed-
- // attributes consist of the remainder of the set-cookie-string
- // (including the %x3B (";") in question).
- const position = { position: 0 }
-
- nameValuePair = collectASequenceOfCodePointsFast(';', header, position)
- unparsedAttributes = header.slice(position.position)
- } else {
- // Otherwise:
-
- // 1. The name-value-pair string consists of all the characters
- // contained in the set-cookie-string, and the unparsed-
- // attributes is the empty string.
- nameValuePair = header
- }
-
- // 3. If the name-value-pair string lacks a %x3D ("=") character, then
- // the name string is empty, and the value string is the value of
- // name-value-pair.
- if (!nameValuePair.includes('=')) {
- value = nameValuePair
- } else {
- // Otherwise, the name string consists of the characters up to, but
- // not including, the first %x3D ("=") character, and the (possibly
- // empty) value string consists of the characters after the first
- // %x3D ("=") character.
- const position = { position: 0 }
- name = collectASequenceOfCodePointsFast(
- '=',
- nameValuePair,
- position
- )
- value = nameValuePair.slice(position.position + 1)
- }
-
- // 4. Remove any leading or trailing WSP characters from the name
- // string and the value string.
- name = name.trim()
- value = value.trim()
-
- // 5. If the sum of the lengths of the name string and the value string
- // is more than 4096 octets, abort these steps and ignore the set-
- // cookie-string entirely.
- if (name.length + value.length > maxNameValuePairSize) {
- return null
- }
-
- // 6. The cookie-name is the name string, and the cookie-value is the
- // value string.
- // https://datatracker.ietf.org/doc/html/rfc6265
- // To maximize compatibility with user agents, servers that wish to
- // store arbitrary data in a cookie-value SHOULD encode that data, for
- // example, using Base64 [RFC4648].
- return {
- name, value: qsUnescape(value), ...parseUnparsedAttributes(unparsedAttributes)
- }
-}
-
-/**
- * Parses the remaining attributes of a set-cookie header
- * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4
- * @param {string} unparsedAttributes
- * @param {Object.<string, unknown>} [cookieAttributeList={}]
- */
-function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {}) {
- // 1. If the unparsed-attributes string is empty, skip the rest of
- // these steps.
- if (unparsedAttributes.length === 0) {
- return cookieAttributeList
- }
-
- // 2. Discard the first character of the unparsed-attributes (which
- // will be a %x3B (";") character).
- assert(unparsedAttributes[0] === ';')
- unparsedAttributes = unparsedAttributes.slice(1)
-
- let cookieAv = ''
-
- // 3. If the remaining unparsed-attributes contains a %x3B (";")
- // character:
- if (unparsedAttributes.includes(';')) {
- // 1. Consume the characters of the unparsed-attributes up to, but
- // not including, the first %x3B (";") character.
- cookieAv = collectASequenceOfCodePointsFast(
- ';',
- unparsedAttributes,
- { position: 0 }
- )
- unparsedAttributes = unparsedAttributes.slice(cookieAv.length)
- } else {
- // Otherwise:
-
- // 1. Consume the remainder of the unparsed-attributes.
- cookieAv = unparsedAttributes
- unparsedAttributes = ''
- }
-
- // Let the cookie-av string be the characters consumed in this step.
-
- let attributeName = ''
- let attributeValue = ''
-
- // 4. If the cookie-av string contains a %x3D ("=") character:
- if (cookieAv.includes('=')) {
- // 1. The (possibly empty) attribute-name string consists of the
- // characters up to, but not including, the first %x3D ("=")
- // character, and the (possibly empty) attribute-value string
- // consists of the characters after the first %x3D ("=")
- // character.
- const position = { position: 0 }
-
- attributeName = collectASequenceOfCodePointsFast(
- '=',
- cookieAv,
- position
- )
- attributeValue = cookieAv.slice(position.position + 1)
- } else {
- // Otherwise:
-
- // 1. The attribute-name string consists of the entire cookie-av
- // string, and the attribute-value string is empty.
- attributeName = cookieAv
- }
-
- // 5. Remove any leading or trailing WSP characters from the attribute-
- // name string and the attribute-value string.
- attributeName = attributeName.trim()
- attributeValue = attributeValue.trim()
-
- // 6. If the attribute-value is longer than 1024 octets, ignore the
- // cookie-av string and return to Step 1 of this algorithm.
- if (attributeValue.length > maxAttributeValueSize) {
- return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)
- }
-
- // 7. Process the attribute-name and attribute-value according to the
- // requirements in the following subsections. (Notice that
- // attributes with unrecognized attribute-names are ignored.)
- const attributeNameLowercase = attributeName.toLowerCase()
-
- // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.1
- // If the attribute-name case-insensitively matches the string
- // "Expires", the user agent MUST process the cookie-av as follows.
- if (attributeNameLowercase === 'expires') {
- // 1. Let the expiry-time be the result of parsing the attribute-value
- // as cookie-date (see Section 5.1.1).
- const expiryTime = new Date(attributeValue)
-
- // 2. If the attribute-value failed to parse as a cookie date, ignore
- // the cookie-av.
-
- cookieAttributeList.expires = expiryTime
- } else if (attributeNameLowercase === 'max-age') {
- // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.2
- // If the attribute-name case-insensitively matches the string "Max-
- // Age", the user agent MUST process the cookie-av as follows.
-
- // 1. If the first character of the attribute-value is not a DIGIT or a
- // "-" character, ignore the cookie-av.
- const charCode = attributeValue.charCodeAt(0)
-
- if ((charCode < 48 || charCode > 57) && attributeValue[0] !== '-') {
- return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)
- }
-
- // 2. If the remainder of attribute-value contains a non-DIGIT
- // character, ignore the cookie-av.
- if (!/^\d+$/.test(attributeValue)) {
- return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)
- }
-
- // 3. Let delta-seconds be the attribute-value converted to an integer.
- const deltaSeconds = Number(attributeValue)
-
- // 4. Let cookie-age-limit be the maximum age of the cookie (which
- // SHOULD be 400 days or less, see Section 4.1.2.2).
-
- // 5. Set delta-seconds to the smaller of its present value and cookie-
- // age-limit.
- // deltaSeconds = Math.min(deltaSeconds * 1000, maxExpiresMs)
-
- // 6. If delta-seconds is less than or equal to zero (0), let expiry-
- // time be the earliest representable date and time. Otherwise, let
- // the expiry-time be the current date and time plus delta-seconds
- // seconds.
- // const expiryTime = deltaSeconds <= 0 ? Date.now() : Date.now() + deltaSeconds
-
- // 7. Append an attribute to the cookie-attribute-list with an
- // attribute-name of Max-Age and an attribute-value of expiry-time.
- cookieAttributeList.maxAge = deltaSeconds
- } else if (attributeNameLowercase === 'domain') {
- // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.3
- // If the attribute-name case-insensitively matches the string "Domain",
- // the user agent MUST process the cookie-av as follows.
-
- // 1. Let cookie-domain be the attribute-value.
- let cookieDomain = attributeValue
-
- // 2. If cookie-domain starts with %x2E ("."), let cookie-domain be
- // cookie-domain without its leading %x2E (".").
- if (cookieDomain[0] === '.') {
- cookieDomain = cookieDomain.slice(1)
- }
-
- // 3. Convert the cookie-domain to lower case.
- cookieDomain = cookieDomain.toLowerCase()
-
- // 4. Append an attribute to the cookie-attribute-list with an
- // attribute-name of Domain and an attribute-value of cookie-domain.
- cookieAttributeList.domain = cookieDomain
- } else if (attributeNameLowercase === 'path') {
- // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.4
- // If the attribute-name case-insensitively matches the string "Path",
- // the user agent MUST process the cookie-av as follows.
-
- // 1. If the attribute-value is empty or if the first character of the
- // attribute-value is not %x2F ("/"):
- let cookiePath = ''
- if (attributeValue.length === 0 || attributeValue[0] !== '/') {
- // 1. Let cookie-path be the default-path.
- cookiePath = '/'
- } else {
- // Otherwise:
-
- // 1. Let cookie-path be the attribute-value.
- cookiePath = attributeValue
- }
-
- // 2. Append an attribute to the cookie-attribute-list with an
- // attribute-name of Path and an attribute-value of cookie-path.
- cookieAttributeList.path = cookiePath
- } else if (attributeNameLowercase === 'secure') {
- // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.5
- // If the attribute-name case-insensitively matches the string "Secure",
- // the user agent MUST append an attribute to the cookie-attribute-list
- // with an attribute-name of Secure and an empty attribute-value.
-
- cookieAttributeList.secure = true
- } else if (attributeNameLowercase === 'httponly') {
- // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.6
- // If the attribute-name case-insensitively matches the string
- // "HttpOnly", the user agent MUST append an attribute to the cookie-
- // attribute-list with an attribute-name of HttpOnly and an empty
- // attribute-value.
-
- cookieAttributeList.httpOnly = true
- } else if (attributeNameLowercase === 'samesite') {
- // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.7
- // If the attribute-name case-insensitively matches the string
- // "SameSite", the user agent MUST process the cookie-av as follows:
-
- // 1. Let enforcement be "Default".
- let enforcement = 'Default'
-
- const attributeValueLowercase = attributeValue.toLowerCase()
- // 2. If cookie-av's attribute-value is a case-insensitive match for
- // "None", set enforcement to "None".
- if (attributeValueLowercase.includes('none')) {
- enforcement = 'None'
- }
-
- // 3. If cookie-av's attribute-value is a case-insensitive match for
- // "Strict", set enforcement to "Strict".
- if (attributeValueLowercase.includes('strict')) {
- enforcement = 'Strict'
- }
-
- // 4. If cookie-av's attribute-value is a case-insensitive match for
- // "Lax", set enforcement to "Lax".
- if (attributeValueLowercase.includes('lax')) {
- enforcement = 'Lax'
- }
-
- // 5. Append an attribute to the cookie-attribute-list with an
- // attribute-name of "SameSite" and an attribute-value of
- // enforcement.
- cookieAttributeList.sameSite = enforcement
- } else {
- cookieAttributeList.unparsed ??= []
-
- cookieAttributeList.unparsed.push(`${attributeName}=${attributeValue}`)
- }
-
- // 8. Return to Step 1 of this algorithm.
- return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)
-}
-
-module.exports = {
- parseSetCookie,
- parseUnparsedAttributes
-}
diff --git a/vanilla/node_modules/undici/lib/web/cookies/util.js b/vanilla/node_modules/undici/lib/web/cookies/util.js
deleted file mode 100644
index 254f541..0000000
--- a/vanilla/node_modules/undici/lib/web/cookies/util.js
+++ /dev/null
@@ -1,282 +0,0 @@
-'use strict'
-
-/**
- * @param {string} value
- * @returns {boolean}
- */
-function isCTLExcludingHtab (value) {
- for (let i = 0; i < value.length; ++i) {
- const code = value.charCodeAt(i)
-
- if (
- (code >= 0x00 && code <= 0x08) ||
- (code >= 0x0A && code <= 0x1F) ||
- code === 0x7F
- ) {
- return true
- }
- }
- return false
-}
-
-/**
- CHAR = <any US-ASCII character (octets 0 - 127)>
- token = 1*<any CHAR except CTLs or separators>
- separators = "(" | ")" | "<" | ">" | "@"
- | "," | ";" | ":" | "\" | <">
- | "/" | "[" | "]" | "?" | "="
- | "{" | "}" | SP | HT
- * @param {string} name
- */
-function validateCookieName (name) {
- for (let i = 0; i < name.length; ++i) {
- const code = name.charCodeAt(i)
-
- if (
- code < 0x21 || // exclude CTLs (0-31), SP and HT
- code > 0x7E || // exclude non-ascii and DEL
- code === 0x22 || // "
- code === 0x28 || // (
- code === 0x29 || // )
- code === 0x3C || // <
- code === 0x3E || // >
- code === 0x40 || // @
- code === 0x2C || // ,
- code === 0x3B || // ;
- code === 0x3A || // :
- code === 0x5C || // \
- code === 0x2F || // /
- code === 0x5B || // [
- code === 0x5D || // ]
- code === 0x3F || // ?
- code === 0x3D || // =
- code === 0x7B || // {
- code === 0x7D // }
- ) {
- throw new Error('Invalid cookie name')
- }
- }
-}
-
-/**
- cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
- cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
- ; US-ASCII characters excluding CTLs,
- ; whitespace DQUOTE, comma, semicolon,
- ; and backslash
- * @param {string} value
- */
-function validateCookieValue (value) {
- let len = value.length
- let i = 0
-
- // if the value is wrapped in DQUOTE
- if (value[0] === '"') {
- if (len === 1 || value[len - 1] !== '"') {
- throw new Error('Invalid cookie value')
- }
- --len
- ++i
- }
-
- while (i < len) {
- const code = value.charCodeAt(i++)
-
- if (
- code < 0x21 || // exclude CTLs (0-31)
- code > 0x7E || // non-ascii and DEL (127)
- code === 0x22 || // "
- code === 0x2C || // ,
- code === 0x3B || // ;
- code === 0x5C // \
- ) {
- throw new Error('Invalid cookie value')
- }
- }
-}
-
-/**
- * path-value = <any CHAR except CTLs or ";">
- * @param {string} path
- */
-function validateCookiePath (path) {
- for (let i = 0; i < path.length; ++i) {
- const code = path.charCodeAt(i)
-
- if (
- code < 0x20 || // exclude CTLs (0-31)
- code === 0x7F || // DEL
- code === 0x3B // ;
- ) {
- throw new Error('Invalid cookie path')
- }
- }
-}
-
-/**
- * I have no idea why these values aren't allowed to be honest,
- * but Deno tests these. - Khafra
- * @param {string} domain
- */
-function validateCookieDomain (domain) {
- if (
- domain.startsWith('-') ||
- domain.endsWith('.') ||
- domain.endsWith('-')
- ) {
- throw new Error('Invalid cookie domain')
- }
-}
-
-const IMFDays = [
- 'Sun', 'Mon', 'Tue', 'Wed',
- 'Thu', 'Fri', 'Sat'
-]
-
-const IMFMonths = [
- 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
-]
-
-const IMFPaddedNumbers = Array(61).fill(0).map((_, i) => i.toString().padStart(2, '0'))
-
-/**
- * @see https://www.rfc-editor.org/rfc/rfc7231#section-7.1.1.1
- * @param {number|Date} date
- IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT
- ; fixed length/zone/capitalization subset of the format
- ; see Section 3.3 of [RFC5322]
-
- day-name = %x4D.6F.6E ; "Mon", case-sensitive
- / %x54.75.65 ; "Tue", case-sensitive
- / %x57.65.64 ; "Wed", case-sensitive
- / %x54.68.75 ; "Thu", case-sensitive
- / %x46.72.69 ; "Fri", case-sensitive
- / %x53.61.74 ; "Sat", case-sensitive
- / %x53.75.6E ; "Sun", case-sensitive
- date1 = day SP month SP year
- ; e.g., 02 Jun 1982
-
- day = 2DIGIT
- month = %x4A.61.6E ; "Jan", case-sensitive
- / %x46.65.62 ; "Feb", case-sensitive
- / %x4D.61.72 ; "Mar", case-sensitive
- / %x41.70.72 ; "Apr", case-sensitive
- / %x4D.61.79 ; "May", case-sensitive
- / %x4A.75.6E ; "Jun", case-sensitive
- / %x4A.75.6C ; "Jul", case-sensitive
- / %x41.75.67 ; "Aug", case-sensitive
- / %x53.65.70 ; "Sep", case-sensitive
- / %x4F.63.74 ; "Oct", case-sensitive
- / %x4E.6F.76 ; "Nov", case-sensitive
- / %x44.65.63 ; "Dec", case-sensitive
- year = 4DIGIT
-
- GMT = %x47.4D.54 ; "GMT", case-sensitive
-
- time-of-day = hour ":" minute ":" second
- ; 00:00:00 - 23:59:60 (leap second)
-
- hour = 2DIGIT
- minute = 2DIGIT
- second = 2DIGIT
- */
-function toIMFDate (date) {
- if (typeof date === 'number') {
- date = new Date(date)
- }
-
- return `${IMFDays[date.getUTCDay()]}, ${IMFPaddedNumbers[date.getUTCDate()]} ${IMFMonths[date.getUTCMonth()]} ${date.getUTCFullYear()} ${IMFPaddedNumbers[date.getUTCHours()]}:${IMFPaddedNumbers[date.getUTCMinutes()]}:${IMFPaddedNumbers[date.getUTCSeconds()]} GMT`
-}
-
-/**
- max-age-av = "Max-Age=" non-zero-digit *DIGIT
- ; In practice, both expires-av and max-age-av
- ; are limited to dates representable by the
- ; user agent.
- * @param {number} maxAge
- */
-function validateCookieMaxAge (maxAge) {
- if (maxAge < 0) {
- throw new Error('Invalid cookie max-age')
- }
-}
-
-/**
- * @see https://www.rfc-editor.org/rfc/rfc6265#section-4.1.1
- * @param {import('./index').Cookie} cookie
- */
-function stringify (cookie) {
- if (cookie.name.length === 0) {
- return null
- }
-
- validateCookieName(cookie.name)
- validateCookieValue(cookie.value)
-
- const out = [`${cookie.name}=${cookie.value}`]
-
- // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1
- // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.2
- if (cookie.name.startsWith('__Secure-')) {
- cookie.secure = true
- }
-
- if (cookie.name.startsWith('__Host-')) {
- cookie.secure = true
- cookie.domain = null
- cookie.path = '/'
- }
-
- if (cookie.secure) {
- out.push('Secure')
- }
-
- if (cookie.httpOnly) {
- out.push('HttpOnly')
- }
-
- if (typeof cookie.maxAge === 'number') {
- validateCookieMaxAge(cookie.maxAge)
- out.push(`Max-Age=${cookie.maxAge}`)
- }
-
- if (cookie.domain) {
- validateCookieDomain(cookie.domain)
- out.push(`Domain=${cookie.domain}`)
- }
-
- if (cookie.path) {
- validateCookiePath(cookie.path)
- out.push(`Path=${cookie.path}`)
- }
-
- if (cookie.expires && cookie.expires.toString() !== 'Invalid Date') {
- out.push(`Expires=${toIMFDate(cookie.expires)}`)
- }
-
- if (cookie.sameSite) {
- out.push(`SameSite=${cookie.sameSite}`)
- }
-
- for (const part of cookie.unparsed) {
- if (!part.includes('=')) {
- throw new Error('Invalid unparsed')
- }
-
- const [key, ...value] = part.split('=')
-
- out.push(`${key.trim()}=${value.join('=')}`)
- }
-
- return out.join('; ')
-}
-
-module.exports = {
- isCTLExcludingHtab,
- validateCookieName,
- validateCookiePath,
- validateCookieValue,
- toIMFDate,
- stringify
-}
diff --git a/vanilla/node_modules/undici/lib/web/eventsource/eventsource-stream.js b/vanilla/node_modules/undici/lib/web/eventsource/eventsource-stream.js
deleted file mode 100644
index d24e8f6..0000000
--- a/vanilla/node_modules/undici/lib/web/eventsource/eventsource-stream.js
+++ /dev/null
@@ -1,399 +0,0 @@
-'use strict'
-const { Transform } = require('node:stream')
-const { isASCIINumber, isValidLastEventId } = require('./util')
-
-/**
- * @type {number[]} BOM
- */
-const BOM = [0xEF, 0xBB, 0xBF]
-/**
- * @type {10} LF
- */
-const LF = 0x0A
-/**
- * @type {13} CR
- */
-const CR = 0x0D
-/**
- * @type {58} COLON
- */
-const COLON = 0x3A
-/**
- * @type {32} SPACE
- */
-const SPACE = 0x20
-
-/**
- * @typedef {object} EventSourceStreamEvent
- * @type {object}
- * @property {string} [event] The event type.
- * @property {string} [data] The data of the message.
- * @property {string} [id] A unique ID for the event.
- * @property {string} [retry] The reconnection time, in milliseconds.
- */
-
-/**
- * @typedef eventSourceSettings
- * @type {object}
- * @property {string} [lastEventId] The last event ID received from the server.
- * @property {string} [origin] The origin of the event source.
- * @property {number} [reconnectionTime] The reconnection time, in milliseconds.
- */
-
-class EventSourceStream extends Transform {
- /**
- * @type {eventSourceSettings}
- */
- state
-
- /**
- * Leading byte-order-mark check.
- * @type {boolean}
- */
- checkBOM = true
-
- /**
- * @type {boolean}
- */
- crlfCheck = false
-
- /**
- * @type {boolean}
- */
- eventEndCheck = false
-
- /**
- * @type {Buffer|null}
- */
- buffer = null
-
- pos = 0
-
- event = {
- data: undefined,
- event: undefined,
- id: undefined,
- retry: undefined
- }
-
- /**
- * @param {object} options
- * @param {boolean} [options.readableObjectMode]
- * @param {eventSourceSettings} [options.eventSourceSettings]
- * @param {(chunk: any, encoding?: BufferEncoding | undefined) => boolean} [options.push]
- */
- constructor (options = {}) {
- // Enable object mode as EventSourceStream emits objects of shape
- // EventSourceStreamEvent
- options.readableObjectMode = true
-
- super(options)
-
- this.state = options.eventSourceSettings || {}
- if (options.push) {
- this.push = options.push
- }
- }
-
- /**
- * @param {Buffer} chunk
- * @param {string} _encoding
- * @param {Function} callback
- * @returns {void}
- */
- _transform (chunk, _encoding, callback) {
- if (chunk.length === 0) {
- callback()
- return
- }
-
- // Cache the chunk in the buffer, as the data might not be complete while
- // processing it
- // TODO: Investigate if there is a more performant way to handle
- // incoming chunks
- // see: https://github.com/nodejs/undici/issues/2630
- if (this.buffer) {
- this.buffer = Buffer.concat([this.buffer, chunk])
- } else {
- this.buffer = chunk
- }
-
- // Strip leading byte-order-mark if we opened the stream and started
- // the processing of the incoming data
- if (this.checkBOM) {
- switch (this.buffer.length) {
- case 1:
- // Check if the first byte is the same as the first byte of the BOM
- if (this.buffer[0] === BOM[0]) {
- // If it is, we need to wait for more data
- callback()
- return
- }
- // Set the checkBOM flag to false as we don't need to check for the
- // BOM anymore
- this.checkBOM = false
-
- // The buffer only contains one byte so we need to wait for more data
- callback()
- return
- case 2:
- // Check if the first two bytes are the same as the first two bytes
- // of the BOM
- if (
- this.buffer[0] === BOM[0] &&
- this.buffer[1] === BOM[1]
- ) {
- // If it is, we need to wait for more data, because the third byte
- // is needed to determine if it is the BOM or not
- callback()
- return
- }
-
- // Set the checkBOM flag to false as we don't need to check for the
- // BOM anymore
- this.checkBOM = false
- break
- case 3:
- // Check if the first three bytes are the same as the first three
- // bytes of the BOM
- if (
- this.buffer[0] === BOM[0] &&
- this.buffer[1] === BOM[1] &&
- this.buffer[2] === BOM[2]
- ) {
- // If it is, we can drop the buffered data, as it is only the BOM
- this.buffer = Buffer.alloc(0)
- // Set the checkBOM flag to false as we don't need to check for the
- // BOM anymore
- this.checkBOM = false
-
- // Await more data
- callback()
- return
- }
- // If it is not the BOM, we can start processing the data
- this.checkBOM = false
- break
- default:
- // The buffer is longer than 3 bytes, so we can drop the BOM if it is
- // present
- if (
- this.buffer[0] === BOM[0] &&
- this.buffer[1] === BOM[1] &&
- this.buffer[2] === BOM[2]
- ) {
- // Remove the BOM from the buffer
- this.buffer = this.buffer.subarray(3)
- }
-
- // Set the checkBOM flag to false as we don't need to check for the
- this.checkBOM = false
- break
- }
- }
-
- while (this.pos < this.buffer.length) {
- // If the previous line ended with an end-of-line, we need to check
- // if the next character is also an end-of-line.
- if (this.eventEndCheck) {
- // If the the current character is an end-of-line, then the event
- // is finished and we can process it
-
- // If the previous line ended with a carriage return, we need to
- // check if the current character is a line feed and remove it
- // from the buffer.
- if (this.crlfCheck) {
- // If the current character is a line feed, we can remove it
- // from the buffer and reset the crlfCheck flag
- if (this.buffer[this.pos] === LF) {
- this.buffer = this.buffer.subarray(this.pos + 1)
- this.pos = 0
- this.crlfCheck = false
-
- // It is possible that the line feed is not the end of the
- // event. We need to check if the next character is an
- // end-of-line character to determine if the event is
- // finished. We simply continue the loop to check the next
- // character.
-
- // As we removed the line feed from the buffer and set the
- // crlfCheck flag to false, we basically don't make any
- // distinction between a line feed and a carriage return.
- continue
- }
- this.crlfCheck = false
- }
-
- if (this.buffer[this.pos] === LF || this.buffer[this.pos] === CR) {
- // If the current character is a carriage return, we need to
- // set the crlfCheck flag to true, as we need to check if the
- // next character is a line feed so we can remove it from the
- // buffer
- if (this.buffer[this.pos] === CR) {
- this.crlfCheck = true
- }
-
- this.buffer = this.buffer.subarray(this.pos + 1)
- this.pos = 0
- if (
- this.event.data !== undefined || this.event.event || this.event.id !== undefined || this.event.retry) {
- this.processEvent(this.event)
- }
- this.clearEvent()
- continue
- }
- // If the current character is not an end-of-line, then the event
- // is not finished and we have to reset the eventEndCheck flag
- this.eventEndCheck = false
- continue
- }
-
- // If the current character is an end-of-line, we can process the
- // line
- if (this.buffer[this.pos] === LF || this.buffer[this.pos] === CR) {
- // If the current character is a carriage return, we need to
- // set the crlfCheck flag to true, as we need to check if the
- // next character is a line feed
- if (this.buffer[this.pos] === CR) {
- this.crlfCheck = true
- }
-
- // In any case, we can process the line as we reached an
- // end-of-line character
- this.parseLine(this.buffer.subarray(0, this.pos), this.event)
-
- // Remove the processed line from the buffer
- this.buffer = this.buffer.subarray(this.pos + 1)
- // Reset the position as we removed the processed line from the buffer
- this.pos = 0
- // A line was processed and this could be the end of the event. We need
- // to check if the next line is empty to determine if the event is
- // finished.
- this.eventEndCheck = true
- continue
- }
-
- this.pos++
- }
-
- callback()
- }
-
- /**
- * @param {Buffer} line
- * @param {EventSourceStreamEvent} event
- */
- parseLine (line, event) {
- // If the line is empty (a blank line)
- // Dispatch the event, as defined below.
- // This will be handled in the _transform method
- if (line.length === 0) {
- return
- }
-
- // If the line starts with a U+003A COLON character (:)
- // Ignore the line.
- const colonPosition = line.indexOf(COLON)
- if (colonPosition === 0) {
- return
- }
-
- let field = ''
- let value = ''
-
- // If the line contains a U+003A COLON character (:)
- if (colonPosition !== -1) {
- // Collect the characters on the line before the first U+003A COLON
- // character (:), and let field be that string.
- // TODO: Investigate if there is a more performant way to extract the
- // field
- // see: https://github.com/nodejs/undici/issues/2630
- field = line.subarray(0, colonPosition).toString('utf8')
-
- // Collect the characters on the line after the first U+003A COLON
- // character (:), and let value be that string.
- // If value starts with a U+0020 SPACE character, remove it from value.
- let valueStart = colonPosition + 1
- if (line[valueStart] === SPACE) {
- ++valueStart
- }
- // TODO: Investigate if there is a more performant way to extract the
- // value
- // see: https://github.com/nodejs/undici/issues/2630
- value = line.subarray(valueStart).toString('utf8')
-
- // Otherwise, the string is not empty but does not contain a U+003A COLON
- // character (:)
- } else {
- // Process the field using the steps described below, using the whole
- // line as the field name, and the empty string as the field value.
- field = line.toString('utf8')
- value = ''
- }
-
- // Modify the event with the field name and value. The value is also
- // decoded as UTF-8
- switch (field) {
- case 'data':
- if (event[field] === undefined) {
- event[field] = value
- } else {
- event[field] += `\n${value}`
- }
- break
- case 'retry':
- if (isASCIINumber(value)) {
- event[field] = value
- }
- break
- case 'id':
- if (isValidLastEventId(value)) {
- event[field] = value
- }
- break
- case 'event':
- if (value.length > 0) {
- event[field] = value
- }
- break
- }
- }
-
- /**
- * @param {EventSourceStreamEvent} event
- */
- processEvent (event) {
- if (event.retry && isASCIINumber(event.retry)) {
- this.state.reconnectionTime = parseInt(event.retry, 10)
- }
-
- if (event.id !== undefined && isValidLastEventId(event.id)) {
- this.state.lastEventId = event.id
- }
-
- // only dispatch event, when data is provided
- if (event.data !== undefined) {
- this.push({
- type: event.event || 'message',
- options: {
- data: event.data,
- lastEventId: this.state.lastEventId,
- origin: this.state.origin
- }
- })
- }
- }
-
- clearEvent () {
- this.event = {
- data: undefined,
- event: undefined,
- id: undefined,
- retry: undefined
- }
- }
-}
-
-module.exports = {
- EventSourceStream
-}
diff --git a/vanilla/node_modules/undici/lib/web/eventsource/eventsource.js b/vanilla/node_modules/undici/lib/web/eventsource/eventsource.js
deleted file mode 100644
index 32dcf0e..0000000
--- a/vanilla/node_modules/undici/lib/web/eventsource/eventsource.js
+++ /dev/null
@@ -1,501 +0,0 @@
-'use strict'
-
-const { pipeline } = require('node:stream')
-const { fetching } = require('../fetch')
-const { makeRequest } = require('../fetch/request')
-const { webidl } = require('../webidl')
-const { EventSourceStream } = require('./eventsource-stream')
-const { parseMIMEType } = require('../fetch/data-url')
-const { createFastMessageEvent } = require('../websocket/events')
-const { isNetworkError } = require('../fetch/response')
-const { kEnumerableProperty } = require('../../core/util')
-const { environmentSettingsObject } = require('../fetch/util')
-
-let experimentalWarned = false
-
-/**
- * A reconnection time, in milliseconds. This must initially be an implementation-defined value,
- * probably in the region of a few seconds.
- *
- * In Comparison:
- * - Chrome uses 3000ms.
- * - Deno uses 5000ms.
- *
- * @type {3000}
- */
-const defaultReconnectionTime = 3000
-
-/**
- * The readyState attribute represents the state of the connection.
- * @typedef ReadyState
- * @type {0|1|2}
- * @readonly
- * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#dom-eventsource-readystate-dev
- */
-
-/**
- * The connection has not yet been established, or it was closed and the user
- * agent is reconnecting.
- * @type {0}
- */
-const CONNECTING = 0
-
-/**
- * The user agent has an open connection and is dispatching events as it
- * receives them.
- * @type {1}
- */
-const OPEN = 1
-
-/**
- * The connection is not open, and the user agent is not trying to reconnect.
- * @type {2}
- */
-const CLOSED = 2
-
-/**
- * Requests for the element will have their mode set to "cors" and their credentials mode set to "same-origin".
- * @type {'anonymous'}
- */
-const ANONYMOUS = 'anonymous'
-
-/**
- * Requests for the element will have their mode set to "cors" and their credentials mode set to "include".
- * @type {'use-credentials'}
- */
-const USE_CREDENTIALS = 'use-credentials'
-
-/**
- * The EventSource interface is used to receive server-sent events. It
- * connects to a server over HTTP and receives events in text/event-stream
- * format without closing the connection.
- * @extends {EventTarget}
- * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events
- * @api public
- */
-class EventSource extends EventTarget {
- #events = {
- open: null,
- error: null,
- message: null
- }
-
- #url
- #withCredentials = false
-
- /**
- * @type {ReadyState}
- */
- #readyState = CONNECTING
-
- #request = null
- #controller = null
-
- #dispatcher
-
- /**
- * @type {import('./eventsource-stream').eventSourceSettings}
- */
- #state
-
- /**
- * Creates a new EventSource object.
- * @param {string} url
- * @param {EventSourceInit} [eventSourceInitDict={}]
- * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#the-eventsource-interface
- */
- constructor (url, eventSourceInitDict = {}) {
- // 1. Let ev be a new EventSource object.
- super()
-
- webidl.util.markAsUncloneable(this)
-
- const prefix = 'EventSource constructor'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- if (!experimentalWarned) {
- experimentalWarned = true
- process.emitWarning('EventSource is experimental, expect them to change at any time.', {
- code: 'UNDICI-ES'
- })
- }
-
- url = webidl.converters.USVString(url)
- eventSourceInitDict = webidl.converters.EventSourceInitDict(eventSourceInitDict, prefix, 'eventSourceInitDict')
-
- this.#dispatcher = eventSourceInitDict.node.dispatcher || eventSourceInitDict.dispatcher
- this.#state = {
- lastEventId: '',
- reconnectionTime: eventSourceInitDict.node.reconnectionTime
- }
-
- // 2. Let settings be ev's relevant settings object.
- // https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object
- const settings = environmentSettingsObject
-
- let urlRecord
-
- try {
- // 3. Let urlRecord be the result of encoding-parsing a URL given url, relative to settings.
- urlRecord = new URL(url, settings.settingsObject.baseUrl)
- this.#state.origin = urlRecord.origin
- } catch (e) {
- // 4. If urlRecord is failure, then throw a "SyntaxError" DOMException.
- throw new DOMException(e, 'SyntaxError')
- }
-
- // 5. Set ev's url to urlRecord.
- this.#url = urlRecord.href
-
- // 6. Let corsAttributeState be Anonymous.
- let corsAttributeState = ANONYMOUS
-
- // 7. If the value of eventSourceInitDict's withCredentials member is true,
- // then set corsAttributeState to Use Credentials and set ev's
- // withCredentials attribute to true.
- if (eventSourceInitDict.withCredentials === true) {
- corsAttributeState = USE_CREDENTIALS
- this.#withCredentials = true
- }
-
- // 8. Let request be the result of creating a potential-CORS request given
- // urlRecord, the empty string, and corsAttributeState.
- const initRequest = {
- redirect: 'follow',
- keepalive: true,
- // @see https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cors-settings-attributes
- mode: 'cors',
- credentials: corsAttributeState === 'anonymous'
- ? 'same-origin'
- : 'omit',
- referrer: 'no-referrer'
- }
-
- // 9. Set request's client to settings.
- initRequest.client = environmentSettingsObject.settingsObject
-
- // 10. User agents may set (`Accept`, `text/event-stream`) in request's header list.
- initRequest.headersList = [['accept', { name: 'accept', value: 'text/event-stream' }]]
-
- // 11. Set request's cache mode to "no-store".
- initRequest.cache = 'no-store'
-
- // 12. Set request's initiator type to "other".
- initRequest.initiator = 'other'
-
- initRequest.urlList = [new URL(this.#url)]
-
- // 13. Set ev's request to request.
- this.#request = makeRequest(initRequest)
-
- this.#connect()
- }
-
- /**
- * Returns the state of this EventSource object's connection. It can have the
- * values described below.
- * @returns {ReadyState}
- * @readonly
- */
- get readyState () {
- return this.#readyState
- }
-
- /**
- * Returns the URL providing the event stream.
- * @readonly
- * @returns {string}
- */
- get url () {
- return this.#url
- }
-
- /**
- * Returns a boolean indicating whether the EventSource object was
- * instantiated with CORS credentials set (true), or not (false, the default).
- */
- get withCredentials () {
- return this.#withCredentials
- }
-
- #connect () {
- if (this.#readyState === CLOSED) return
-
- this.#readyState = CONNECTING
-
- const fetchParams = {
- request: this.#request,
- dispatcher: this.#dispatcher
- }
-
- // 14. Let processEventSourceEndOfBody given response res be the following step: if res is not a network error, then reestablish the connection.
- const processEventSourceEndOfBody = (response) => {
- if (!isNetworkError(response)) {
- return this.#reconnect()
- }
- }
-
- // 15. Fetch request, with processResponseEndOfBody set to processEventSourceEndOfBody...
- fetchParams.processResponseEndOfBody = processEventSourceEndOfBody
-
- // and processResponse set to the following steps given response res:
- fetchParams.processResponse = (response) => {
- // 1. If res is an aborted network error, then fail the connection.
-
- if (isNetworkError(response)) {
- // 1. When a user agent is to fail the connection, the user agent
- // must queue a task which, if the readyState attribute is set to a
- // value other than CLOSED, sets the readyState attribute to CLOSED
- // and fires an event named error at the EventSource object. Once the
- // user agent has failed the connection, it does not attempt to
- // reconnect.
- if (response.aborted) {
- this.close()
- this.dispatchEvent(new Event('error'))
- return
- // 2. Otherwise, if res is a network error, then reestablish the
- // connection, unless the user agent knows that to be futile, in
- // which case the user agent may fail the connection.
- } else {
- this.#reconnect()
- return
- }
- }
-
- // 3. Otherwise, if res's status is not 200, or if res's `Content-Type`
- // is not `text/event-stream`, then fail the connection.
- const contentType = response.headersList.get('content-type', true)
- const mimeType = contentType !== null ? parseMIMEType(contentType) : 'failure'
- const contentTypeValid = mimeType !== 'failure' && mimeType.essence === 'text/event-stream'
- if (
- response.status !== 200 ||
- contentTypeValid === false
- ) {
- this.close()
- this.dispatchEvent(new Event('error'))
- return
- }
-
- // 4. Otherwise, announce the connection and interpret res's body
- // line by line.
-
- // When a user agent is to announce the connection, the user agent
- // must queue a task which, if the readyState attribute is set to a
- // value other than CLOSED, sets the readyState attribute to OPEN
- // and fires an event named open at the EventSource object.
- // @see https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model
- this.#readyState = OPEN
- this.dispatchEvent(new Event('open'))
-
- // If redirected to a different origin, set the origin to the new origin.
- this.#state.origin = response.urlList[response.urlList.length - 1].origin
-
- const eventSourceStream = new EventSourceStream({
- eventSourceSettings: this.#state,
- push: (event) => {
- this.dispatchEvent(createFastMessageEvent(
- event.type,
- event.options
- ))
- }
- })
-
- pipeline(response.body.stream,
- eventSourceStream,
- (error) => {
- if (
- error?.aborted === false
- ) {
- this.close()
- this.dispatchEvent(new Event('error'))
- }
- })
- }
-
- this.#controller = fetching(fetchParams)
- }
-
- /**
- * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model
- * @returns {void}
- */
- #reconnect () {
- // When a user agent is to reestablish the connection, the user agent must
- // run the following steps. These steps are run in parallel, not as part of
- // a task. (The tasks that it queues, of course, are run like normal tasks
- // and not themselves in parallel.)
-
- // 1. Queue a task to run the following steps:
-
- // 1. If the readyState attribute is set to CLOSED, abort the task.
- if (this.#readyState === CLOSED) return
-
- // 2. Set the readyState attribute to CONNECTING.
- this.#readyState = CONNECTING
-
- // 3. Fire an event named error at the EventSource object.
- this.dispatchEvent(new Event('error'))
-
- // 2. Wait a delay equal to the reconnection time of the event source.
- setTimeout(() => {
- // 5. Queue a task to run the following steps:
-
- // 1. If the EventSource object's readyState attribute is not set to
- // CONNECTING, then return.
- if (this.#readyState !== CONNECTING) return
-
- // 2. Let request be the EventSource object's request.
- // 3. If the EventSource object's last event ID string is not the empty
- // string, then:
- // 1. Let lastEventIDValue be the EventSource object's last event ID
- // string, encoded as UTF-8.
- // 2. Set (`Last-Event-ID`, lastEventIDValue) in request's header
- // list.
- if (this.#state.lastEventId.length) {
- this.#request.headersList.set('last-event-id', this.#state.lastEventId, true)
- }
-
- // 4. Fetch request and process the response obtained in this fashion, if any, as described earlier in this section.
- this.#connect()
- }, this.#state.reconnectionTime)?.unref()
- }
-
- /**
- * Closes the connection, if any, and sets the readyState attribute to
- * CLOSED.
- */
- close () {
- webidl.brandCheck(this, EventSource)
-
- if (this.#readyState === CLOSED) return
- this.#readyState = CLOSED
- this.#controller.abort()
- this.#request = null
- }
-
- get onopen () {
- return this.#events.open
- }
-
- set onopen (fn) {
- if (this.#events.open) {
- this.removeEventListener('open', this.#events.open)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('open', listener)
- this.#events.open = fn
- } else {
- this.#events.open = null
- }
- }
-
- get onmessage () {
- return this.#events.message
- }
-
- set onmessage (fn) {
- if (this.#events.message) {
- this.removeEventListener('message', this.#events.message)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('message', listener)
- this.#events.message = fn
- } else {
- this.#events.message = null
- }
- }
-
- get onerror () {
- return this.#events.error
- }
-
- set onerror (fn) {
- if (this.#events.error) {
- this.removeEventListener('error', this.#events.error)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('error', listener)
- this.#events.error = fn
- } else {
- this.#events.error = null
- }
- }
-}
-
-const constantsPropertyDescriptors = {
- CONNECTING: {
- __proto__: null,
- configurable: false,
- enumerable: true,
- value: CONNECTING,
- writable: false
- },
- OPEN: {
- __proto__: null,
- configurable: false,
- enumerable: true,
- value: OPEN,
- writable: false
- },
- CLOSED: {
- __proto__: null,
- configurable: false,
- enumerable: true,
- value: CLOSED,
- writable: false
- }
-}
-
-Object.defineProperties(EventSource, constantsPropertyDescriptors)
-Object.defineProperties(EventSource.prototype, constantsPropertyDescriptors)
-
-Object.defineProperties(EventSource.prototype, {
- close: kEnumerableProperty,
- onerror: kEnumerableProperty,
- onmessage: kEnumerableProperty,
- onopen: kEnumerableProperty,
- readyState: kEnumerableProperty,
- url: kEnumerableProperty,
- withCredentials: kEnumerableProperty
-})
-
-webidl.converters.EventSourceInitDict = webidl.dictionaryConverter([
- {
- key: 'withCredentials',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- },
- {
- key: 'dispatcher', // undici only
- converter: webidl.converters.any
- },
- {
- key: 'node', // undici only
- converter: webidl.dictionaryConverter([
- {
- key: 'reconnectionTime',
- converter: webidl.converters['unsigned long'],
- defaultValue: () => defaultReconnectionTime
- },
- {
- key: 'dispatcher',
- converter: webidl.converters.any
- }
- ]),
- defaultValue: () => ({})
- }
-])
-
-module.exports = {
- EventSource,
- defaultReconnectionTime
-}
diff --git a/vanilla/node_modules/undici/lib/web/eventsource/util.js b/vanilla/node_modules/undici/lib/web/eventsource/util.js
deleted file mode 100644
index a87cc83..0000000
--- a/vanilla/node_modules/undici/lib/web/eventsource/util.js
+++ /dev/null
@@ -1,29 +0,0 @@
-'use strict'
-
-/**
- * Checks if the given value is a valid LastEventId.
- * @param {string} value
- * @returns {boolean}
- */
-function isValidLastEventId (value) {
- // LastEventId should not contain U+0000 NULL
- return value.indexOf('\u0000') === -1
-}
-
-/**
- * Checks if the given value is a base 10 digit.
- * @param {string} value
- * @returns {boolean}
- */
-function isASCIINumber (value) {
- if (value.length === 0) return false
- for (let i = 0; i < value.length; i++) {
- if (value.charCodeAt(i) < 0x30 || value.charCodeAt(i) > 0x39) return false
- }
- return true
-}
-
-module.exports = {
- isValidLastEventId,
- isASCIINumber
-}
diff --git a/vanilla/node_modules/undici/lib/web/fetch/LICENSE b/vanilla/node_modules/undici/lib/web/fetch/LICENSE
deleted file mode 100644
index 2943500..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-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.
diff --git a/vanilla/node_modules/undici/lib/web/fetch/body.js b/vanilla/node_modules/undici/lib/web/fetch/body.js
deleted file mode 100644
index 5d17249..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/body.js
+++ /dev/null
@@ -1,509 +0,0 @@
-'use strict'
-
-const util = require('../../core/util')
-const {
- ReadableStreamFrom,
- readableStreamClose,
- fullyReadBody,
- extractMimeType
-} = require('./util')
-const { FormData, setFormDataState } = require('./formdata')
-const { webidl } = require('../webidl')
-const assert = require('node:assert')
-const { isErrored, isDisturbed } = require('node:stream')
-const { isUint8Array } = require('node:util/types')
-const { serializeAMimeType } = require('./data-url')
-const { multipartFormDataParser } = require('./formdata-parser')
-const { createDeferredPromise } = require('../../util/promise')
-const { parseJSONFromBytes } = require('../infra')
-const { utf8DecodeBytes } = require('../../encoding')
-const { runtimeFeatures } = require('../../util/runtime-features.js')
-
-const random = runtimeFeatures.has('crypto')
- ? require('node:crypto').randomInt
- : (max) => Math.floor(Math.random() * max)
-
-const textEncoder = new TextEncoder()
-function noop () {}
-
-const streamRegistry = new FinalizationRegistry((weakRef) => {
- const stream = weakRef.deref()
- if (stream && !stream.locked && !isDisturbed(stream) && !isErrored(stream)) {
- stream.cancel('Response object has been garbage collected').catch(noop)
- }
-})
-
-/**
- * Extract a body with type from a byte sequence or BodyInit object
- *
- * @param {import('../../../types').BodyInit} object - The BodyInit object to extract from
- * @param {boolean} [keepalive=false] - If true, indicates that the body
- * @returns {[{stream: ReadableStream, source: any, length: number | null}, string | null]} - Returns a tuple containing the body and its type
- *
- * @see https://fetch.spec.whatwg.org/#concept-bodyinit-extract
- */
-function extractBody (object, keepalive = false) {
- // 1. Let stream be null.
- let stream = null
- let controller = null
-
- // 2. If object is a ReadableStream object, then set stream to object.
- if (webidl.is.ReadableStream(object)) {
- stream = object
- } else if (webidl.is.Blob(object)) {
- // 3. Otherwise, if object is a Blob object, set stream to the
- // result of running object’s get stream.
- stream = object.stream()
- } else {
- // 4. Otherwise, set stream to a new ReadableStream object, and set
- // up stream with byte reading support.
- stream = new ReadableStream({
- pull () {},
- start (c) {
- controller = c
- },
- cancel () {},
- type: 'bytes'
- })
- }
-
- // 5. Assert: stream is a ReadableStream object.
- assert(webidl.is.ReadableStream(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 (webidl.is.URLSearchParams(object)) {
- // 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 object’s 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 (webidl.is.BufferSource(object)) {
- // Set source to a copy of the bytes held by object.
- source = webidl.util.getCopyOfBytesHeldByBufferSource(object)
- } else if (webidl.is.FormData(object)) {
- const boundary = `----formdata-undici-0${`${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 formdataEscape = (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 object’s 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="${formdataEscape(normalizeLinefeeds(name))}"` +
- `\r\n\r\n${normalizeLinefeeds(value)}\r\n`)
- blobParts.push(chunk)
- length += chunk.byteLength
- } else {
- const chunk = textEncoder.encode(`${prefix}; name="${formdataEscape(normalizeLinefeeds(name))}"` +
- (value.name ? `; filename="${formdataEscape(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
- }
- }
- }
-
- // CRLF is appended to the body to function with legacy servers and match other implementations.
- // https://github.com/curl/curl/blob/3434c6b46e682452973972e8313613dfa58cd690/lib/mime.c#L1029-L1030
- // https://github.com/form-data/form-data/issues/63
- const chunk = textEncoder.encode(`--${boundary}--\r\n`)
- 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 (webidl.is.Blob(object)) {
- // Blob
-
- // Set source to object.
- source = object
-
- // Set length to object’s size.
- length = object.size
-
- // If object’s 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 =
- webidl.is.ReadableStream(object) ? object : ReadableStreamFrom(object)
- }
-
- // 11. If source is a byte sequence, then set action to a
- // step that returns source and length to source’s length.
- if (typeof source === 'string' || isUint8Array(source)) {
- action = () => {
- length = typeof source === 'string' ? Buffer.byteLength(source) : source.length
- return source
- }
- }
-
- // 12. If action is non-null, then run these steps in parallel:
- if (action != null) {
- ;(async () => {
- // 1. Run action.
- const result = action()
-
- // 2. Whenever one or more bytes are available and stream is not errored,
- // enqueue the result of creating a Uint8Array from the available bytes into stream.
- const iterator = result?.[Symbol.asyncIterator]?.()
- if (iterator) {
- for await (const bytes of iterator) {
- if (isErrored(stream)) break
- if (bytes.length) {
- controller.enqueue(new Uint8Array(bytes))
- }
- }
- } else if (result?.length && !isErrored(stream)) {
- controller.enqueue(typeof result === 'string' ? textEncoder.encode(result) : new Uint8Array(result))
- }
-
- // 3. When running action is done, close stream.
- queueMicrotask(() => readableStreamClose(controller))
- })()
- }
-
- // 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]
-}
-
-/**
- * @typedef {object} ExtractBodyResult
- * @property {ReadableStream<Uint8Array<ArrayBuffer>>} stream - The ReadableStream containing the body data
- * @property {any} source - The original source of the body data
- * @property {number | null} length - The length of the body data, or null
- */
-
-/**
- * Safely extract a body with type from a byte sequence or BodyInit object.
- *
- * @param {import('../../../types').BodyInit} object - The BodyInit object to extract from
- * @param {boolean} [keepalive=false] - If true, indicates that the body
- * @returns {[ExtractBodyResult, string | null]} - Returns a tuple containing the body and its type
- *
- * @see https://fetch.spec.whatwg.org/#bodyinit-safely-extract
- */
-function safelyExtractBody (object, keepalive = false) {
- // 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 (webidl.is.ReadableStream(object)) {
- // Assert: object is neither disturbed nor locked.
- assert(!util.isDisturbed(object), 'The body has already been consumed.')
- 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 body’s stream.
- const { 0: out1, 1: out2 } = body.stream.tee()
-
- // 2. Set body’s stream to out1.
- body.stream = out1
-
- // 3. Return a body whose stream is out2 and other members are copied from body.
- return {
- stream: out2,
- length: body.length,
- source: body.source
- }
-}
-
-function bodyMixinMethods (instance, getInternalState) {
- 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 this’s
- // MIME type.
- return consumeBody(this, (bytes) => {
- let mimeType = bodyMimeType(getInternalState(this))
-
- if (mimeType === null) {
- 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, getInternalState)
- },
-
- 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 consumeBody(this, (bytes) => {
- return new Uint8Array(bytes).buffer
- }, instance, getInternalState)
- },
-
- text () {
- // The text() method steps are to return the result of running
- // consume body with this and UTF-8 decode.
- return consumeBody(this, utf8DecodeBytes, instance, getInternalState)
- },
-
- json () {
- // The json() method steps are to return the result of running
- // consume body with this and parse JSON from bytes.
- return consumeBody(this, parseJSONFromBytes, instance, getInternalState)
- },
-
- formData () {
- // The formData() method steps are to return the result of running
- // consume body with this and the following step given a byte sequence bytes:
- return consumeBody(this, (value) => {
- // 1. Let mimeType be the result of get the MIME type with this.
- const mimeType = bodyMimeType(getInternalState(this))
-
- // 2. If mimeType is non-null, then switch on mimeType’s essence and run
- // the corresponding steps:
- if (mimeType !== null) {
- switch (mimeType.essence) {
- case 'multipart/form-data': {
- // 1. ... [long step]
- // 2. If that fails for some reason, then throw a TypeError.
- const parsed = multipartFormDataParser(value, mimeType)
-
- // 3. Return a new FormData object, appending each entry,
- // resulting from the parsing operation, to its entry list.
- const fd = new FormData()
- setFormDataState(fd, parsed)
-
- return fd
- }
- case 'application/x-www-form-urlencoded': {
- // 1. Let entries be the result of parsing bytes.
- const entries = new URLSearchParams(value.toString())
-
- // 2. If entries is failure, then throw a TypeError.
-
- // 3. Return a new FormData object whose entry list is entries.
- const fd = new FormData()
-
- for (const [name, value] of entries) {
- fd.append(name, value)
- }
-
- return fd
- }
- }
- }
-
- // 3. Throw a TypeError.
- throw new TypeError(
- 'Content-Type was not one of "multipart/form-data" or "application/x-www-form-urlencoded".'
- )
- }, instance, getInternalState)
- },
-
- bytes () {
- // The bytes() method steps are to return the result of running consume body
- // with this and the following step given a byte sequence bytes: return the
- // result of creating a Uint8Array from bytes in this’s relevant realm.
- return consumeBody(this, (bytes) => {
- return new Uint8Array(bytes)
- }, instance, getInternalState)
- }
- }
-
- return methods
-}
-
-function mixinBody (prototype, getInternalState) {
- Object.assign(prototype.prototype, bodyMixinMethods(prototype, getInternalState))
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#concept-body-consume-body
- * @param {any} object internal state
- * @param {(value: unknown) => unknown} convertBytesToJSValue
- * @param {any} instance
- * @param {(target: any) => any} getInternalState
- */
-function consumeBody (object, convertBytesToJSValue, instance, getInternalState) {
- try {
- webidl.brandCheck(object, instance)
- } catch (e) {
- return Promise.reject(e)
- }
-
- object = getInternalState(object)
-
- // 1. If object is unusable, then return a promise rejected
- // with a TypeError.
- if (bodyUnusable(object)) {
- return Promise.reject(new TypeError('Body is unusable: Body has already been read'))
- }
-
- // 2. Let promise be a new promise.
- const promise = createDeferredPromise()
-
- // 3. Let errorSteps given error be to reject promise with error.
- const errorSteps = promise.reject
-
- // 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 object’s body is null, then run successSteps with an
- // empty byte sequence.
- if (object.body == null) {
- successSteps(Buffer.allocUnsafe(0))
- return promise.promise
- }
-
- // 6. Otherwise, fully read object’s body given successSteps,
- // errorSteps, and object’s relevant global object.
- fullyReadBody(object.body, successSteps, errorSteps)
-
- // 7. Return promise.
- return promise.promise
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#body-unusable
- * @param {any} object internal state
- */
-function bodyUnusable (object) {
- const body = object.body
-
- // An object including the Body interface mixin is
- // said to be unusable if its body is non-null and
- // its body’s stream is disturbed or locked.
- return body != null && (body.stream.locked || util.isDisturbed(body.stream))
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#concept-body-mime-type
- * @param {any} requestOrResponse internal state
- */
-function bodyMimeType (requestOrResponse) {
- // 1. Let headers be null.
- // 2. If requestOrResponse is a Request object, then set headers to requestOrResponse’s request’s header list.
- // 3. Otherwise, set headers to requestOrResponse’s response’s header list.
- /** @type {import('./headers').HeadersList} */
- const headers = requestOrResponse.headersList
-
- // 4. Let mimeType be the result of extracting a MIME type from headers.
- const mimeType = extractMimeType(headers)
-
- // 5. If mimeType is failure, then return null.
- if (mimeType === 'failure') {
- return null
- }
-
- // 6. Return mimeType.
- return mimeType
-}
-
-module.exports = {
- extractBody,
- safelyExtractBody,
- cloneBody,
- mixinBody,
- streamRegistry,
- bodyUnusable
-}
diff --git a/vanilla/node_modules/undici/lib/web/fetch/constants.js b/vanilla/node_modules/undici/lib/web/fetch/constants.js
deleted file mode 100644
index ef63b0c..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/constants.js
+++ /dev/null
@@ -1,131 +0,0 @@
-'use strict'
-
-const corsSafeListedMethods = /** @type {const} */ (['GET', 'HEAD', 'POST'])
-const corsSafeListedMethodsSet = new Set(corsSafeListedMethods)
-
-const nullBodyStatus = /** @type {const} */ ([101, 204, 205, 304])
-
-const redirectStatus = /** @type {const} */ ([301, 302, 303, 307, 308])
-const redirectStatusSet = new Set(redirectStatus)
-
-/**
- * @see https://fetch.spec.whatwg.org/#block-bad-port
- */
-const badPorts = /** @type {const} */ ([
- '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', '4190', '5060', '5061', '6000', '6566', '6665', '6666', '6667', '6668', '6669', '6679',
- '6697', '10080'
-])
-const badPortsSet = new Set(badPorts)
-
-/**
- * @see https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-header
- */
-const referrerPolicyTokens = /** @type {const} */ ([
- 'no-referrer',
- 'no-referrer-when-downgrade',
- 'same-origin',
- 'origin',
- 'strict-origin',
- 'origin-when-cross-origin',
- 'strict-origin-when-cross-origin',
- 'unsafe-url'
-])
-
-/**
- * @see https://w3c.github.io/webappsec-referrer-policy/#referrer-policies
- */
-const referrerPolicy = /** @type {const} */ ([
- '',
- ...referrerPolicyTokens
-])
-const referrerPolicyTokensSet = new Set(referrerPolicyTokens)
-
-const requestRedirect = /** @type {const} */ (['follow', 'manual', 'error'])
-
-const safeMethods = /** @type {const} */ (['GET', 'HEAD', 'OPTIONS', 'TRACE'])
-const safeMethodsSet = new Set(safeMethods)
-
-const requestMode = /** @type {const} */ (['navigate', 'same-origin', 'no-cors', 'cors'])
-
-const requestCredentials = /** @type {const} */ (['omit', 'same-origin', 'include'])
-
-const requestCache = /** @type {const} */ ([
- 'default',
- 'no-store',
- 'reload',
- 'no-cache',
- 'force-cache',
- 'only-if-cached'
-])
-
-/**
- * @see https://fetch.spec.whatwg.org/#request-body-header-name
- */
-const requestBodyHeader = /** @type {const} */ ([
- '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'
-])
-
-/**
- * @see https://fetch.spec.whatwg.org/#enumdef-requestduplex
- */
-const requestDuplex = /** @type {const} */ ([
- 'half'
-])
-
-/**
- * @see http://fetch.spec.whatwg.org/#forbidden-method
- */
-const forbiddenMethods = /** @type {const} */ (['CONNECT', 'TRACE', 'TRACK'])
-const forbiddenMethodsSet = new Set(forbiddenMethods)
-
-const subresource = /** @type {const} */ ([
- 'audio',
- 'audioworklet',
- 'font',
- 'image',
- 'manifest',
- 'paintworklet',
- 'script',
- 'style',
- 'track',
- 'video',
- 'xslt',
- ''
-])
-const subresourceSet = new Set(subresource)
-
-module.exports = {
- subresource,
- forbiddenMethods,
- requestBodyHeader,
- referrerPolicy,
- requestRedirect,
- requestMode,
- requestCredentials,
- requestCache,
- redirectStatus,
- corsSafeListedMethods,
- nullBodyStatus,
- safeMethods,
- badPorts,
- requestDuplex,
- subresourceSet,
- badPortsSet,
- redirectStatusSet,
- corsSafeListedMethodsSet,
- safeMethodsSet,
- forbiddenMethodsSet,
- referrerPolicyTokens: referrerPolicyTokensSet
-}
diff --git a/vanilla/node_modules/undici/lib/web/fetch/data-url.js b/vanilla/node_modules/undici/lib/web/fetch/data-url.js
deleted file mode 100644
index 27ad7ae..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/data-url.js
+++ /dev/null
@@ -1,596 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { forgivingBase64, collectASequenceOfCodePoints, collectASequenceOfCodePointsFast, isomorphicDecode, removeASCIIWhitespace, removeChars } = require('../infra')
-
-const encoder = new TextEncoder()
-
-/**
- * @see https://mimesniff.spec.whatwg.org/#http-token-code-point
- */
-const HTTP_TOKEN_CODEPOINTS = /^[-!#$%&'*+.^_|~A-Za-z0-9]+$/u
-const HTTP_WHITESPACE_REGEX = /[\u000A\u000D\u0009\u0020]/u // 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]+$/u // eslint-disable-line
-
-// https://fetch.spec.whatwg.org/#data-url-processor
-/** @param {URL} dataURL */
-function dataURLProcessor (dataURL) {
- // 1. Assert: dataURL’s 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*)base64$/ui.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+)$/u, '')
-
- // 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) {
- if (!excludeFragment) {
- return url.href
- }
-
- const href = url.href
- const hashLength = url.hash.length
-
- const serialized = hashLength === 0 ? href : href.substring(0, href.length - hashLength)
-
- if (!hashLength && href.endsWith('#')) {
- return serialized.slice(0, -1)
- }
-
- return serialized
-}
-
-// 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)
-}
-
-/**
- * @param {number} byte
- */
-function isHexCharByte (byte) {
- // 0-9 A-F a-f
- return (byte >= 0x30 && byte <= 0x39) || (byte >= 0x41 && byte <= 0x46) || (byte >= 0x61 && byte <= 0x66)
-}
-
-/**
- * @param {number} byte
- */
-function hexByteToNumber (byte) {
- return (
- // 0-9
- byte >= 0x30 && byte <= 0x39
- ? (byte - 48)
- // Convert to uppercase
- // ((byte & 0xDF) - 65) + 10
- : ((byte & 0xDF) - 55)
- )
-}
-
-// https://url.spec.whatwg.org/#percent-decode
-/** @param {Uint8Array} input */
-function percentDecode (input) {
- const length = input.length
- // 1. Let output be an empty byte sequence.
- /** @type {Uint8Array} */
- const output = new Uint8Array(length)
- let j = 0
- let i = 0
- // 2. For each byte byte in input:
- while (i < length) {
- const byte = input[i]
-
- // 1. If byte is not 0x25 (%), then append byte to output.
- if (byte !== 0x25) {
- output[j++] = 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 &&
- !(isHexCharByte(input[i + 1]) && isHexCharByte(input[i + 2]))
- ) {
- output[j++] = 0x25
-
- // 3. Otherwise:
- } else {
- // 1. Let bytePoint be the two bytes after byte in input,
- // decoded, and then interpreted as hexadecimal number.
- // 2. Append a byte whose value is bytePoint to output.
- output[j++] = (hexByteToNumber(input[i + 1]) << 4) | hexByteToNumber(input[i + 2])
-
- // 3. Skip the next two bytes in input.
- i += 2
- }
- ++i
- }
-
- // 3. Return output.
- return length === j ? output : output.subarray(0, j)
-}
-
-// 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
- // - mimeType’s parameters[parameterName] does not exist
- // then set mimeType’s 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://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=false]
- */
-function collectAnHTTPQuotedString (input, position, extractValue = false) {
- // 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 mimeType’s
- // type, U+002F (/), and mimeType’s subtype.
- let serialization = essence
-
- // 2. For each name → value of mimeType’s 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 occurrence of U+0022 (") or
- // U+005C (\) in value with U+005C (\).
- value = value.replace(/[\\"]/ug, '\\$&')
-
- // 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 {number} char
- */
-function isHTTPWhiteSpace (char) {
- // "\r\n\t "
- return char === 0x00d || char === 0x00a || char === 0x009 || char === 0x020
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#http-whitespace
- * @param {string} str
- * @param {boolean} [leading=true]
- * @param {boolean} [trailing=true]
- */
-function removeHTTPWhitespace (str, leading = true, trailing = true) {
- return removeChars(str, leading, trailing, isHTTPWhiteSpace)
-}
-
-/**
- * @see https://mimesniff.spec.whatwg.org/#minimize-a-supported-mime-type
- * @param {Exclude<ReturnType<typeof parseMIMEType>, 'failure'>} mimeType
- */
-function minimizeSupportedMimeType (mimeType) {
- switch (mimeType.essence) {
- case 'application/ecmascript':
- case 'application/javascript':
- case 'application/x-ecmascript':
- case 'application/x-javascript':
- case 'text/ecmascript':
- case 'text/javascript':
- case 'text/javascript1.0':
- case 'text/javascript1.1':
- case 'text/javascript1.2':
- case 'text/javascript1.3':
- case 'text/javascript1.4':
- case 'text/javascript1.5':
- case 'text/jscript':
- case 'text/livescript':
- case 'text/x-ecmascript':
- case 'text/x-javascript':
- // 1. If mimeType is a JavaScript MIME type, then return "text/javascript".
- return 'text/javascript'
- case 'application/json':
- case 'text/json':
- // 2. If mimeType is a JSON MIME type, then return "application/json".
- return 'application/json'
- case 'image/svg+xml':
- // 3. If mimeType’s essence is "image/svg+xml", then return "image/svg+xml".
- return 'image/svg+xml'
- case 'text/xml':
- case 'application/xml':
- // 4. If mimeType is an XML MIME type, then return "application/xml".
- return 'application/xml'
- }
-
- // 2. If mimeType is a JSON MIME type, then return "application/json".
- if (mimeType.subtype.endsWith('+json')) {
- return 'application/json'
- }
-
- // 4. If mimeType is an XML MIME type, then return "application/xml".
- if (mimeType.subtype.endsWith('+xml')) {
- return 'application/xml'
- }
-
- // 5. If mimeType is supported by the user agent, then return mimeType’s essence.
- // Technically, node doesn't support any mimetypes.
-
- // 6. Return the empty string.
- return ''
-}
-
-module.exports = {
- dataURLProcessor,
- URLSerializer,
- stringPercentDecode,
- parseMIMEType,
- collectAnHTTPQuotedString,
- serializeAMimeType,
- removeHTTPWhitespace,
- minimizeSupportedMimeType,
- HTTP_TOKEN_CODEPOINTS
-}
diff --git a/vanilla/node_modules/undici/lib/web/fetch/formdata-parser.js b/vanilla/node_modules/undici/lib/web/fetch/formdata-parser.js
deleted file mode 100644
index 4ba204c..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/formdata-parser.js
+++ /dev/null
@@ -1,575 +0,0 @@
-'use strict'
-
-const { bufferToLowerCasedHeaderName } = require('../../core/util')
-const { HTTP_TOKEN_CODEPOINTS } = require('./data-url')
-const { makeEntry } = require('./formdata')
-const { webidl } = require('../webidl')
-const assert = require('node:assert')
-const { isomorphicDecode } = require('../infra')
-const { utf8DecodeBytes } = require('../../encoding')
-
-const dd = Buffer.from('--')
-const decoder = new TextDecoder()
-
-/**
- * @param {string} chars
- */
-function isAsciiString (chars) {
- for (let i = 0; i < chars.length; ++i) {
- if ((chars.charCodeAt(i) & ~0x7F) !== 0) {
- return false
- }
- }
- return true
-}
-
-/**
- * @see https://andreubotella.github.io/multipart-form-data/#multipart-form-data-boundary
- * @param {string} boundary
- */
-function validateBoundary (boundary) {
- const length = boundary.length
-
- // - its length is greater or equal to 27 and lesser or equal to 70, and
- if (length < 27 || length > 70) {
- return false
- }
-
- // - it is composed by bytes in the ranges 0x30 to 0x39, 0x41 to 0x5A, or
- // 0x61 to 0x7A, inclusive (ASCII alphanumeric), or which are 0x27 ('),
- // 0x2D (-) or 0x5F (_).
- for (let i = 0; i < length; ++i) {
- const cp = boundary.charCodeAt(i)
-
- if (!(
- (cp >= 0x30 && cp <= 0x39) ||
- (cp >= 0x41 && cp <= 0x5a) ||
- (cp >= 0x61 && cp <= 0x7a) ||
- cp === 0x27 ||
- cp === 0x2d ||
- cp === 0x5f
- )) {
- return false
- }
- }
-
- return true
-}
-
-/**
- * @see https://andreubotella.github.io/multipart-form-data/#multipart-form-data-parser
- * @param {Buffer} input
- * @param {ReturnType<import('./data-url')['parseMIMEType']>} mimeType
- */
-function multipartFormDataParser (input, mimeType) {
- // 1. Assert: mimeType’s essence is "multipart/form-data".
- assert(mimeType !== 'failure' && mimeType.essence === 'multipart/form-data')
-
- const boundaryString = mimeType.parameters.get('boundary')
-
- // 2. If mimeType’s parameters["boundary"] does not exist, return failure.
- // Otherwise, let boundary be the result of UTF-8 decoding mimeType’s
- // parameters["boundary"].
- if (boundaryString === undefined) {
- throw parsingError('missing boundary in content-type header')
- }
-
- const boundary = Buffer.from(`--${boundaryString}`, 'utf8')
-
- // 3. Let entry list be an empty entry list.
- const entryList = []
-
- // 4. Let position be a pointer to a byte in input, initially pointing at
- // the first byte.
- const position = { position: 0 }
-
- // Note: Per RFC 2046 Section 5.1.1, we must ignore anything before the
- // first boundary delimiter line (preamble). Search for the first boundary.
- const firstBoundaryIndex = input.indexOf(boundary)
-
- if (firstBoundaryIndex === -1) {
- throw parsingError('no boundary found in multipart body')
- }
-
- // Start parsing from the first boundary, ignoring any preamble
- position.position = firstBoundaryIndex
-
- // 5. While true:
- while (true) {
- // 5.1. If position points to a sequence of bytes starting with 0x2D 0x2D
- // (`--`) followed by boundary, advance position by 2 + the length of
- // boundary. Otherwise, return failure.
- // Note: boundary is padded with 2 dashes already, no need to add 2.
- if (input.subarray(position.position, position.position + boundary.length).equals(boundary)) {
- position.position += boundary.length
- } else {
- throw parsingError('expected a value starting with -- and the boundary')
- }
-
- // 5.2. If position points to the sequence of bytes 0x2D 0x2D 0x0D 0x0A
- // (`--` followed by CR LF) followed by the end of input, return entry list.
- // Note: Per RFC 2046 Section 5.1.1, we must ignore anything after the
- // final boundary delimiter (epilogue). Check for -- or --CRLF and return
- // regardless of what follows.
- if (bufferStartsWith(input, dd, position)) {
- // Found closing boundary delimiter (--), ignore any epilogue
- return entryList
- }
-
- // 5.3. If position does not point to a sequence of bytes starting with 0x0D
- // 0x0A (CR LF), return failure.
- if (input[position.position] !== 0x0d || input[position.position + 1] !== 0x0a) {
- throw parsingError('expected CRLF')
- }
-
- // 5.4. Advance position by 2. (This skips past the newline.)
- position.position += 2
-
- // 5.5. Let name, filename and contentType be the result of parsing
- // multipart/form-data headers on input and position, if the result
- // is not failure. Otherwise, return failure.
- const result = parseMultipartFormDataHeaders(input, position)
-
- let { name, filename, contentType, encoding } = result
-
- // 5.6. Advance position by 2. (This skips past the empty line that marks
- // the end of the headers.)
- position.position += 2
-
- // 5.7. Let body be the empty byte sequence.
- let body
-
- // 5.8. Body loop: While position is not past the end of input:
- // TODO: the steps here are completely wrong
- {
- const boundaryIndex = input.indexOf(boundary.subarray(2), position.position)
-
- if (boundaryIndex === -1) {
- throw parsingError('expected boundary after body')
- }
-
- body = input.subarray(position.position, boundaryIndex - 4)
-
- position.position += body.length
-
- // Note: position must be advanced by the body's length before being
- // decoded, otherwise the parsing will fail.
- if (encoding === 'base64') {
- body = Buffer.from(body.toString(), 'base64')
- }
- }
-
- // 5.9. If position does not point to a sequence of bytes starting with
- // 0x0D 0x0A (CR LF), return failure. Otherwise, advance position by 2.
- if (input[position.position] !== 0x0d || input[position.position + 1] !== 0x0a) {
- throw parsingError('expected CRLF')
- } else {
- position.position += 2
- }
-
- // 5.10. If filename is not null:
- let value
-
- if (filename !== null) {
- // 5.10.1. If contentType is null, set contentType to "text/plain".
- contentType ??= 'text/plain'
-
- // 5.10.2. If contentType is not an ASCII string, set contentType to the empty string.
-
- // Note: `buffer.isAscii` can be used at zero-cost, but converting a string to a buffer is a high overhead.
- // Content-Type is a relatively small string, so it is faster to use `String#charCodeAt`.
- if (!isAsciiString(contentType)) {
- contentType = ''
- }
-
- // 5.10.3. Let value be a new File object with name filename, type contentType, and body body.
- value = new File([body], filename, { type: contentType })
- } else {
- // 5.11. Otherwise:
-
- // 5.11.1. Let value be the UTF-8 decoding without BOM of body.
- value = utf8DecodeBytes(Buffer.from(body))
- }
-
- // 5.12. Assert: name is a scalar value string and value is either a scalar value string or a File object.
- assert(webidl.is.USVString(name))
- assert((typeof value === 'string' && webidl.is.USVString(value)) || webidl.is.File(value))
-
- // 5.13. Create an entry with name and value, and append it to entry list.
- entryList.push(makeEntry(name, value, filename))
- }
-}
-
-/**
- * Parses content-disposition attributes (e.g., name="value" or filename*=utf-8''encoded)
- * @param {Buffer} input
- * @param {{ position: number }} position
- * @returns {{ name: string, value: string }}
- */
-function parseContentDispositionAttribute (input, position) {
- // Skip leading semicolon and whitespace
- if (input[position.position] === 0x3b /* ; */) {
- position.position++
- }
-
- // Skip whitespace
- collectASequenceOfBytes(
- (char) => char === 0x20 || char === 0x09,
- input,
- position
- )
-
- // Collect attribute name (token characters)
- const attributeName = collectASequenceOfBytes(
- (char) => isToken(char) && char !== 0x3d && char !== 0x2a, // not = or *
- input,
- position
- )
-
- if (attributeName.length === 0) {
- return null
- }
-
- const attrNameStr = attributeName.toString('ascii').toLowerCase()
-
- // Check for extended notation (attribute*)
- const isExtended = input[position.position] === 0x2a /* * */
- if (isExtended) {
- position.position++ // skip *
- }
-
- // Expect = sign
- if (input[position.position] !== 0x3d /* = */) {
- return null
- }
- position.position++ // skip =
-
- // Skip whitespace
- collectASequenceOfBytes(
- (char) => char === 0x20 || char === 0x09,
- input,
- position
- )
-
- let value
-
- if (isExtended) {
- // Extended attribute format: charset'language'encoded-value
- const headerValue = collectASequenceOfBytes(
- (char) => char !== 0x20 && char !== 0x0d && char !== 0x0a && char !== 0x3b, // not space, CRLF, or ;
- input,
- position
- )
-
- // Check for utf-8'' prefix (case insensitive)
- if (
- (headerValue[0] !== 0x75 && headerValue[0] !== 0x55) || // u or U
- (headerValue[1] !== 0x74 && headerValue[1] !== 0x54) || // t or T
- (headerValue[2] !== 0x66 && headerValue[2] !== 0x46) || // f or F
- headerValue[3] !== 0x2d || // -
- headerValue[4] !== 0x38 // 8
- ) {
- throw parsingError('unknown encoding, expected utf-8\'\'')
- }
-
- // Skip utf-8'' and decode the rest
- value = decodeURIComponent(decoder.decode(headerValue.subarray(7)))
- } else if (input[position.position] === 0x22 /* " */) {
- // Quoted string
- position.position++ // skip opening quote
-
- const quotedValue = collectASequenceOfBytes(
- (char) => char !== 0x0a && char !== 0x0d && char !== 0x22, // not LF, CR, or "
- input,
- position
- )
-
- if (input[position.position] !== 0x22) {
- throw parsingError('Closing quote not found')
- }
- position.position++ // skip closing quote
-
- value = decoder.decode(quotedValue)
- .replace(/%0A/ig, '\n')
- .replace(/%0D/ig, '\r')
- .replace(/%22/g, '"')
- } else {
- // Token value (no quotes)
- const tokenValue = collectASequenceOfBytes(
- (char) => isToken(char) && char !== 0x3b, // not ;
- input,
- position
- )
-
- value = decoder.decode(tokenValue)
- }
-
- return { name: attrNameStr, value }
-}
-
-/**
- * @see https://andreubotella.github.io/multipart-form-data/#parse-multipart-form-data-headers
- * @param {Buffer} input
- * @param {{ position: number }} position
- */
-function parseMultipartFormDataHeaders (input, position) {
- // 1. Let name, filename and contentType be null.
- let name = null
- let filename = null
- let contentType = null
- let encoding = null
-
- // 2. While true:
- while (true) {
- // 2.1. If position points to a sequence of bytes starting with 0x0D 0x0A (CR LF):
- if (input[position.position] === 0x0d && input[position.position + 1] === 0x0a) {
- // 2.1.1. If name is null, return failure.
- if (name === null) {
- throw parsingError('header name is null')
- }
-
- // 2.1.2. Return name, filename and contentType.
- return { name, filename, contentType, encoding }
- }
-
- // 2.2. Let header name be the result of collecting a sequence of bytes that are
- // not 0x0A (LF), 0x0D (CR) or 0x3A (:), given position.
- let headerName = collectASequenceOfBytes(
- (char) => char !== 0x0a && char !== 0x0d && char !== 0x3a,
- input,
- position
- )
-
- // 2.3. Remove any HTTP tab or space bytes from the start or end of header name.
- headerName = removeChars(headerName, true, true, (char) => char === 0x9 || char === 0x20)
-
- // 2.4. If header name does not match the field-name token production, return failure.
- if (!HTTP_TOKEN_CODEPOINTS.test(headerName.toString())) {
- throw parsingError('header name does not match the field-name token production')
- }
-
- // 2.5. If the byte at position is not 0x3A (:), return failure.
- if (input[position.position] !== 0x3a) {
- throw parsingError('expected :')
- }
-
- // 2.6. Advance position by 1.
- position.position++
-
- // 2.7. Collect a sequence of bytes that are HTTP tab or space bytes given position.
- // (Do nothing with those bytes.)
- collectASequenceOfBytes(
- (char) => char === 0x20 || char === 0x09,
- input,
- position
- )
-
- // 2.8. Byte-lowercase header name and switch on the result:
- switch (bufferToLowerCasedHeaderName(headerName)) {
- case 'content-disposition': {
- name = filename = null
-
- // Collect the disposition type (should be "form-data")
- const dispositionType = collectASequenceOfBytes(
- (char) => isToken(char),
- input,
- position
- )
-
- if (dispositionType.toString('ascii').toLowerCase() !== 'form-data') {
- throw parsingError('expected form-data for content-disposition header')
- }
-
- // Parse attributes recursively until CRLF
- while (
- position.position < input.length &&
- input[position.position] !== 0x0d &&
- input[position.position + 1] !== 0x0a
- ) {
- const attribute = parseContentDispositionAttribute(input, position)
-
- if (!attribute) {
- break
- }
-
- if (attribute.name === 'name') {
- name = attribute.value
- } else if (attribute.name === 'filename') {
- filename = attribute.value
- }
- }
-
- if (name === null) {
- throw parsingError('name attribute is required in content-disposition header')
- }
-
- break
- }
- case 'content-type': {
- // 1. Let header value be the result of collecting a sequence of bytes that are
- // not 0x0A (LF) or 0x0D (CR), given position.
- let headerValue = collectASequenceOfBytes(
- (char) => char !== 0x0a && char !== 0x0d,
- input,
- position
- )
-
- // 2. Remove any HTTP tab or space bytes from the end of header value.
- headerValue = removeChars(headerValue, false, true, (char) => char === 0x9 || char === 0x20)
-
- // 3. Set contentType to the isomorphic decoding of header value.
- contentType = isomorphicDecode(headerValue)
-
- break
- }
- case 'content-transfer-encoding': {
- let headerValue = collectASequenceOfBytes(
- (char) => char !== 0x0a && char !== 0x0d,
- input,
- position
- )
-
- headerValue = removeChars(headerValue, false, true, (char) => char === 0x9 || char === 0x20)
-
- encoding = isomorphicDecode(headerValue)
-
- break
- }
- default: {
- // Collect a sequence of bytes that are not 0x0A (LF) or 0x0D (CR), given position.
- // (Do nothing with those bytes.)
- collectASequenceOfBytes(
- (char) => char !== 0x0a && char !== 0x0d,
- input,
- position
- )
- }
- }
-
- // 2.9. If position does not point to a sequence of bytes starting with 0x0D 0x0A
- // (CR LF), return failure. Otherwise, advance position by 2 (past the newline).
- if (input[position.position] !== 0x0d && input[position.position + 1] !== 0x0a) {
- throw parsingError('expected CRLF')
- } else {
- position.position += 2
- }
- }
-}
-
-/**
- * @param {(char: number) => boolean} condition
- * @param {Buffer} input
- * @param {{ position: number }} position
- */
-function collectASequenceOfBytes (condition, input, position) {
- let start = position.position
-
- while (start < input.length && condition(input[start])) {
- ++start
- }
-
- return input.subarray(position.position, (position.position = start))
-}
-
-/**
- * @param {Buffer} buf
- * @param {boolean} leading
- * @param {boolean} trailing
- * @param {(charCode: number) => boolean} predicate
- * @returns {Buffer}
- */
-function removeChars (buf, leading, trailing, predicate) {
- let lead = 0
- let trail = buf.length - 1
-
- if (leading) {
- while (lead < buf.length && predicate(buf[lead])) lead++
- }
-
- if (trailing) {
- while (trail > 0 && predicate(buf[trail])) trail--
- }
-
- return lead === 0 && trail === buf.length - 1 ? buf : buf.subarray(lead, trail + 1)
-}
-
-/**
- * Checks if {@param buffer} starts with {@param start}
- * @param {Buffer} buffer
- * @param {Buffer} start
- * @param {{ position: number }} position
- */
-function bufferStartsWith (buffer, start, position) {
- if (buffer.length < start.length) {
- return false
- }
-
- for (let i = 0; i < start.length; i++) {
- if (start[i] !== buffer[position.position + i]) {
- return false
- }
- }
-
- return true
-}
-
-function parsingError (cause) {
- return new TypeError('Failed to parse body as FormData.', { cause: new TypeError(cause) })
-}
-
-/**
- * CTL = <any US-ASCII control character
- * (octets 0 - 31) and DEL (127)>
- * @param {number} char
- */
-function isCTL (char) {
- return char <= 0x1f || char === 0x7f
-}
-
-/**
- * tspecials := "(" / ")" / "<" / ">" / "@" /
- * "," / ";" / ":" / "\" / <">
- * "/" / "[" / "]" / "?" / "="
- * ; Must be in quoted-string,
- * ; to use within parameter values
- * @param {number} char
- */
-function isTSpecial (char) {
- return (
- char === 0x28 || // (
- char === 0x29 || // )
- char === 0x3c || // <
- char === 0x3e || // >
- char === 0x40 || // @
- char === 0x2c || // ,
- char === 0x3b || // ;
- char === 0x3a || // :
- char === 0x5c || // \
- char === 0x22 || // "
- char === 0x2f || // /
- char === 0x5b || // [
- char === 0x5d || // ]
- char === 0x3f || // ?
- char === 0x3d // +
- )
-}
-
-/**
- * token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
- * or tspecials>
- * @param {number} char
- */
-function isToken (char) {
- return (
- char <= 0x7f && // ascii
- char !== 0x20 && // space
- char !== 0x09 &&
- !isCTL(char) &&
- !isTSpecial(char)
- )
-}
-
-module.exports = {
- multipartFormDataParser,
- validateBoundary
-}
diff --git a/vanilla/node_modules/undici/lib/web/fetch/formdata.js b/vanilla/node_modules/undici/lib/web/fetch/formdata.js
deleted file mode 100644
index c21fb06..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/formdata.js
+++ /dev/null
@@ -1,259 +0,0 @@
-'use strict'
-
-const { iteratorMixin } = require('./util')
-const { kEnumerableProperty } = require('../../core/util')
-const { webidl } = require('../webidl')
-const nodeUtil = require('node:util')
-
-// https://xhr.spec.whatwg.org/#formdata
-class FormData {
- #state = []
-
- constructor (form = undefined) {
- webidl.util.markAsUncloneable(this)
-
- if (form !== undefined) {
- throw webidl.errors.conversionFailed({
- prefix: 'FormData constructor',
- argument: 'Argument 1',
- types: ['undefined']
- })
- }
- }
-
- append (name, value, filename = undefined) {
- webidl.brandCheck(this, FormData)
-
- const prefix = 'FormData.append'
- webidl.argumentLengthCheck(arguments, 2, prefix)
-
- name = webidl.converters.USVString(name)
-
- if (arguments.length === 3 || webidl.is.Blob(value)) {
- value = webidl.converters.Blob(value, prefix, 'value')
-
- if (filename !== undefined) {
- filename = webidl.converters.USVString(filename)
- }
- } else {
- value = webidl.converters.USVString(value)
- }
-
- // 1. Let value be value if given; otherwise blobValue.
-
- // 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 this’s entry list.
- this.#state.push(entry)
- }
-
- delete (name) {
- webidl.brandCheck(this, FormData)
-
- const prefix = 'FormData.delete'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- name = webidl.converters.USVString(name)
-
- // The delete(name) method steps are to remove all entries whose name
- // is name from this’s entry list.
- this.#state = this.#state.filter(entry => entry.name !== name)
- }
-
- get (name) {
- webidl.brandCheck(this, FormData)
-
- const prefix = 'FormData.get'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- name = webidl.converters.USVString(name)
-
- // 1. If there is no entry whose name is name in this’s entry list,
- // then return null.
- const idx = this.#state.findIndex((entry) => entry.name === name)
- if (idx === -1) {
- return null
- }
-
- // 2. Return the value of the first entry whose name is name from
- // this’s entry list.
- return this.#state[idx].value
- }
-
- getAll (name) {
- webidl.brandCheck(this, FormData)
-
- const prefix = 'FormData.getAll'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- name = webidl.converters.USVString(name)
-
- // 1. If there is no entry whose name is name in this’s entry list,
- // then return the empty list.
- // 2. Return the values of all entries whose name is name, in order,
- // from this’s entry list.
- return this.#state
- .filter((entry) => entry.name === name)
- .map((entry) => entry.value)
- }
-
- has (name) {
- webidl.brandCheck(this, FormData)
-
- const prefix = 'FormData.has'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- name = webidl.converters.USVString(name)
-
- // The has(name) method steps are to return true if there is an entry
- // whose name is name in this’s entry list; otherwise false.
- return this.#state.findIndex((entry) => entry.name === name) !== -1
- }
-
- set (name, value, filename = undefined) {
- webidl.brandCheck(this, FormData)
-
- const prefix = 'FormData.set'
- webidl.argumentLengthCheck(arguments, 2, prefix)
-
- name = webidl.converters.USVString(name)
-
- if (arguments.length === 3 || webidl.is.Blob(value)) {
- value = webidl.converters.Blob(value, prefix, 'value')
-
- if (filename !== undefined) {
- filename = webidl.converters.USVString(filename)
- }
- } else {
- value = webidl.converters.USVString(value)
- }
-
- // The set(name, value) and set(name, blobValue, filename) method steps
- // are:
-
- // 1. Let value be value if given; otherwise blobValue.
-
- // 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 this’s entry list whose name is name, then
- // replace the first such entry with entry and remove the others.
- const idx = this.#state.findIndex((entry) => entry.name === name)
- if (idx !== -1) {
- this.#state = [
- ...this.#state.slice(0, idx),
- entry,
- ...this.#state.slice(idx + 1).filter((entry) => entry.name !== name)
- ]
- } else {
- // 4. Otherwise, append entry to this’s entry list.
- this.#state.push(entry)
- }
- }
-
- [nodeUtil.inspect.custom] (depth, options) {
- const state = this.#state.reduce((a, b) => {
- if (a[b.name]) {
- if (Array.isArray(a[b.name])) {
- a[b.name].push(b.value)
- } else {
- a[b.name] = [a[b.name], b.value]
- }
- } else {
- a[b.name] = b.value
- }
-
- return a
- }, { __proto__: null })
-
- options.depth ??= depth
- options.colors ??= true
-
- const output = nodeUtil.formatWithOptions(options, state)
-
- // remove [Object null prototype]
- return `FormData ${output.slice(output.indexOf(']') + 2)}`
- }
-
- /**
- * @param {FormData} formData
- */
- static getFormDataState (formData) {
- return formData.#state
- }
-
- /**
- * @param {FormData} formData
- * @param {any[]} newState
- */
- static setFormDataState (formData, newState) {
- formData.#state = newState
- }
-}
-
-const { getFormDataState, setFormDataState } = FormData
-Reflect.deleteProperty(FormData, 'getFormDataState')
-Reflect.deleteProperty(FormData, 'setFormDataState')
-
-iteratorMixin('FormData', FormData, getFormDataState, 'name', 'value')
-
-Object.defineProperties(FormData.prototype, {
- append: kEnumerableProperty,
- delete: kEnumerableProperty,
- get: kEnumerableProperty,
- getAll: kEnumerableProperty,
- has: kEnumerableProperty,
- set: kEnumerableProperty,
- [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.
- // Note: This operation was done by the webidl converter USVString.
-
- // 2. If value is a string, then set value to the result of converting
- // value into a scalar value string.
- if (typeof value === 'string') {
- // Note: This operation was done by the webidl converter USVString.
- } 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 (!webidl.is.File(value)) {
- value = new File([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 = new File([value], filename, options)
- }
- }
-
- // 4. Return an entry whose name is name and whose value is value.
- return { name, value }
-}
-
-webidl.is.FormData = webidl.util.MakeTypeAssertion(FormData)
-
-module.exports = { FormData, makeEntry, setFormDataState }
diff --git a/vanilla/node_modules/undici/lib/web/fetch/global.js b/vanilla/node_modules/undici/lib/web/fetch/global.js
deleted file mode 100644
index 1df6f12..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/global.js
+++ /dev/null
@@ -1,40 +0,0 @@
-'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
-}
diff --git a/vanilla/node_modules/undici/lib/web/fetch/headers.js b/vanilla/node_modules/undici/lib/web/fetch/headers.js
deleted file mode 100644
index 024d198..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/headers.js
+++ /dev/null
@@ -1,719 +0,0 @@
-// https://github.com/Ethan-Arrowood/undici-fetch
-
-'use strict'
-
-const { kConstruct } = require('../../core/symbols')
-const { kEnumerableProperty } = require('../../core/util')
-const {
- iteratorMixin,
- isValidHeaderName,
- isValidHeaderValue
-} = require('./util')
-const { webidl } = require('../webidl')
-const assert = require('node:assert')
-const util = require('node:util')
-
-/**
- * @param {number} code
- * @returns {code is (0x0a | 0x0d | 0x09 | 0x20)}
- */
-function isHTTPWhiteSpaceCharCode (code) {
- return code === 0x0a || code === 0x0d || code === 0x09 || code === 0x20
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#concept-header-value-normalize
- * @param {string} potentialValue
- * @returns {string}
- */
-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)
-}
-
-/**
- * @param {Headers} headers
- * @param {Array|Object} object
- */
-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 (header’s first item, header’s 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
- * @param {Headers} headers
- * @param {string} name
- * @param {string} value
- */
-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 headers’s guard is "immutable", then throw a TypeError.
- // 4. Otherwise, if headers’s guard is "request" and name is a
- // forbidden header name, return.
- // 5. Otherwise, if headers’s guard is "request-no-cors":
- // TODO
- // Note: undici does not implement forbidden header names
- if (getHeadersGuard(headers) === 'immutable') {
- throw new TypeError('immutable')
- }
-
- // 6. Otherwise, if headers’s guard is "response" and name is a
- // forbidden response-header name, return.
-
- // 7. Append (name, value) to headers’s header list.
- return getHeadersList(headers).append(name, value, false)
-
- // 8. If headers’s guard is "request-no-cors", then remove
- // privileged no-CORS request headers from headers
-}
-
-// https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
-/**
- * @param {Headers} target
- */
-function headersListSortAndCombine (target) {
- const headersList = getHeadersList(target)
-
- if (!headersList) {
- return []
- }
-
- if (headersList.sortedMap) {
- return headersList.sortedMap
- }
-
- // 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 = headersList.toSortedArray()
-
- const cookies = headersList.cookies
-
- // fast-path
- if (cookies === null || cookies.length === 1) {
- // Note: The non-null assertion of value has already been done by `HeadersList#toSortedArray`
- return (headersList.sortedMap = names)
- }
-
- // 3. For each name of names:
- for (let i = 0; i < names.length; ++i) {
- const { 0: name, 1: 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.
- // Note: This operation was done by `HeadersList#toSortedArray`.
-
- // 3. Append (name, value) to headers.
- headers.push([name, value])
- }
- }
-
- // 4. Return headers.
- return (headersList.sortedMap = headers)
-}
-
-function compareHeaderName (a, b) {
- return a[0] < b[0] ? -1 : 1
-}
-
-class HeadersList {
- /** @type {[string, string][]|null} */
- cookies = null
-
- sortedMap
- headersMap
-
- constructor (init) {
- if (init instanceof HeadersList) {
- this.headersMap = new Map(init.headersMap)
- this.sortedMap = init.sortedMap
- this.cookies = init.cookies === null ? null : [...init.cookies]
- } else {
- this.headersMap = new Map(init)
- this.sortedMap = null
- }
- }
-
- /**
- * @see https://fetch.spec.whatwg.org/#header-list-contains
- * @param {string} name
- * @param {boolean} isLowerCase
- */
- contains (name, isLowerCase) {
- // A header list list contains a header name name if list
- // contains a header whose name is a byte-case-insensitive
- // match for name.
-
- return this.headersMap.has(isLowerCase ? name : name.toLowerCase())
- }
-
- clear () {
- this.headersMap.clear()
- this.sortedMap = null
- this.cookies = null
- }
-
- /**
- * @see https://fetch.spec.whatwg.org/#concept-header-list-append
- * @param {string} name
- * @param {string} value
- * @param {boolean} isLowerCase
- */
- append (name, value, isLowerCase) {
- this.sortedMap = null
-
- // 1. If list contains name, then set name to the first such
- // header’s name.
- const lowercaseName = isLowerCase ? name : name.toLowerCase()
- const exists = this.headersMap.get(lowercaseName)
-
- // 2. Append (name, value) to list.
- if (exists) {
- const delimiter = lowercaseName === 'cookie' ? '; ' : ', '
- this.headersMap.set(lowercaseName, {
- name: exists.name,
- value: `${exists.value}${delimiter}${value}`
- })
- } else {
- this.headersMap.set(lowercaseName, { name, value })
- }
-
- if (lowercaseName === 'set-cookie') {
- (this.cookies ??= []).push(value)
- }
- }
-
- /**
- * @see https://fetch.spec.whatwg.org/#concept-header-list-set
- * @param {string} name
- * @param {string} value
- * @param {boolean} isLowerCase
- */
- set (name, value, isLowerCase) {
- this.sortedMap = null
- const lowercaseName = isLowerCase ? name : 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.headersMap.set(lowercaseName, { name, value })
- }
-
- /**
- * @see https://fetch.spec.whatwg.org/#concept-header-list-delete
- * @param {string} name
- * @param {boolean} isLowerCase
- */
- delete (name, isLowerCase) {
- this.sortedMap = null
- if (!isLowerCase) name = name.toLowerCase()
-
- if (name === 'set-cookie') {
- this.cookies = null
- }
-
- this.headersMap.delete(name)
- }
-
- /**
- * @see https://fetch.spec.whatwg.org/#concept-header-list-get
- * @param {string} name
- * @param {boolean} isLowerCase
- * @returns {string | null}
- */
- get (name, isLowerCase) {
- // 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 this.headersMap.get(isLowerCase ? name : name.toLowerCase())?.value ?? null
- }
-
- * [Symbol.iterator] () {
- // use the lowercased name
- for (const { 0: name, 1: { value } } of this.headersMap) {
- yield [name, value]
- }
- }
-
- get entries () {
- const headers = {}
-
- if (this.headersMap.size !== 0) {
- for (const { name, value } of this.headersMap.values()) {
- headers[name] = value
- }
- }
-
- return headers
- }
-
- rawValues () {
- return this.headersMap.values()
- }
-
- get entriesList () {
- const headers = []
-
- if (this.headersMap.size !== 0) {
- for (const { 0: lowerName, 1: { name, value } } of this.headersMap) {
- if (lowerName === 'set-cookie') {
- for (const cookie of this.cookies) {
- headers.push([name, cookie])
- }
- } else {
- headers.push([name, value])
- }
- }
- }
-
- return headers
- }
-
- // https://fetch.spec.whatwg.org/#convert-header-names-to-a-sorted-lowercase-set
- toSortedArray () {
- const size = this.headersMap.size
- const array = new Array(size)
- // In most cases, you will use the fast-path.
- // fast-path: Use binary insertion sort for small arrays.
- if (size <= 32) {
- if (size === 0) {
- // If empty, it is an empty array. To avoid the first index assignment.
- return array
- }
- // Improve performance by unrolling loop and avoiding double-loop.
- // Double-loop-less version of the binary insertion sort.
- const iterator = this.headersMap[Symbol.iterator]()
- const firstValue = iterator.next().value
- // set [name, value] to first index.
- array[0] = [firstValue[0], firstValue[1].value]
- // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
- // 3.2.2. Assert: value is non-null.
- assert(firstValue[1].value !== null)
- for (
- let i = 1, j = 0, right = 0, left = 0, pivot = 0, x, value;
- i < size;
- ++i
- ) {
- // get next value
- value = iterator.next().value
- // set [name, value] to current index.
- x = array[i] = [value[0], value[1].value]
- // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
- // 3.2.2. Assert: value is non-null.
- assert(x[1] !== null)
- left = 0
- right = i
- // binary search
- while (left < right) {
- // middle index
- pivot = left + ((right - left) >> 1)
- // compare header name
- if (array[pivot][0] <= x[0]) {
- left = pivot + 1
- } else {
- right = pivot
- }
- }
- if (i !== pivot) {
- j = i
- while (j > left) {
- array[j] = array[--j]
- }
- array[left] = x
- }
- }
- /* c8 ignore next 4 */
- if (!iterator.next().done) {
- // This is for debugging and will never be called.
- throw new TypeError('Unreachable')
- }
- return array
- } else {
- // This case would be a rare occurrence.
- // slow-path: fallback
- let i = 0
- for (const { 0: name, 1: { value } } of this.headersMap) {
- array[i++] = [name, value]
- // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
- // 3.2.2. Assert: value is non-null.
- assert(value !== null)
- }
- return array.sort(compareHeaderName)
- }
- }
-}
-
-// https://fetch.spec.whatwg.org/#headers-class
-class Headers {
- #guard
- /**
- * @type {HeadersList}
- */
- #headersList
-
- /**
- * @param {HeadersInit|Symbol} [init]
- * @returns
- */
- constructor (init = undefined) {
- webidl.util.markAsUncloneable(this)
-
- if (init === kConstruct) {
- return
- }
-
- this.#headersList = new HeadersList()
-
- // The new Headers(init) constructor steps are:
-
- // 1. Set this’s guard to "none".
- this.#guard = 'none'
-
- // 2. If init is given, then fill this with init.
- if (init !== undefined) {
- init = webidl.converters.HeadersInit(init, 'Headers constructor', 'init')
- fill(this, init)
- }
- }
-
- // https://fetch.spec.whatwg.org/#dom-headers-append
- append (name, value) {
- webidl.brandCheck(this, Headers)
-
- webidl.argumentLengthCheck(arguments, 2, 'Headers.append')
-
- const prefix = 'Headers.append'
- name = webidl.converters.ByteString(name, prefix, 'name')
- value = webidl.converters.ByteString(value, prefix, 'value')
-
- return appendHeader(this, name, value)
- }
-
- // https://fetch.spec.whatwg.org/#dom-headers-delete
- delete (name) {
- webidl.brandCheck(this, Headers)
-
- webidl.argumentLengthCheck(arguments, 1, 'Headers.delete')
-
- const prefix = 'Headers.delete'
- name = webidl.converters.ByteString(name, prefix, '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 this’s guard is "immutable", then throw a TypeError.
- // 3. Otherwise, if this’s guard is "request" and name is a
- // forbidden header name, return.
- // 4. Otherwise, if this’s 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 this’s guard is "response" and name is
- // a forbidden response-header name, return.
- // Note: undici does not implement forbidden header names
- if (this.#guard === 'immutable') {
- throw new TypeError('immutable')
- }
-
- // 6. If this’s header list does not contain name, then
- // return.
- if (!this.#headersList.contains(name, false)) {
- return
- }
-
- // 7. Delete name from this’s header list.
- // 8. If this’s guard is "request-no-cors", then remove
- // privileged no-CORS request headers from this.
- this.#headersList.delete(name, false)
- }
-
- // https://fetch.spec.whatwg.org/#dom-headers-get
- get (name) {
- webidl.brandCheck(this, Headers)
-
- webidl.argumentLengthCheck(arguments, 1, 'Headers.get')
-
- const prefix = 'Headers.get'
- name = webidl.converters.ByteString(name, prefix, 'name')
-
- // 1. If name is not a header name, then throw a TypeError.
- if (!isValidHeaderName(name)) {
- throw webidl.errors.invalidArgument({
- prefix,
- value: name,
- type: 'header name'
- })
- }
-
- // 2. Return the result of getting name from this’s header
- // list.
- return this.#headersList.get(name, false)
- }
-
- // https://fetch.spec.whatwg.org/#dom-headers-has
- has (name) {
- webidl.brandCheck(this, Headers)
-
- webidl.argumentLengthCheck(arguments, 1, 'Headers.has')
-
- const prefix = 'Headers.has'
- name = webidl.converters.ByteString(name, prefix, 'name')
-
- // 1. If name is not a header name, then throw a TypeError.
- if (!isValidHeaderName(name)) {
- throw webidl.errors.invalidArgument({
- prefix,
- value: name,
- type: 'header name'
- })
- }
-
- // 2. Return true if this’s header list contains name;
- // otherwise false.
- return this.#headersList.contains(name, false)
- }
-
- // https://fetch.spec.whatwg.org/#dom-headers-set
- set (name, value) {
- webidl.brandCheck(this, Headers)
-
- webidl.argumentLengthCheck(arguments, 2, 'Headers.set')
-
- const prefix = 'Headers.set'
- name = webidl.converters.ByteString(name, prefix, 'name')
- value = webidl.converters.ByteString(value, prefix, '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,
- value: name,
- type: 'header name'
- })
- } else if (!isValidHeaderValue(value)) {
- throw webidl.errors.invalidArgument({
- prefix,
- value,
- type: 'header value'
- })
- }
-
- // 3. If this’s guard is "immutable", then throw a TypeError.
- // 4. Otherwise, if this’s guard is "request" and name is a
- // forbidden header name, return.
- // 5. Otherwise, if this’s guard is "request-no-cors" and
- // name/value is not a no-CORS-safelisted request-header,
- // return.
- // 6. Otherwise, if this’s guard is "response" and name is a
- // forbidden response-header name, return.
- // Note: undici does not implement forbidden header names
- if (this.#guard === 'immutable') {
- throw new TypeError('immutable')
- }
-
- // 7. Set (name, value) in this’s header list.
- // 8. If this’s guard is "request-no-cors", then remove
- // privileged no-CORS request headers from this
- this.#headersList.set(name, value, false)
- }
-
- // https://fetch.spec.whatwg.org/#dom-headers-getsetcookie
- getSetCookie () {
- webidl.brandCheck(this, Headers)
-
- // 1. If this’s header list does not contain `Set-Cookie`, then return « ».
- // 2. Return the values of all headers in this’s header list whose name is
- // a byte-case-insensitive match for `Set-Cookie`, in order.
-
- const list = this.#headersList.cookies
-
- if (list) {
- return [...list]
- }
-
- return []
- }
-
- [util.inspect.custom] (depth, options) {
- options.depth ??= depth
-
- return `Headers ${util.formatWithOptions(options, this.#headersList.entries)}`
- }
-
- static getHeadersGuard (o) {
- return o.#guard
- }
-
- static setHeadersGuard (o, guard) {
- o.#guard = guard
- }
-
- /**
- * @param {Headers} o
- */
- static getHeadersList (o) {
- return o.#headersList
- }
-
- /**
- * @param {Headers} target
- * @param {HeadersList} list
- */
- static setHeadersList (target, list) {
- target.#headersList = list
- }
-}
-
-const { getHeadersGuard, setHeadersGuard, getHeadersList, setHeadersList } = Headers
-Reflect.deleteProperty(Headers, 'getHeadersGuard')
-Reflect.deleteProperty(Headers, 'setHeadersGuard')
-Reflect.deleteProperty(Headers, 'getHeadersList')
-Reflect.deleteProperty(Headers, 'setHeadersList')
-
-iteratorMixin('Headers', Headers, headersListSortAndCombine, 0, 1)
-
-Object.defineProperties(Headers.prototype, {
- append: kEnumerableProperty,
- delete: kEnumerableProperty,
- get: kEnumerableProperty,
- has: kEnumerableProperty,
- set: kEnumerableProperty,
- getSetCookie: kEnumerableProperty,
- [Symbol.toStringTag]: {
- value: 'Headers',
- configurable: true
- },
- [util.inspect.custom]: {
- enumerable: false
- }
-})
-
-webidl.converters.HeadersInit = function (V, prefix, argument) {
- if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {
- const iterator = Reflect.get(V, Symbol.iterator)
-
- // A work-around to ensure we send the properly-cased Headers when V is a Headers object.
- // Read https://github.com/nodejs/undici/pull/3159#issuecomment-2075537226 before touching, please.
- if (!util.types.isProxy(V) && iterator === Headers.prototype.entries) { // Headers object
- try {
- return getHeadersList(V).entriesList
- } catch {
- // fall-through
- }
- }
-
- if (typeof iterator === 'function') {
- return webidl.converters['sequence<sequence<ByteString>>'](V, prefix, argument, iterator.bind(V))
- }
-
- return webidl.converters['record<ByteString, ByteString>'](V, prefix, argument)
- }
-
- throw webidl.errors.conversionFailed({
- prefix: 'Headers constructor',
- argument: 'Argument 1',
- types: ['sequence<sequence<ByteString>>', 'record<ByteString, ByteString>']
- })
-}
-
-module.exports = {
- fill,
- // for test.
- compareHeaderName,
- Headers,
- HeadersList,
- getHeadersGuard,
- setHeadersGuard,
- setHeadersList,
- getHeadersList
-}
diff --git a/vanilla/node_modules/undici/lib/web/fetch/index.js b/vanilla/node_modules/undici/lib/web/fetch/index.js
deleted file mode 100644
index f350035..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/index.js
+++ /dev/null
@@ -1,2372 +0,0 @@
-// https://github.com/Ethan-Arrowood/undici-fetch
-
-'use strict'
-
-const {
- makeNetworkError,
- makeAppropriateNetworkError,
- filterResponse,
- makeResponse,
- fromInnerResponse,
- getResponseState
-} = require('./response')
-const { HeadersList } = require('./headers')
-const { Request, cloneRequest, getRequestDispatcher, getRequestState } = require('./request')
-const zlib = require('node:zlib')
-const {
- makePolicyContainer,
- clonePolicyContainer,
- requestBadPort,
- TAOCheck,
- appendRequestOriginHeader,
- responseLocationURL,
- requestCurrentURL,
- setRequestReferrerPolicyOnRedirect,
- tryUpgradeRequestToAPotentiallyTrustworthyURL,
- createOpaqueTimingInfo,
- appendFetchMetadata,
- corsCheck,
- crossOriginResourcePolicyCheck,
- determineRequestsReferrer,
- coarsenedSharedCurrentTime,
- sameOrigin,
- isCancelled,
- isAborted,
- isErrorLike,
- fullyReadBody,
- readableStreamClose,
- urlIsLocal,
- urlIsHttpHttpsScheme,
- urlHasHttpsScheme,
- clampAndCoarsenConnectionTimingInfo,
- simpleRangeHeaderValue,
- buildContentRange,
- createInflate,
- extractMimeType,
- hasAuthenticationEntry,
- includesCredentials,
- isTraversableNavigable
-} = require('./util')
-const assert = require('node:assert')
-const { safelyExtractBody, extractBody } = require('./body')
-const {
- redirectStatusSet,
- nullBodyStatus,
- safeMethodsSet,
- requestBodyHeader,
- subresourceSet
-} = require('./constants')
-const EE = require('node:events')
-const { Readable, pipeline, finished, isErrored, isReadable } = require('node:stream')
-const { addAbortListener, bufferToLowerCasedHeaderName } = require('../../core/util')
-const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require('./data-url')
-const { getGlobalDispatcher } = require('../../global')
-const { webidl } = require('../webidl')
-const { STATUS_CODES } = require('node:http')
-const { bytesMatch } = require('../subresource-integrity/subresource-integrity')
-const { createDeferredPromise } = require('../../util/promise')
-const { isomorphicEncode } = require('../infra')
-const { runtimeFeatures } = require('../../util/runtime-features')
-
-// Node.js v23.8.0+ and v22.15.0+ supports Zstandard
-const hasZstd = runtimeFeatures.has('zstd')
-
-const GET_OR_HEAD = ['GET', 'HEAD']
-
-const defaultUserAgent = typeof __UNDICI_IS_NODE__ !== 'undefined' || typeof esbuildDetection !== 'undefined'
- ? 'node'
- : 'undici'
-
-/** @type {import('buffer').resolveObjectURL} */
-let resolveObjectURL
-
-class Fetch extends EE {
- constructor (dispatcher) {
- super()
-
- this.dispatcher = dispatcher
- this.connection = null
- this.dump = false
- this.state = 'ongoing'
- }
-
- terminate (reason) {
- if (this.state !== 'ongoing') {
- return
- }
-
- this.state = 'terminated'
- this.connection?.destroy(reason)
- this.emit('terminated', reason)
- }
-
- // https://fetch.spec.whatwg.org/#fetch-controller-abort
- abort (error) {
- if (this.state !== 'ongoing') {
- return
- }
-
- // 1. Set controller’s state to "aborted".
- this.state = 'aborted'
-
- // 2. Let fallbackError be an "AbortError" DOMException.
- // 3. Set error to fallbackError if it is not given.
- if (!error) {
- error = new DOMException('The operation was aborted.', 'AbortError')
- }
-
- // 4. Let serializedError be StructuredSerialize(error).
- // If that threw an exception, catch it, and let
- // serializedError be StructuredSerialize(fallbackError).
-
- // 5. Set controller’s serialized abort reason to serializedError.
- this.serializedAbortReason = error
-
- this.connection?.destroy(error)
- this.emit('terminated', error)
- }
-}
-
-function handleFetchDone (response) {
- finalizeAndReportTiming(response, 'fetch')
-}
-
-// https://fetch.spec.whatwg.org/#fetch-method
-function fetch (input, init = undefined) {
- webidl.argumentLengthCheck(arguments, 1, 'globalThis.fetch')
-
- // 1. Let p be a new promise.
- let p = createDeferredPromise()
-
- // 2. Let requestObject be the result of invoking the initial value of
- // Request as constructor with input and init as arguments. If this throws
- // an exception, reject p with it and return p.
- let requestObject
-
- try {
- requestObject = new Request(input, init)
- } catch (e) {
- p.reject(e)
- return p.promise
- }
-
- // 3. Let request be requestObject’s request.
- const request = getRequestState(requestObject)
-
- // 4. If requestObject’s signal’s aborted flag is set, then:
- if (requestObject.signal.aborted) {
- // 1. Abort the fetch() call with p, request, null, and
- // requestObject’s signal’s abort reason.
- abortFetch(p, request, null, requestObject.signal.reason, null)
-
- // 2. Return p.
- return p.promise
- }
-
- // 5. Let globalObject be request’s client’s global object.
- const globalObject = request.client.globalObject
-
- // 6. If globalObject is a ServiceWorkerGlobalScope object, then set
- // request’s service-workers mode to "none".
- if (globalObject?.constructor?.name === 'ServiceWorkerGlobalScope') {
- request.serviceWorkers = 'none'
- }
-
- // 7. Let responseObject be null.
- let responseObject = null
-
- // 8. Let relevantRealm be this’s relevant Realm.
-
- // 9. Let locallyAborted be false.
- let locallyAborted = false
-
- // 10. Let controller be null.
- let controller = null
-
- // 11. Add the following abort steps to requestObject’s signal:
- addAbortListener(
- requestObject.signal,
- () => {
- // 1. Set locallyAborted to true.
- locallyAborted = true
-
- // 2. Assert: controller is non-null.
- assert(controller != null)
-
- // 3. Abort controller with requestObject’s signal’s abort reason.
- controller.abort(requestObject.signal.reason)
-
- const realResponse = responseObject?.deref()
-
- // 4. Abort the fetch() call with p, request, responseObject,
- // and requestObject’s signal’s abort reason.
- abortFetch(p, request, realResponse, requestObject.signal.reason, controller.controller)
- }
- )
-
- // 12. Let handleFetchDone given response response be to finalize and
- // report timing with response, globalObject, and "fetch".
- // see function handleFetchDone
-
- // 13. Set controller to the result of calling fetch given request,
- // with processResponseEndOfBody set to handleFetchDone, and processResponse
- // given response being these substeps:
-
- const processResponse = (response) => {
- // 1. If locallyAborted is true, terminate these substeps.
- if (locallyAborted) {
- return
- }
-
- // 2. If response’s aborted flag is set, then:
- if (response.aborted) {
- // 1. Let deserializedError be the result of deserialize a serialized
- // abort reason given controller’s serialized abort reason and
- // relevantRealm.
-
- // 2. Abort the fetch() call with p, request, responseObject, and
- // deserializedError.
-
- abortFetch(p, request, responseObject, controller.serializedAbortReason, controller.controller)
- return
- }
-
- // 3. If response is a network error, then reject p with a TypeError
- // and terminate these substeps.
- if (response.type === 'error') {
- p.reject(new TypeError('fetch failed', { cause: response.error }))
- return
- }
-
- // 4. Set responseObject to the result of creating a Response object,
- // given response, "immutable", and relevantRealm.
- responseObject = new WeakRef(fromInnerResponse(response, 'immutable'))
-
- // 5. Resolve p with responseObject.
- p.resolve(responseObject.deref())
- p = null
- }
-
- controller = fetching({
- request,
- processResponseEndOfBody: handleFetchDone,
- processResponse,
- dispatcher: getRequestDispatcher(requestObject) // undici
- })
-
- // 14. Return p.
- return p.promise
-}
-
-// https://fetch.spec.whatwg.org/#finalize-and-report-timing
-function finalizeAndReportTiming (response, initiatorType = 'other') {
- // 1. If response is an aborted network error, then return.
- if (response.type === 'error' && response.aborted) {
- return
- }
-
- // 2. If response’s URL list is null or empty, then return.
- if (!response.urlList?.length) {
- return
- }
-
- // 3. Let originalURL be response’s URL list[0].
- const originalURL = response.urlList[0]
-
- // 4. Let timingInfo be response’s timing info.
- let timingInfo = response.timingInfo
-
- // 5. Let cacheState be response’s cache state.
- let cacheState = response.cacheState
-
- // 6. If originalURL’s scheme is not an HTTP(S) scheme, then return.
- if (!urlIsHttpHttpsScheme(originalURL)) {
- return
- }
-
- // 7. If timingInfo is null, then return.
- if (timingInfo === null) {
- return
- }
-
- // 8. If response’s timing allow passed flag is not set, then:
- if (!response.timingAllowPassed) {
- // 1. Set timingInfo to a the result of creating an opaque timing info for timingInfo.
- timingInfo = createOpaqueTimingInfo({
- startTime: timingInfo.startTime
- })
-
- // 2. Set cacheState to the empty string.
- cacheState = ''
- }
-
- // 9. Set timingInfo’s end time to the coarsened shared current time
- // given global’s relevant settings object’s cross-origin isolated
- // capability.
- // TODO: given global’s relevant settings object’s cross-origin isolated
- // capability?
- timingInfo.endTime = coarsenedSharedCurrentTime()
-
- // 10. Set response’s timing info to timingInfo.
- response.timingInfo = timingInfo
-
- // 11. Mark resource timing for timingInfo, originalURL, initiatorType,
- // global, and cacheState.
- markResourceTiming(
- timingInfo,
- originalURL.href,
- initiatorType,
- globalThis,
- cacheState,
- '', // bodyType
- response.status
- )
-}
-
-// https://w3c.github.io/resource-timing/#dfn-mark-resource-timing
-const markResourceTiming = performance.markResourceTiming
-
-// https://fetch.spec.whatwg.org/#abort-fetch
-function abortFetch (p, request, responseObject, error, controller /* undici-specific */) {
- // 1. Reject promise with error.
- if (p) {
- // We might have already resolved the promise at this stage
- p.reject(error)
- }
-
- // 2. If request’s body is not null and is readable, then cancel request’s
- // body with error.
- if (request.body?.stream != null && isReadable(request.body.stream)) {
- request.body.stream.cancel(error).catch((err) => {
- if (err.code === 'ERR_INVALID_STATE') {
- // Node bug?
- return
- }
- throw err
- })
- }
-
- // 3. If responseObject is null, then return.
- if (responseObject == null) {
- return
- }
-
- // 4. Let response be responseObject’s response.
- const response = getResponseState(responseObject)
-
- // 5. If response’s body is not null and is readable, then error response’s
- // body with error.
- if (response.body?.stream != null && isReadable(response.body.stream)) {
- controller.error(error)
- }
-}
-
-// https://fetch.spec.whatwg.org/#fetching
-function fetching ({
- request,
- processRequestBodyChunkLength,
- processRequestEndOfBody,
- processResponse,
- processResponseEndOfBody,
- processResponseConsumeBody,
- useParallelQueue = false,
- dispatcher = getGlobalDispatcher() // undici
-}) {
- // Ensure that the dispatcher is set accordingly
- assert(dispatcher)
-
- // 1. Let taskDestination be null.
- let taskDestination = null
-
- // 2. Let crossOriginIsolatedCapability be false.
- let crossOriginIsolatedCapability = false
-
- // 3. If request’s client is non-null, then:
- if (request.client != null) {
- // 1. Set taskDestination to request’s client’s global object.
- taskDestination = request.client.globalObject
-
- // 2. Set crossOriginIsolatedCapability to request’s client’s cross-origin
- // isolated capability.
- crossOriginIsolatedCapability =
- request.client.crossOriginIsolatedCapability
- }
-
- // 4. If useParallelQueue is true, then set taskDestination to the result of
- // starting a new parallel queue.
- // TODO
-
- // 5. Let timingInfo be a new fetch timing info whose start time and
- // post-redirect start time are the coarsened shared current time given
- // crossOriginIsolatedCapability.
- const currentTime = coarsenedSharedCurrentTime(crossOriginIsolatedCapability)
- const timingInfo = createOpaqueTimingInfo({
- startTime: currentTime
- })
-
- // 6. Let fetchParams be a new fetch params whose
- // request is request,
- // timing info is timingInfo,
- // process request body chunk length is processRequestBodyChunkLength,
- // process request end-of-body is processRequestEndOfBody,
- // process response is processResponse,
- // process response consume body is processResponseConsumeBody,
- // process response end-of-body is processResponseEndOfBody,
- // task destination is taskDestination,
- // and cross-origin isolated capability is crossOriginIsolatedCapability.
- const fetchParams = {
- controller: new Fetch(dispatcher),
- request,
- timingInfo,
- processRequestBodyChunkLength,
- processRequestEndOfBody,
- processResponse,
- processResponseConsumeBody,
- processResponseEndOfBody,
- taskDestination,
- crossOriginIsolatedCapability
- }
-
- // 7. If request’s body is a byte sequence, then set request’s body to
- // request’s body as a body.
- // NOTE: Since fetching is only called from fetch, body should already be
- // extracted.
- assert(!request.body || request.body.stream)
-
- // 8. If request’s window is "client", then set request’s window to request’s
- // client, if request’s client’s global object is a Window object; otherwise
- // "no-window".
- if (request.window === 'client') {
- // TODO: What if request.client is null?
- request.window =
- request.client?.globalObject?.constructor?.name === 'Window'
- ? request.client
- : 'no-window'
- }
-
- // 9. If request’s origin is "client", then set request’s origin to request’s
- // client’s origin.
- if (request.origin === 'client') {
- request.origin = request.client.origin
- }
-
- // 10. If all of the following conditions are true:
- // TODO
-
- // 11. If request’s policy container is "client", then:
- if (request.policyContainer === 'client') {
- // 1. If request’s client is non-null, then set request’s policy
- // container to a clone of request’s client’s policy container. [HTML]
- if (request.client != null) {
- request.policyContainer = clonePolicyContainer(
- request.client.policyContainer
- )
- } else {
- // 2. Otherwise, set request’s policy container to a new policy
- // container.
- request.policyContainer = makePolicyContainer()
- }
- }
-
- // 12. If request’s header list does not contain `Accept`, then:
- if (!request.headersList.contains('accept', true)) {
- // 1. Let value be `*/*`.
- const value = '*/*'
-
- // 2. A user agent should set value to the first matching statement, if
- // any, switching on request’s destination:
- // "document"
- // "frame"
- // "iframe"
- // `text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8`
- // "image"
- // `image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5`
- // "style"
- // `text/css,*/*;q=0.1`
- // TODO
-
- // 3. Append `Accept`/value to request’s header list.
- request.headersList.append('accept', value, true)
- }
-
- // 13. If request’s header list does not contain `Accept-Language`, then
- // user agents should append `Accept-Language`/an appropriate value to
- // request’s header list.
- if (!request.headersList.contains('accept-language', true)) {
- request.headersList.append('accept-language', '*', true)
- }
-
- // 14. If request’s priority is null, then use request’s initiator and
- // destination appropriately in setting request’s priority to a
- // user-agent-defined object.
- if (request.priority === null) {
- // TODO
- }
-
- // 15. If request is a subresource request, then:
- if (subresourceSet.has(request.destination)) {
- // TODO
- }
-
- // 16. Run main fetch given fetchParams.
- mainFetch(fetchParams, false)
-
- // 17. Return fetchParam's controller
- return fetchParams.controller
-}
-
-// https://fetch.spec.whatwg.org/#concept-main-fetch
-async function mainFetch (fetchParams, recursive) {
- try {
- // 1. Let request be fetchParams’s request.
- const request = fetchParams.request
-
- // 2. Let response be null.
- let response = null
-
- // 3. If request’s local-URLs-only flag is set and request’s current URL is
- // not local, then set response to a network error.
- if (request.localURLsOnly && !urlIsLocal(requestCurrentURL(request))) {
- response = makeNetworkError('local URLs only')
- }
-
- // 4. Run report Content Security Policy violations for request.
- // TODO
-
- // 5. Upgrade request to a potentially trustworthy URL, if appropriate.
- tryUpgradeRequestToAPotentiallyTrustworthyURL(request)
-
- // 6. If should request be blocked due to a bad port, should fetching request
- // be blocked as mixed content, or should request be blocked by Content
- // Security Policy returns blocked, then set response to a network error.
- if (requestBadPort(request) === 'blocked') {
- response = makeNetworkError('bad port')
- }
- // TODO: should fetching request be blocked as mixed content?
- // TODO: should request be blocked by Content Security Policy?
-
- // 7. If request’s referrer policy is the empty string, then set request’s
- // referrer policy to request’s policy container’s referrer policy.
- if (request.referrerPolicy === '') {
- request.referrerPolicy = request.policyContainer.referrerPolicy
- }
-
- // 8. If request’s referrer is not "no-referrer", then set request’s
- // referrer to the result of invoking determine request’s referrer.
- if (request.referrer !== 'no-referrer') {
- request.referrer = determineRequestsReferrer(request)
- }
-
- // 9. Set request’s current URL’s scheme to "https" if all of the following
- // conditions are true:
- // - request’s current URL’s scheme is "http"
- // - request’s current URL’s host is a domain
- // - Matching request’s current URL’s host per Known HSTS Host Domain Name
- // Matching results in either a superdomain match with an asserted
- // includeSubDomains directive or a congruent match (with or without an
- // asserted includeSubDomains directive). [HSTS]
- // TODO
-
- // 10. If recursive is false, then run the remaining steps in parallel.
- // TODO
-
- // 11. If response is null, then set response to the result of running
- // the steps corresponding to the first matching statement:
- if (response === null) {
- const currentURL = requestCurrentURL(request)
- if (
- // - request’s current URL’s origin is same origin with request’s origin,
- // and request’s response tainting is "basic"
- (sameOrigin(currentURL, request.url) && request.responseTainting === 'basic') ||
- // request’s current URL’s scheme is "data"
- (currentURL.protocol === 'data:') ||
- // - request’s mode is "navigate" or "websocket"
- (request.mode === 'navigate' || request.mode === 'websocket')
- ) {
- // 1. Set request’s response tainting to "basic".
- request.responseTainting = 'basic'
-
- // 2. Return the result of running scheme fetch given fetchParams.
- response = await schemeFetch(fetchParams)
-
- // request’s mode is "same-origin"
- } else if (request.mode === 'same-origin') {
- // 1. Return a network error.
- response = makeNetworkError('request mode cannot be "same-origin"')
-
- // request’s mode is "no-cors"
- } else if (request.mode === 'no-cors') {
- // 1. If request’s redirect mode is not "follow", then return a network
- // error.
- if (request.redirect !== 'follow') {
- response = makeNetworkError(
- 'redirect mode cannot be "follow" for "no-cors" request'
- )
- } else {
- // 2. Set request’s response tainting to "opaque".
- request.responseTainting = 'opaque'
-
- // 3. Return the result of running scheme fetch given fetchParams.
- response = await schemeFetch(fetchParams)
- }
- // request’s current URL’s scheme is not an HTTP(S) scheme
- } else if (!urlIsHttpHttpsScheme(requestCurrentURL(request))) {
- // Return a network error.
- response = makeNetworkError('URL scheme must be a HTTP(S) scheme')
-
- // - request’s use-CORS-preflight flag is set
- // - request’s unsafe-request flag is set and either request’s method is
- // not a CORS-safelisted method or CORS-unsafe request-header names with
- // request’s header list is not empty
- // 1. Set request’s response tainting to "cors".
- // 2. Let corsWithPreflightResponse be the result of running HTTP fetch
- // given fetchParams and true.
- // 3. If corsWithPreflightResponse is a network error, then clear cache
- // entries using request.
- // 4. Return corsWithPreflightResponse.
- // TODO
-
- // Otherwise
- } else {
- // 1. Set request’s response tainting to "cors".
- request.responseTainting = 'cors'
-
- // 2. Return the result of running HTTP fetch given fetchParams.
- response = await httpFetch(fetchParams)
- }
- }
-
- // 12. If recursive is true, then return response.
- if (recursive) {
- return response
- }
-
- // 13. If response is not a network error and response is not a filtered
- // response, then:
- if (response.status !== 0 && !response.internalResponse) {
- // If request’s response tainting is "cors", then:
- if (request.responseTainting === 'cors') {
- // 1. Let headerNames be the result of extracting header list values
- // given `Access-Control-Expose-Headers` and response’s header list.
- // TODO
- // 2. If request’s credentials mode is not "include" and headerNames
- // contains `*`, then set response’s CORS-exposed header-name list to
- // all unique header names in response’s header list.
- // TODO
- // 3. Otherwise, if headerNames is not null or failure, then set
- // response’s CORS-exposed header-name list to headerNames.
- // TODO
- }
-
- // Set response to the following filtered response with response as its
- // internal response, depending on request’s response tainting:
- if (request.responseTainting === 'basic') {
- response = filterResponse(response, 'basic')
- } else if (request.responseTainting === 'cors') {
- response = filterResponse(response, 'cors')
- } else if (request.responseTainting === 'opaque') {
- response = filterResponse(response, 'opaque')
- } else {
- assert(false)
- }
- }
-
- // 14. Let internalResponse be response, if response is a network error,
- // and response’s internal response otherwise.
- let internalResponse =
- response.status === 0 ? response : response.internalResponse
-
- // 15. If internalResponse’s URL list is empty, then set it to a clone of
- // request’s URL list.
- if (internalResponse.urlList.length === 0) {
- internalResponse.urlList.push(...request.urlList)
- }
-
- // 16. If request’s timing allow failed flag is unset, then set
- // internalResponse’s timing allow passed flag.
- if (!request.timingAllowFailed) {
- response.timingAllowPassed = true
- }
-
- // 17. If response is not a network error and any of the following returns
- // blocked
- // - should internalResponse to request be blocked as mixed content
- // - should internalResponse to request be blocked by Content Security Policy
- // - should internalResponse to request be blocked due to its MIME type
- // - should internalResponse to request be blocked due to nosniff
- // TODO
-
- // 18. If response’s type is "opaque", internalResponse’s status is 206,
- // internalResponse’s range-requested flag is set, and request’s header
- // list does not contain `Range`, then set response and internalResponse
- // to a network error.
- if (
- response.type === 'opaque' &&
- internalResponse.status === 206 &&
- internalResponse.rangeRequested &&
- !request.headers.contains('range', true)
- ) {
- response = internalResponse = makeNetworkError()
- }
-
- // 19. If response is not a network error and either request’s method is
- // `HEAD` or `CONNECT`, or internalResponse’s status is a null body status,
- // set internalResponse’s body to null and disregard any enqueuing toward
- // it (if any).
- if (
- response.status !== 0 &&
- (request.method === 'HEAD' ||
- request.method === 'CONNECT' ||
- nullBodyStatus.includes(internalResponse.status))
- ) {
- internalResponse.body = null
- fetchParams.controller.dump = true
- }
-
- // 20. If request’s integrity metadata is not the empty string, then:
- if (request.integrity) {
- // 1. Let processBodyError be this step: run fetch finale given fetchParams
- // and a network error.
- const processBodyError = (reason) =>
- fetchFinale(fetchParams, makeNetworkError(reason))
-
- // 2. If request’s response tainting is "opaque", or response’s body is null,
- // then run processBodyError and abort these steps.
- if (request.responseTainting === 'opaque' || response.body == null) {
- processBodyError(response.error)
- return
- }
-
- // 3. Let processBody given bytes be these steps:
- const processBody = (bytes) => {
- // 1. If bytes do not match request’s integrity metadata,
- // then run processBodyError and abort these steps. [SRI]
- if (!bytesMatch(bytes, request.integrity)) {
- processBodyError('integrity mismatch')
- return
- }
-
- // 2. Set response’s body to bytes as a body.
- response.body = safelyExtractBody(bytes)[0]
-
- // 3. Run fetch finale given fetchParams and response.
- fetchFinale(fetchParams, response)
- }
-
- // 4. Fully read response’s body given processBody and processBodyError.
- fullyReadBody(response.body, processBody, processBodyError)
- } else {
- // 21. Otherwise, run fetch finale given fetchParams and response.
- fetchFinale(fetchParams, response)
- }
- } catch (err) {
- fetchParams.controller.terminate(err)
- }
-}
-
-// https://fetch.spec.whatwg.org/#concept-scheme-fetch
-// given a fetch params fetchParams
-function schemeFetch (fetchParams) {
- // Note: since the connection is destroyed on redirect, which sets fetchParams to a
- // cancelled state, we do not want this condition to trigger *unless* there have been
- // no redirects. See https://github.com/nodejs/undici/issues/1776
- // 1. If fetchParams is canceled, then return the appropriate network error for fetchParams.
- if (isCancelled(fetchParams) && fetchParams.request.redirectCount === 0) {
- return Promise.resolve(makeAppropriateNetworkError(fetchParams))
- }
-
- // 2. Let request be fetchParams’s request.
- const { request } = fetchParams
-
- const { protocol: scheme } = requestCurrentURL(request)
-
- // 3. Switch on request’s current URL’s scheme and run the associated steps:
- switch (scheme) {
- case 'about:': {
- // If request’s current URL’s path is the string "blank", then return a new response
- // whose status message is `OK`, header list is « (`Content-Type`, `text/html;charset=utf-8`) »,
- // and body is the empty byte sequence as a body.
-
- // Otherwise, return a network error.
- return Promise.resolve(makeNetworkError('about scheme is not supported'))
- }
- case 'blob:': {
- if (!resolveObjectURL) {
- resolveObjectURL = require('node:buffer').resolveObjectURL
- }
-
- // 1. Let blobURLEntry be request’s current URL’s blob URL entry.
- const blobURLEntry = requestCurrentURL(request)
-
- // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L52-L56
- // Buffer.resolveObjectURL does not ignore URL queries.
- if (blobURLEntry.search.length !== 0) {
- return Promise.resolve(makeNetworkError('NetworkError when attempting to fetch resource.'))
- }
-
- const blob = resolveObjectURL(blobURLEntry.toString())
-
- // 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s
- // object is not a Blob object, then return a network error.
- if (request.method !== 'GET' || !webidl.is.Blob(blob)) {
- return Promise.resolve(makeNetworkError('invalid method'))
- }
-
- // 3. Let blob be blobURLEntry’s object.
- // Note: done above
-
- // 4. Let response be a new response.
- const response = makeResponse()
-
- // 5. Let fullLength be blob’s size.
- const fullLength = blob.size
-
- // 6. Let serializedFullLength be fullLength, serialized and isomorphic encoded.
- const serializedFullLength = isomorphicEncode(`${fullLength}`)
-
- // 7. Let type be blob’s type.
- const type = blob.type
-
- // 8. If request’s header list does not contain `Range`:
- // 9. Otherwise:
- if (!request.headersList.contains('range', true)) {
- // 1. Let bodyWithType be the result of safely extracting blob.
- // Note: in the FileAPI a blob "object" is a Blob *or* a MediaSource.
- // In node, this can only ever be a Blob. Therefore we can safely
- // use extractBody directly.
- const bodyWithType = extractBody(blob)
-
- // 2. Set response’s status message to `OK`.
- response.statusText = 'OK'
-
- // 3. Set response’s body to bodyWithType’s body.
- response.body = bodyWithType[0]
-
- // 4. Set response’s header list to « (`Content-Length`, serializedFullLength), (`Content-Type`, type) ».
- response.headersList.set('content-length', serializedFullLength, true)
- response.headersList.set('content-type', type, true)
- } else {
- // 1. Set response’s range-requested flag.
- response.rangeRequested = true
-
- // 2. Let rangeHeader be the result of getting `Range` from request’s header list.
- const rangeHeader = request.headersList.get('range', true)
-
- // 3. Let rangeValue be the result of parsing a single range header value given rangeHeader and true.
- const rangeValue = simpleRangeHeaderValue(rangeHeader, true)
-
- // 4. If rangeValue is failure, then return a network error.
- if (rangeValue === 'failure') {
- return Promise.resolve(makeNetworkError('failed to fetch the data URL'))
- }
-
- // 5. Let (rangeStart, rangeEnd) be rangeValue.
- let { rangeStartValue: rangeStart, rangeEndValue: rangeEnd } = rangeValue
-
- // 6. If rangeStart is null:
- // 7. Otherwise:
- if (rangeStart === null) {
- // 1. Set rangeStart to fullLength − rangeEnd.
- rangeStart = fullLength - rangeEnd
-
- // 2. Set rangeEnd to rangeStart + rangeEnd − 1.
- rangeEnd = rangeStart + rangeEnd - 1
- } else {
- // 1. If rangeStart is greater than or equal to fullLength, then return a network error.
- if (rangeStart >= fullLength) {
- return Promise.resolve(makeNetworkError('Range start is greater than the blob\'s size.'))
- }
-
- // 2. If rangeEnd is null or rangeEnd is greater than or equal to fullLength, then set
- // rangeEnd to fullLength − 1.
- if (rangeEnd === null || rangeEnd >= fullLength) {
- rangeEnd = fullLength - 1
- }
- }
-
- // 8. Let slicedBlob be the result of invoking slice blob given blob, rangeStart,
- // rangeEnd + 1, and type.
- const slicedBlob = blob.slice(rangeStart, rangeEnd + 1, type)
-
- // 9. Let slicedBodyWithType be the result of safely extracting slicedBlob.
- // Note: same reason as mentioned above as to why we use extractBody
- const slicedBodyWithType = extractBody(slicedBlob)
-
- // 10. Set response’s body to slicedBodyWithType’s body.
- response.body = slicedBodyWithType[0]
-
- // 11. Let serializedSlicedLength be slicedBlob’s size, serialized and isomorphic encoded.
- const serializedSlicedLength = isomorphicEncode(`${slicedBlob.size}`)
-
- // 12. Let contentRange be the result of invoking build a content range given rangeStart,
- // rangeEnd, and fullLength.
- const contentRange = buildContentRange(rangeStart, rangeEnd, fullLength)
-
- // 13. Set response’s status to 206.
- response.status = 206
-
- // 14. Set response’s status message to `Partial Content`.
- response.statusText = 'Partial Content'
-
- // 15. Set response’s header list to « (`Content-Length`, serializedSlicedLength),
- // (`Content-Type`, type), (`Content-Range`, contentRange) ».
- response.headersList.set('content-length', serializedSlicedLength, true)
- response.headersList.set('content-type', type, true)
- response.headersList.set('content-range', contentRange, true)
- }
-
- // 10. Return response.
- return Promise.resolve(response)
- }
- case 'data:': {
- // 1. Let dataURLStruct be the result of running the
- // data: URL processor on request’s current URL.
- const currentURL = requestCurrentURL(request)
- const dataURLStruct = dataURLProcessor(currentURL)
-
- // 2. If dataURLStruct is failure, then return a
- // network error.
- if (dataURLStruct === 'failure') {
- return Promise.resolve(makeNetworkError('failed to fetch the data URL'))
- }
-
- // 3. Let mimeType be dataURLStruct’s MIME type, serialized.
- const mimeType = serializeAMimeType(dataURLStruct.mimeType)
-
- // 4. Return a response whose status message is `OK`,
- // header list is « (`Content-Type`, mimeType) »,
- // and body is dataURLStruct’s body as a body.
- return Promise.resolve(makeResponse({
- statusText: 'OK',
- headersList: [
- ['content-type', { name: 'Content-Type', value: mimeType }]
- ],
- body: safelyExtractBody(dataURLStruct.body)[0]
- }))
- }
- case 'file:': {
- // For now, unfortunate as it is, file URLs are left as an exercise for the reader.
- // When in doubt, return a network error.
- return Promise.resolve(makeNetworkError('not implemented... yet...'))
- }
- case 'http:':
- case 'https:': {
- // Return the result of running HTTP fetch given fetchParams.
-
- return httpFetch(fetchParams)
- .catch((err) => makeNetworkError(err))
- }
- default: {
- return Promise.resolve(makeNetworkError('unknown scheme'))
- }
- }
-}
-
-// https://fetch.spec.whatwg.org/#finalize-response
-function finalizeResponse (fetchParams, response) {
- // 1. Set fetchParams’s request’s done flag.
- fetchParams.request.done = true
-
- // 2, If fetchParams’s process response done is not null, then queue a fetch
- // task to run fetchParams’s process response done given response, with
- // fetchParams’s task destination.
- if (fetchParams.processResponseDone != null) {
- queueMicrotask(() => fetchParams.processResponseDone(response))
- }
-}
-
-// https://fetch.spec.whatwg.org/#fetch-finale
-function fetchFinale (fetchParams, response) {
- // 1. Let timingInfo be fetchParams’s timing info.
- let timingInfo = fetchParams.timingInfo
-
- // 2. If response is not a network error and fetchParams’s request’s client is a secure context,
- // then set timingInfo’s server-timing headers to the result of getting, decoding, and splitting
- // `Server-Timing` from response’s internal response’s header list.
- // TODO
-
- // 3. Let processResponseEndOfBody be the following steps:
- const processResponseEndOfBody = () => {
- // 1. Let unsafeEndTime be the unsafe shared current time.
- const unsafeEndTime = Date.now() // ?
-
- // 2. If fetchParams’s request’s destination is "document", then set fetchParams’s controller’s
- // full timing info to fetchParams’s timing info.
- if (fetchParams.request.destination === 'document') {
- fetchParams.controller.fullTimingInfo = timingInfo
- }
-
- // 3. Set fetchParams’s controller’s report timing steps to the following steps given a global object global:
- fetchParams.controller.reportTimingSteps = () => {
- // 1. If fetchParams’s request’s URL’s scheme is not an HTTP(S) scheme, then return.
- if (!urlIsHttpHttpsScheme(fetchParams.request.url)) {
- return
- }
-
- // 2. Set timingInfo’s end time to the relative high resolution time given unsafeEndTime and global.
- timingInfo.endTime = unsafeEndTime
-
- // 3. Let cacheState be response’s cache state.
- let cacheState = response.cacheState
-
- // 4. Let bodyInfo be response’s body info.
- const bodyInfo = response.bodyInfo
-
- // 5. If response’s timing allow passed flag is not set, then set timingInfo to the result of creating an
- // opaque timing info for timingInfo and set cacheState to the empty string.
- if (!response.timingAllowPassed) {
- timingInfo = createOpaqueTimingInfo(timingInfo)
-
- cacheState = ''
- }
-
- // 6. Let responseStatus be 0.
- let responseStatus = 0
-
- // 7. If fetchParams’s request’s mode is not "navigate" or response’s has-cross-origin-redirects is false:
- if (fetchParams.request.mode !== 'navigator' || !response.hasCrossOriginRedirects) {
- // 1. Set responseStatus to response’s status.
- responseStatus = response.status
-
- // 2. Let mimeType be the result of extracting a MIME type from response’s header list.
- const mimeType = extractMimeType(response.headersList)
-
- // 3. If mimeType is not failure, then set bodyInfo’s content type to the result of minimizing a supported MIME type given mimeType.
- if (mimeType !== 'failure') {
- bodyInfo.contentType = minimizeSupportedMimeType(mimeType)
- }
- }
-
- // 8. If fetchParams’s request’s initiator type is non-null, then mark resource timing given timingInfo,
- // fetchParams’s request’s URL, fetchParams’s request’s initiator type, global, cacheState, bodyInfo,
- // and responseStatus.
- if (fetchParams.request.initiatorType != null) {
- markResourceTiming(timingInfo, fetchParams.request.url.href, fetchParams.request.initiatorType, globalThis, cacheState, bodyInfo, responseStatus)
- }
- }
-
- // 4. Let processResponseEndOfBodyTask be the following steps:
- const processResponseEndOfBodyTask = () => {
- // 1. Set fetchParams’s request’s done flag.
- fetchParams.request.done = true
-
- // 2. If fetchParams’s process response end-of-body is non-null, then run fetchParams’s process
- // response end-of-body given response.
- if (fetchParams.processResponseEndOfBody != null) {
- queueMicrotask(() => fetchParams.processResponseEndOfBody(response))
- }
-
- // 3. If fetchParams’s request’s initiator type is non-null and fetchParams’s request’s client’s
- // global object is fetchParams’s task destination, then run fetchParams’s controller’s report
- // timing steps given fetchParams’s request’s client’s global object.
- if (fetchParams.request.initiatorType != null) {
- fetchParams.controller.reportTimingSteps()
- }
- }
-
- // 5. Queue a fetch task to run processResponseEndOfBodyTask with fetchParams’s task destination
- queueMicrotask(() => processResponseEndOfBodyTask())
- }
-
- // 4. If fetchParams’s process response is non-null, then queue a fetch task to run fetchParams’s
- // process response given response, with fetchParams’s task destination.
- if (fetchParams.processResponse != null) {
- queueMicrotask(() => {
- fetchParams.processResponse(response)
- fetchParams.processResponse = null
- })
- }
-
- // 5. Let internalResponse be response, if response is a network error; otherwise response’s internal response.
- const internalResponse = response.type === 'error' ? response : (response.internalResponse ?? response)
-
- // 6. If internalResponse’s body is null, then run processResponseEndOfBody.
- // 7. Otherwise:
- if (internalResponse.body == null) {
- processResponseEndOfBody()
- } else {
- // mcollina: all the following steps of the specs are skipped.
- // The internal transform stream is not needed.
- // See https://github.com/nodejs/undici/pull/3093#issuecomment-2050198541
-
- // 1. Let transformStream be a new TransformStream.
- // 2. Let identityTransformAlgorithm be an algorithm which, given chunk, enqueues chunk in transformStream.
- // 3. Set up transformStream with transformAlgorithm set to identityTransformAlgorithm and flushAlgorithm
- // set to processResponseEndOfBody.
- // 4. Set internalResponse’s body’s stream to the result of internalResponse’s body’s stream piped through transformStream.
-
- finished(internalResponse.body.stream, () => {
- processResponseEndOfBody()
- })
- }
-}
-
-// https://fetch.spec.whatwg.org/#http-fetch
-async function httpFetch (fetchParams) {
- // 1. Let request be fetchParams’s request.
- const request = fetchParams.request
-
- // 2. Let response be null.
- let response = null
-
- // 3. Let actualResponse be null.
- let actualResponse = null
-
- // 4. Let timingInfo be fetchParams’s timing info.
- const timingInfo = fetchParams.timingInfo
-
- // 5. If request’s service-workers mode is "all", then:
- if (request.serviceWorkers === 'all') {
- // TODO
- }
-
- // 6. If response is null, then:
- if (response === null) {
- // 1. If makeCORSPreflight is true and one of these conditions is true:
- // TODO
-
- // 2. If request’s redirect mode is "follow", then set request’s
- // service-workers mode to "none".
- if (request.redirect === 'follow') {
- request.serviceWorkers = 'none'
- }
-
- // 3. Set response and actualResponse to the result of running
- // HTTP-network-or-cache fetch given fetchParams.
- actualResponse = response = await httpNetworkOrCacheFetch(fetchParams)
-
- // 4. If request’s response tainting is "cors" and a CORS check
- // for request and response returns failure, then return a network error.
- if (
- request.responseTainting === 'cors' &&
- corsCheck(request, response) === 'failure'
- ) {
- return makeNetworkError('cors failure')
- }
-
- // 5. If the TAO check for request and response returns failure, then set
- // request’s timing allow failed flag.
- if (TAOCheck(request, response) === 'failure') {
- request.timingAllowFailed = true
- }
- }
-
- // 7. If either request’s response tainting or response’s type
- // is "opaque", and the cross-origin resource policy check with
- // request’s origin, request’s client, request’s destination,
- // and actualResponse returns blocked, then return a network error.
- if (
- (request.responseTainting === 'opaque' || response.type === 'opaque') &&
- crossOriginResourcePolicyCheck(
- request.origin,
- request.client,
- request.destination,
- actualResponse
- ) === 'blocked'
- ) {
- return makeNetworkError('blocked')
- }
-
- // 8. If actualResponse’s status is a redirect status, then:
- if (redirectStatusSet.has(actualResponse.status)) {
- // 1. If actualResponse’s status is not 303, request’s body is not null,
- // and the connection uses HTTP/2, then user agents may, and are even
- // encouraged to, transmit an RST_STREAM frame.
- // See, https://github.com/whatwg/fetch/issues/1288
- if (request.redirect !== 'manual') {
- fetchParams.controller.connection.destroy(undefined, false)
- }
-
- // 2. Switch on request’s redirect mode:
- if (request.redirect === 'error') {
- // Set response to a network error.
- response = makeNetworkError('unexpected redirect')
- } else if (request.redirect === 'manual') {
- // Set response to an opaque-redirect filtered response whose internal
- // response is actualResponse.
- // NOTE(spec): On the web this would return an `opaqueredirect` response,
- // but that doesn't make sense server side.
- // See https://github.com/nodejs/undici/issues/1193.
- response = actualResponse
- } else if (request.redirect === 'follow') {
- // Set response to the result of running HTTP-redirect fetch given
- // fetchParams and response.
- response = await httpRedirectFetch(fetchParams, response)
- } else {
- assert(false)
- }
- }
-
- // 9. Set response’s timing info to timingInfo.
- response.timingInfo = timingInfo
-
- // 10. Return response.
- return response
-}
-
-// https://fetch.spec.whatwg.org/#http-redirect-fetch
-function httpRedirectFetch (fetchParams, response) {
- // 1. Let request be fetchParams’s request.
- const request = fetchParams.request
-
- // 2. Let actualResponse be response, if response is not a filtered response,
- // and response’s internal response otherwise.
- const actualResponse = response.internalResponse
- ? response.internalResponse
- : response
-
- // 3. Let locationURL be actualResponse’s location URL given request’s current
- // URL’s fragment.
- let locationURL
-
- try {
- locationURL = responseLocationURL(
- actualResponse,
- requestCurrentURL(request).hash
- )
-
- // 4. If locationURL is null, then return response.
- if (locationURL == null) {
- return response
- }
- } catch (err) {
- // 5. If locationURL is failure, then return a network error.
- return Promise.resolve(makeNetworkError(err))
- }
-
- // 6. If locationURL’s scheme is not an HTTP(S) scheme, then return a network
- // error.
- if (!urlIsHttpHttpsScheme(locationURL)) {
- return Promise.resolve(makeNetworkError('URL scheme must be a HTTP(S) scheme'))
- }
-
- // 7. If request’s redirect count is 20, then return a network error.
- if (request.redirectCount === 20) {
- return Promise.resolve(makeNetworkError('redirect count exceeded'))
- }
-
- // 8. Increase request’s redirect count by 1.
- request.redirectCount += 1
-
- // 9. If request’s mode is "cors", locationURL includes credentials, and
- // request’s origin is not same origin with locationURL’s origin, then return
- // a network error.
- if (
- request.mode === 'cors' &&
- (locationURL.username || locationURL.password) &&
- !sameOrigin(request, locationURL)
- ) {
- return Promise.resolve(makeNetworkError('cross origin not allowed for request mode "cors"'))
- }
-
- // 10. If request’s response tainting is "cors" and locationURL includes
- // credentials, then return a network error.
- if (
- request.responseTainting === 'cors' &&
- (locationURL.username || locationURL.password)
- ) {
- return Promise.resolve(makeNetworkError(
- 'URL cannot contain credentials for request mode "cors"'
- ))
- }
-
- // 11. If actualResponse’s status is not 303, request’s body is non-null,
- // and request’s body’s source is null, then return a network error.
- if (
- actualResponse.status !== 303 &&
- request.body != null &&
- request.body.source == null
- ) {
- return Promise.resolve(makeNetworkError())
- }
-
- // 12. If one of the following is true
- // - actualResponse’s status is 301 or 302 and request’s method is `POST`
- // - actualResponse’s status is 303 and request’s method is not `GET` or `HEAD`
- if (
- ([301, 302].includes(actualResponse.status) && request.method === 'POST') ||
- (actualResponse.status === 303 &&
- !GET_OR_HEAD.includes(request.method))
- ) {
- // then:
- // 1. Set request’s method to `GET` and request’s body to null.
- request.method = 'GET'
- request.body = null
-
- // 2. For each headerName of request-body-header name, delete headerName from
- // request’s header list.
- for (const headerName of requestBodyHeader) {
- request.headersList.delete(headerName)
- }
- }
-
- // 13. If request’s current URL’s origin is not same origin with locationURL’s
- // origin, then for each headerName of CORS non-wildcard request-header name,
- // delete headerName from request’s header list.
- if (!sameOrigin(requestCurrentURL(request), locationURL)) {
- // https://fetch.spec.whatwg.org/#cors-non-wildcard-request-header-name
- request.headersList.delete('authorization', true)
-
- // https://fetch.spec.whatwg.org/#authentication-entries
- request.headersList.delete('proxy-authorization', true)
-
- // "Cookie" and "Host" are forbidden request-headers, which undici doesn't implement.
- request.headersList.delete('cookie', true)
- request.headersList.delete('host', true)
- }
-
- // 14. If request's body is non-null, then set request's body to the first return
- // value of safely extracting request's body's source.
- if (request.body != null) {
- assert(request.body.source != null)
- request.body = safelyExtractBody(request.body.source)[0]
- }
-
- // 15. Let timingInfo be fetchParams’s timing info.
- const timingInfo = fetchParams.timingInfo
-
- // 16. Set timingInfo’s redirect end time and post-redirect start time to the
- // coarsened shared current time given fetchParams’s cross-origin isolated
- // capability.
- timingInfo.redirectEndTime = timingInfo.postRedirectStartTime =
- coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)
-
- // 17. If timingInfo’s redirect start time is 0, then set timingInfo’s
- // redirect start time to timingInfo’s start time.
- if (timingInfo.redirectStartTime === 0) {
- timingInfo.redirectStartTime = timingInfo.startTime
- }
-
- // 18. Append locationURL to request’s URL list.
- request.urlList.push(locationURL)
-
- // 19. Invoke set request’s referrer policy on redirect on request and
- // actualResponse.
- setRequestReferrerPolicyOnRedirect(request, actualResponse)
-
- // 20. Return the result of running main fetch given fetchParams and true.
- return mainFetch(fetchParams, true)
-}
-
-// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
-async function httpNetworkOrCacheFetch (
- fetchParams,
- isAuthenticationFetch = false,
- isNewConnectionFetch = false
-) {
- // 1. Let request be fetchParams’s request.
- const request = fetchParams.request
-
- // 2. Let httpFetchParams be null.
- let httpFetchParams = null
-
- // 3. Let httpRequest be null.
- let httpRequest = null
-
- // 4. Let response be null.
- let response = null
-
- // 5. Let storedResponse be null.
- // TODO: cache
-
- // 6. Let httpCache be null.
- const httpCache = null
-
- // 7. Let the revalidatingFlag be unset.
- const revalidatingFlag = false
-
- // 8. Run these steps, but abort when the ongoing fetch is terminated:
-
- // 1. If request’s window is "no-window" and request’s redirect mode is
- // "error", then set httpFetchParams to fetchParams and httpRequest to
- // request.
- if (request.window === 'no-window' && request.redirect === 'error') {
- httpFetchParams = fetchParams
- httpRequest = request
- } else {
- // Otherwise:
-
- // 1. Set httpRequest to a clone of request.
- httpRequest = cloneRequest(request)
-
- // 2. Set httpFetchParams to a copy of fetchParams.
- httpFetchParams = { ...fetchParams }
-
- // 3. Set httpFetchParams’s request to httpRequest.
- httpFetchParams.request = httpRequest
- }
-
- // 3. Let includeCredentials be true if one of
- const includeCredentials =
- request.credentials === 'include' ||
- (request.credentials === 'same-origin' &&
- request.responseTainting === 'basic')
-
- // 4. Let contentLength be httpRequest’s body’s length, if httpRequest’s
- // body is non-null; otherwise null.
- const contentLength = httpRequest.body ? httpRequest.body.length : null
-
- // 5. Let contentLengthHeaderValue be null.
- let contentLengthHeaderValue = null
-
- // 6. If httpRequest’s body is null and httpRequest’s method is `POST` or
- // `PUT`, then set contentLengthHeaderValue to `0`.
- if (
- httpRequest.body == null &&
- ['POST', 'PUT'].includes(httpRequest.method)
- ) {
- contentLengthHeaderValue = '0'
- }
-
- // 7. If contentLength is non-null, then set contentLengthHeaderValue to
- // contentLength, serialized and isomorphic encoded.
- if (contentLength != null) {
- contentLengthHeaderValue = isomorphicEncode(`${contentLength}`)
- }
-
- // 8. If contentLengthHeaderValue is non-null, then append
- // `Content-Length`/contentLengthHeaderValue to httpRequest’s header
- // list.
- if (contentLengthHeaderValue != null) {
- httpRequest.headersList.append('content-length', contentLengthHeaderValue, true)
- }
-
- // 9. If contentLengthHeaderValue is non-null, then append (`Content-Length`,
- // contentLengthHeaderValue) to httpRequest’s header list.
-
- // 10. If contentLength is non-null and httpRequest’s keepalive is true,
- // then:
- if (contentLength != null && httpRequest.keepalive) {
- // NOTE: keepalive is a noop outside of browser context.
- }
-
- // 11. If httpRequest’s referrer is a URL, then append
- // `Referer`/httpRequest’s referrer, serialized and isomorphic encoded,
- // to httpRequest’s header list.
- if (webidl.is.URL(httpRequest.referrer)) {
- httpRequest.headersList.append('referer', isomorphicEncode(httpRequest.referrer.href), true)
- }
-
- // 12. Append a request `Origin` header for httpRequest.
- appendRequestOriginHeader(httpRequest)
-
- // 13. Append the Fetch metadata headers for httpRequest. [FETCH-METADATA]
- appendFetchMetadata(httpRequest)
-
- // 14. If httpRequest’s header list does not contain `User-Agent`, then
- // user agents should append `User-Agent`/default `User-Agent` value to
- // httpRequest’s header list.
- if (!httpRequest.headersList.contains('user-agent', true)) {
- httpRequest.headersList.append('user-agent', defaultUserAgent, true)
- }
-
- // 15. If httpRequest’s cache mode is "default" and httpRequest’s header
- // list contains `If-Modified-Since`, `If-None-Match`,
- // `If-Unmodified-Since`, `If-Match`, or `If-Range`, then set
- // httpRequest’s cache mode to "no-store".
- if (
- httpRequest.cache === 'default' &&
- (httpRequest.headersList.contains('if-modified-since', true) ||
- httpRequest.headersList.contains('if-none-match', true) ||
- httpRequest.headersList.contains('if-unmodified-since', true) ||
- httpRequest.headersList.contains('if-match', true) ||
- httpRequest.headersList.contains('if-range', true))
- ) {
- httpRequest.cache = 'no-store'
- }
-
- // 16. If httpRequest’s cache mode is "no-cache", httpRequest’s prevent
- // no-cache cache-control header modification flag is unset, and
- // httpRequest’s header list does not contain `Cache-Control`, then append
- // `Cache-Control`/`max-age=0` to httpRequest’s header list.
- if (
- httpRequest.cache === 'no-cache' &&
- !httpRequest.preventNoCacheCacheControlHeaderModification &&
- !httpRequest.headersList.contains('cache-control', true)
- ) {
- httpRequest.headersList.append('cache-control', 'max-age=0', true)
- }
-
- // 17. If httpRequest’s cache mode is "no-store" or "reload", then:
- if (httpRequest.cache === 'no-store' || httpRequest.cache === 'reload') {
- // 1. If httpRequest’s header list does not contain `Pragma`, then append
- // `Pragma`/`no-cache` to httpRequest’s header list.
- if (!httpRequest.headersList.contains('pragma', true)) {
- httpRequest.headersList.append('pragma', 'no-cache', true)
- }
-
- // 2. If httpRequest’s header list does not contain `Cache-Control`,
- // then append `Cache-Control`/`no-cache` to httpRequest’s header list.
- if (!httpRequest.headersList.contains('cache-control', true)) {
- httpRequest.headersList.append('cache-control', 'no-cache', true)
- }
- }
-
- // 18. If httpRequest’s header list contains `Range`, then append
- // `Accept-Encoding`/`identity` to httpRequest’s header list.
- if (httpRequest.headersList.contains('range', true)) {
- httpRequest.headersList.append('accept-encoding', 'identity', true)
- }
-
- // 19. Modify httpRequest’s header list per HTTP. Do not append a given
- // header if httpRequest’s header list contains that header’s name.
- // TODO: https://github.com/whatwg/fetch/issues/1285#issuecomment-896560129
- if (!httpRequest.headersList.contains('accept-encoding', true)) {
- if (urlHasHttpsScheme(requestCurrentURL(httpRequest))) {
- httpRequest.headersList.append('accept-encoding', 'br, gzip, deflate', true)
- } else {
- httpRequest.headersList.append('accept-encoding', 'gzip, deflate', true)
- }
- }
-
- httpRequest.headersList.delete('host', true)
-
- // 21. If includeCredentials is true, then:
- if (includeCredentials) {
- // 1. If the user agent is not configured to block cookies for httpRequest
- // (see section 7 of [COOKIES]), then:
- // TODO: credentials
-
- // 2. If httpRequest’s header list does not contain `Authorization`, then:
- if (!httpRequest.headersList.contains('authorization', true)) {
- // 1. Let authorizationValue be null.
- let authorizationValue = null
-
- // 2. If there’s an authentication entry for httpRequest and either
- // httpRequest’s use-URL-credentials flag is unset or httpRequest’s
- // current URL does not include credentials, then set
- // authorizationValue to authentication entry.
- if (hasAuthenticationEntry(httpRequest) && (
- httpRequest.useURLCredentials === undefined || !includesCredentials(requestCurrentURL(httpRequest))
- )) {
- // TODO
- } else if (includesCredentials(requestCurrentURL(httpRequest)) && isAuthenticationFetch) {
- // 3. Otherwise, if httpRequest’s current URL does include credentials
- // and isAuthenticationFetch is true, set authorizationValue to
- // httpRequest’s current URL, converted to an `Authorization` value
- const { username, password } = requestCurrentURL(httpRequest)
- authorizationValue = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`
- }
-
- // 4. If authorizationValue is non-null, then append (`Authorization`,
- // authorizationValue) to httpRequest’s header list.
- if (authorizationValue !== null) {
- httpRequest.headersList.append('Authorization', authorizationValue, false)
- }
- }
- }
-
- // 21. If there’s a proxy-authentication entry, use it as appropriate.
- // TODO: proxy-authentication
-
- // 22. Set httpCache to the result of determining the HTTP cache
- // partition, given httpRequest.
- // TODO: cache
-
- // 23. If httpCache is null, then set httpRequest’s cache mode to
- // "no-store".
- if (httpCache == null) {
- httpRequest.cache = 'no-store'
- }
-
- // 24. If httpRequest’s cache mode is neither "no-store" nor "reload",
- // then:
- if (httpRequest.cache !== 'no-store' && httpRequest.cache !== 'reload') {
- // TODO: cache
- }
-
- // 9. If aborted, then return the appropriate network error for fetchParams.
- // TODO
-
- // 10. If response is null, then:
- if (response == null) {
- // 1. If httpRequest’s cache mode is "only-if-cached", then return a
- // network error.
- if (httpRequest.cache === 'only-if-cached') {
- return makeNetworkError('only if cached')
- }
-
- // 2. Let forwardResponse be the result of running HTTP-network fetch
- // given httpFetchParams, includeCredentials, and isNewConnectionFetch.
- const forwardResponse = await httpNetworkFetch(
- httpFetchParams,
- includeCredentials,
- isNewConnectionFetch
- )
-
- // 3. If httpRequest’s method is unsafe and forwardResponse’s status is
- // in the range 200 to 399, inclusive, invalidate appropriate stored
- // responses in httpCache, as per the "Invalidation" chapter of HTTP
- // Caching, and set storedResponse to null. [HTTP-CACHING]
- if (
- !safeMethodsSet.has(httpRequest.method) &&
- forwardResponse.status >= 200 &&
- forwardResponse.status <= 399
- ) {
- // TODO: cache
- }
-
- // 4. If the revalidatingFlag is set and forwardResponse’s status is 304,
- // then:
- if (revalidatingFlag && forwardResponse.status === 304) {
- // TODO: cache
- }
-
- // 5. If response is null, then:
- if (response == null) {
- // 1. Set response to forwardResponse.
- response = forwardResponse
-
- // 2. Store httpRequest and forwardResponse in httpCache, as per the
- // "Storing Responses in Caches" chapter of HTTP Caching. [HTTP-CACHING]
- // TODO: cache
- }
- }
-
- // 11. Set response’s URL list to a clone of httpRequest’s URL list.
- response.urlList = [...httpRequest.urlList]
-
- // 12. If httpRequest’s header list contains `Range`, then set response’s
- // range-requested flag.
- if (httpRequest.headersList.contains('range', true)) {
- response.rangeRequested = true
- }
-
- // 13. Set response’s request-includes-credentials to includeCredentials.
- response.requestIncludesCredentials = includeCredentials
-
- // 14. If response’s status is 401, httpRequest’s response tainting is not "cors",
- // includeCredentials is true, and request’s traversable for user prompts is
- // a traversable navigable:
- if (response.status === 401 && httpRequest.responseTainting !== 'cors' && includeCredentials && isTraversableNavigable(request.traversableForUserPrompts)) {
- // 2. If request’s body is non-null, then:
- if (request.body != null) {
- // 1. If request’s body’s source is null, then return a network error.
- if (request.body.source == null) {
- return makeNetworkError('expected non-null body source')
- }
-
- // 2. Set request’s body to the body of the result of safely extracting
- // request’s body’s source.
- request.body = safelyExtractBody(request.body.source)[0]
- }
-
- // 3. If request’s use-URL-credentials flag is unset or isAuthenticationFetch is
- // true, then:
- if (request.useURLCredentials === undefined || isAuthenticationFetch) {
- // 1. If fetchParams is canceled, then return the appropriate network error
- // for fetchParams.
- if (isCancelled(fetchParams)) {
- return makeAppropriateNetworkError(fetchParams)
- }
-
- // 2. Let username and password be the result of prompting the end user for a
- // username and password, respectively, in request’s traversable for user prompts.
- // TODO
-
- // 3. Set the username given request’s current URL and username.
- // requestCurrentURL(request).username = TODO
-
- // 4. Set the password given request’s current URL and password.
- // requestCurrentURL(request).password = TODO
-
- // In browsers, the user will be prompted to enter a username/password before the request
- // is re-sent. To prevent an infinite 401 loop, return the response for now.
- // https://github.com/nodejs/undici/pull/4756
- return response
- }
-
- // 4. Set response to the result of running HTTP-network-or-cache fetch given
- // fetchParams and true.
- fetchParams.controller.connection.destroy()
-
- response = await httpNetworkOrCacheFetch(fetchParams, true)
- }
-
- // 15. If response’s status is 407, then:
- if (response.status === 407) {
- // 1. If request’s window is "no-window", then return a network error.
- if (request.window === 'no-window') {
- return makeNetworkError()
- }
-
- // 2. ???
-
- // 3. If fetchParams is canceled, then return the appropriate network error for fetchParams.
- if (isCancelled(fetchParams)) {
- return makeAppropriateNetworkError(fetchParams)
- }
-
- // 4. Prompt the end user as appropriate in request’s window and store
- // the result as a proxy-authentication entry. [HTTP-AUTH]
- // TODO: Invoke some kind of callback?
-
- // 5. Set response to the result of running HTTP-network-or-cache fetch given
- // fetchParams.
- // TODO
- return makeNetworkError('proxy authentication required')
- }
-
- // 16. If all of the following are true
- if (
- // response’s status is 421
- response.status === 421 &&
- // isNewConnectionFetch is false
- !isNewConnectionFetch &&
- // request’s body is null, or request’s body is non-null and request’s body’s source is non-null
- (request.body == null || request.body.source != null)
- ) {
- // then:
-
- // 1. If fetchParams is canceled, then return the appropriate network error for fetchParams.
- if (isCancelled(fetchParams)) {
- return makeAppropriateNetworkError(fetchParams)
- }
-
- // 2. Set response to the result of running HTTP-network-or-cache
- // fetch given fetchParams, isAuthenticationFetch, and true.
-
- // TODO (spec): The spec doesn't specify this but we need to cancel
- // the active response before we can start a new one.
- // https://github.com/whatwg/fetch/issues/1293
- fetchParams.controller.connection.destroy()
-
- response = await httpNetworkOrCacheFetch(
- fetchParams,
- isAuthenticationFetch,
- true
- )
- }
-
- // 17. If isAuthenticationFetch is true, then create an authentication entry
- if (isAuthenticationFetch) {
- // TODO
- }
-
- // 18. Return response.
- return response
-}
-
-// https://fetch.spec.whatwg.org/#http-network-fetch
-async function httpNetworkFetch (
- fetchParams,
- includeCredentials = false,
- forceNewConnection = false
-) {
- assert(!fetchParams.controller.connection || fetchParams.controller.connection.destroyed)
-
- fetchParams.controller.connection = {
- abort: null,
- destroyed: false,
- destroy (err, abort = true) {
- if (!this.destroyed) {
- this.destroyed = true
- if (abort) {
- this.abort?.(err ?? new DOMException('The operation was aborted.', 'AbortError'))
- }
- }
- }
- }
-
- // 1. Let request be fetchParams’s request.
- const request = fetchParams.request
-
- // 2. Let response be null.
- let response = null
-
- // 3. Let timingInfo be fetchParams’s timing info.
- const timingInfo = fetchParams.timingInfo
-
- // 4. Let httpCache be the result of determining the HTTP cache partition,
- // given request.
- // TODO: cache
- const httpCache = null
-
- // 5. If httpCache is null, then set request’s cache mode to "no-store".
- if (httpCache == null) {
- request.cache = 'no-store'
- }
-
- // 6. Let networkPartitionKey be the result of determining the network
- // partition key given request.
- // TODO
-
- // 7. Let newConnection be "yes" if forceNewConnection is true; otherwise
- // "no".
- const newConnection = forceNewConnection ? 'yes' : 'no' // eslint-disable-line no-unused-vars
-
- // 8. Switch on request’s mode:
- if (request.mode === 'websocket') {
- // Let connection be the result of obtaining a WebSocket connection,
- // given request’s current URL.
- // TODO
- } else {
- // Let connection be the result of obtaining a connection, given
- // networkPartitionKey, request’s current URL’s origin,
- // includeCredentials, and forceNewConnection.
- // TODO
- }
-
- // 9. Run these steps, but abort when the ongoing fetch is terminated:
-
- // 1. If connection is failure, then return a network error.
-
- // 2. Set timingInfo’s final connection timing info to the result of
- // calling clamp and coarsen connection timing info with connection’s
- // timing info, timingInfo’s post-redirect start time, and fetchParams’s
- // cross-origin isolated capability.
-
- // 3. If connection is not an HTTP/2 connection, request’s body is non-null,
- // and request’s body’s source is null, then append (`Transfer-Encoding`,
- // `chunked`) to request’s header list.
-
- // 4. Set timingInfo’s final network-request start time to the coarsened
- // shared current time given fetchParams’s cross-origin isolated
- // capability.
-
- // 5. Set response to the result of making an HTTP request over connection
- // using request with the following caveats:
-
- // - Follow the relevant requirements from HTTP. [HTTP] [HTTP-SEMANTICS]
- // [HTTP-COND] [HTTP-CACHING] [HTTP-AUTH]
-
- // - If request’s body is non-null, and request’s body’s source is null,
- // then the user agent may have a buffer of up to 64 kibibytes and store
- // a part of request’s body in that buffer. If the user agent reads from
- // request’s body beyond that buffer’s size and the user agent needs to
- // resend request, then instead return a network error.
-
- // - Set timingInfo’s final network-response start time to the coarsened
- // shared current time given fetchParams’s cross-origin isolated capability,
- // immediately after the user agent’s HTTP parser receives the first byte
- // of the response (e.g., frame header bytes for HTTP/2 or response status
- // line for HTTP/1.x).
-
- // - Wait until all the headers are transmitted.
-
- // - Any responses whose status is in the range 100 to 199, inclusive,
- // and is not 101, are to be ignored, except for the purposes of setting
- // timingInfo’s final network-response start time above.
-
- // - If request’s header list contains `Transfer-Encoding`/`chunked` and
- // response is transferred via HTTP/1.0 or older, then return a network
- // error.
-
- // - If the HTTP request results in a TLS client certificate dialog, then:
-
- // 1. If request’s window is an environment settings object, make the
- // dialog available in request’s window.
-
- // 2. Otherwise, return a network error.
-
- // To transmit request’s body body, run these steps:
- let requestBody = null
- // 1. If body is null and fetchParams’s process request end-of-body is
- // non-null, then queue a fetch task given fetchParams’s process request
- // end-of-body and fetchParams’s task destination.
- if (request.body == null && fetchParams.processRequestEndOfBody) {
- queueMicrotask(() => fetchParams.processRequestEndOfBody())
- } else if (request.body != null) {
- // 2. Otherwise, if body is non-null:
-
- // 1. Let processBodyChunk given bytes be these steps:
- const processBodyChunk = async function * (bytes) {
- // 1. If the ongoing fetch is terminated, then abort these steps.
- if (isCancelled(fetchParams)) {
- return
- }
-
- // 2. Run this step in parallel: transmit bytes.
- yield bytes
-
- // 3. If fetchParams’s process request body is non-null, then run
- // fetchParams’s process request body given bytes’s length.
- fetchParams.processRequestBodyChunkLength?.(bytes.byteLength)
- }
-
- // 2. Let processEndOfBody be these steps:
- const processEndOfBody = () => {
- // 1. If fetchParams is canceled, then abort these steps.
- if (isCancelled(fetchParams)) {
- return
- }
-
- // 2. If fetchParams’s process request end-of-body is non-null,
- // then run fetchParams’s process request end-of-body.
- if (fetchParams.processRequestEndOfBody) {
- fetchParams.processRequestEndOfBody()
- }
- }
-
- // 3. Let processBodyError given e be these steps:
- const processBodyError = (e) => {
- // 1. If fetchParams is canceled, then abort these steps.
- if (isCancelled(fetchParams)) {
- return
- }
-
- // 2. If e is an "AbortError" DOMException, then abort fetchParams’s controller.
- if (e.name === 'AbortError') {
- fetchParams.controller.abort()
- } else {
- fetchParams.controller.terminate(e)
- }
- }
-
- // 4. Incrementally read request’s body given processBodyChunk, processEndOfBody,
- // processBodyError, and fetchParams’s task destination.
- requestBody = (async function * () {
- try {
- for await (const bytes of request.body.stream) {
- yield * processBodyChunk(bytes)
- }
- processEndOfBody()
- } catch (err) {
- processBodyError(err)
- }
- })()
- }
-
- try {
- // socket is only provided for websockets
- const { body, status, statusText, headersList, socket } = await dispatch({ body: requestBody })
-
- if (socket) {
- response = makeResponse({ status, statusText, headersList, socket })
- } else {
- const iterator = body[Symbol.asyncIterator]()
- fetchParams.controller.next = () => iterator.next()
-
- response = makeResponse({ status, statusText, headersList })
- }
- } catch (err) {
- // 10. If aborted, then:
- if (err.name === 'AbortError') {
- // 1. If connection uses HTTP/2, then transmit an RST_STREAM frame.
- fetchParams.controller.connection.destroy()
-
- // 2. Return the appropriate network error for fetchParams.
- return makeAppropriateNetworkError(fetchParams, err)
- }
-
- return makeNetworkError(err)
- }
-
- // 11. Let pullAlgorithm be an action that resumes the ongoing fetch
- // if it is suspended.
- const pullAlgorithm = () => {
- return fetchParams.controller.resume()
- }
-
- // 12. Let cancelAlgorithm be an algorithm that aborts fetchParams’s
- // controller with reason, given reason.
- const cancelAlgorithm = (reason) => {
- // If the aborted fetch was already terminated, then we do not
- // need to do anything.
- if (!isCancelled(fetchParams)) {
- fetchParams.controller.abort(reason)
- }
- }
-
- // 13. Let highWaterMark be a non-negative, non-NaN number, chosen by
- // the user agent.
- // TODO
-
- // 14. Let sizeAlgorithm be an algorithm that accepts a chunk object
- // and returns a non-negative, non-NaN, non-infinite number, chosen by the user agent.
- // TODO
-
- // 15. Let stream be a new ReadableStream.
- // 16. Set up stream with byte reading support with pullAlgorithm set to pullAlgorithm,
- // cancelAlgorithm set to cancelAlgorithm.
- const stream = new ReadableStream(
- {
- start (controller) {
- fetchParams.controller.controller = controller
- },
- pull: pullAlgorithm,
- cancel: cancelAlgorithm,
- type: 'bytes'
- }
- )
-
- // 17. Run these steps, but abort when the ongoing fetch is terminated:
-
- // 1. Set response’s body to a new body whose stream is stream.
- response.body = { stream, source: null, length: null }
-
- // 2. If response is not a network error and request’s cache mode is
- // not "no-store", then update response in httpCache for request.
- // TODO
-
- // 3. If includeCredentials is true and the user agent is not configured
- // to block cookies for request (see section 7 of [COOKIES]), then run the
- // "set-cookie-string" parsing algorithm (see section 5.2 of [COOKIES]) on
- // the value of each header whose name is a byte-case-insensitive match for
- // `Set-Cookie` in response’s header list, if any, and request’s current URL.
- // TODO
-
- // 18. If aborted, then:
- // TODO
-
- // 19. Run these steps in parallel:
-
- // 1. Run these steps, but abort when fetchParams is canceled:
- if (!fetchParams.controller.resume) {
- fetchParams.controller.on('terminated', onAborted)
- }
-
- fetchParams.controller.resume = async () => {
- // 1. While true
- while (true) {
- // 1-3. See onData...
-
- // 4. Set bytes to the result of handling content codings given
- // codings and bytes.
- let bytes
- let isFailure
- try {
- const { done, value } = await fetchParams.controller.next()
-
- if (isAborted(fetchParams)) {
- break
- }
-
- bytes = done ? undefined : value
- } catch (err) {
- if (fetchParams.controller.ended && !timingInfo.encodedBodySize) {
- // zlib doesn't like empty streams.
- bytes = undefined
- } else {
- bytes = err
-
- // err may be propagated from the result of calling readablestream.cancel,
- // which might not be an error. https://github.com/nodejs/undici/issues/2009
- isFailure = true
- }
- }
-
- if (bytes === undefined) {
- // 2. Otherwise, if the bytes transmission for response’s message
- // body is done normally and stream is readable, then close
- // stream, finalize response for fetchParams and response, and
- // abort these in-parallel steps.
- readableStreamClose(fetchParams.controller.controller)
-
- finalizeResponse(fetchParams, response)
-
- return
- }
-
- // 5. Increase timingInfo’s decoded body size by bytes’s length.
- timingInfo.decodedBodySize += bytes?.byteLength ?? 0
-
- // 6. If bytes is failure, then terminate fetchParams’s controller.
- if (isFailure) {
- fetchParams.controller.terminate(bytes)
- return
- }
-
- // 7. Enqueue a Uint8Array wrapping an ArrayBuffer containing bytes
- // into stream.
- const buffer = new Uint8Array(bytes)
- if (buffer.byteLength) {
- fetchParams.controller.controller.enqueue(buffer)
- }
-
- // 8. If stream is errored, then terminate the ongoing fetch.
- if (isErrored(stream)) {
- fetchParams.controller.terminate()
- return
- }
-
- // 9. If stream doesn’t need more data ask the user agent to suspend
- // the ongoing fetch.
- if (fetchParams.controller.controller.desiredSize <= 0) {
- return
- }
- }
- }
-
- // 2. If aborted, then:
- function onAborted (reason) {
- // 2. If fetchParams is aborted, then:
- if (isAborted(fetchParams)) {
- // 1. Set response’s aborted flag.
- response.aborted = true
-
- // 2. If stream is readable, then error stream with the result of
- // deserialize a serialized abort reason given fetchParams’s
- // controller’s serialized abort reason and an
- // implementation-defined realm.
- if (isReadable(stream)) {
- fetchParams.controller.controller.error(
- fetchParams.controller.serializedAbortReason
- )
- }
- } else {
- // 3. Otherwise, if stream is readable, error stream with a TypeError.
- if (isReadable(stream)) {
- fetchParams.controller.controller.error(new TypeError('terminated', {
- cause: isErrorLike(reason) ? reason : undefined
- }))
- }
- }
-
- // 4. If connection uses HTTP/2, then transmit an RST_STREAM frame.
- // 5. Otherwise, the user agent should close connection unless it would be bad for performance to do so.
- fetchParams.controller.connection.destroy()
- }
-
- // 20. Return response.
- return response
-
- function dispatch ({ body }) {
- const url = requestCurrentURL(request)
- /** @type {import('../../..').Agent} */
- const agent = fetchParams.controller.dispatcher
-
- return new Promise((resolve, reject) => agent.dispatch(
- {
- path: url.pathname + url.search,
- origin: url.origin,
- method: request.method,
- body: agent.isMockActive ? request.body && (request.body.source || request.body.stream) : body,
- headers: request.headersList.entries,
- maxRedirections: 0,
- upgrade: request.mode === 'websocket' ? 'websocket' : undefined
- },
- {
- body: null,
- abort: null,
-
- onConnect (abort) {
- // TODO (fix): Do we need connection here?
- const { connection } = fetchParams.controller
-
- // Set timingInfo’s final connection timing info to the result of calling clamp and coarsen
- // connection timing info with connection’s timing info, timingInfo’s post-redirect start
- // time, and fetchParams’s cross-origin isolated capability.
- // TODO: implement connection timing
- timingInfo.finalConnectionTimingInfo = clampAndCoarsenConnectionTimingInfo(undefined, timingInfo.postRedirectStartTime, fetchParams.crossOriginIsolatedCapability)
-
- if (connection.destroyed) {
- abort(new DOMException('The operation was aborted.', 'AbortError'))
- } else {
- fetchParams.controller.on('terminated', abort)
- this.abort = connection.abort = abort
- }
-
- // Set timingInfo’s final network-request start time to the coarsened shared current time given
- // fetchParams’s cross-origin isolated capability.
- timingInfo.finalNetworkRequestStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)
- },
-
- onResponseStarted () {
- // Set timingInfo’s final network-response start time to the coarsened shared current
- // time given fetchParams’s cross-origin isolated capability, immediately after the
- // user agent’s HTTP parser receives the first byte of the response (e.g., frame header
- // bytes for HTTP/2 or response status line for HTTP/1.x).
- timingInfo.finalNetworkResponseStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)
- },
-
- onHeaders (status, rawHeaders, resume, statusText) {
- if (status < 200) {
- return false
- }
-
- const headersList = new HeadersList()
-
- for (let i = 0; i < rawHeaders.length; i += 2) {
- headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true)
- }
- const location = headersList.get('location', true)
-
- this.body = new Readable({ read: resume })
-
- const willFollow = location && request.redirect === 'follow' &&
- redirectStatusSet.has(status)
-
- const decoders = []
-
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
- if (request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {
- // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
- const contentEncoding = headersList.get('content-encoding', true)
- // "All content-coding values are case-insensitive..."
- /** @type {string[]} */
- const codings = contentEncoding ? contentEncoding.toLowerCase().split(',') : []
-
- // Limit the number of content-encodings to prevent resource exhaustion.
- // CVE fix similar to urllib3 (GHSA-gm62-xv2j-4w53) and curl (CVE-2022-32206).
- const maxContentEncodings = 5
- if (codings.length > maxContentEncodings) {
- reject(new Error(`too many content-encodings in response: ${codings.length}, maximum allowed is ${maxContentEncodings}`))
- return true
- }
-
- for (let i = codings.length - 1; i >= 0; --i) {
- const coding = codings[i].trim()
- // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2
- if (coding === 'x-gzip' || coding === 'gzip') {
- decoders.push(zlib.createGunzip({
- // Be less strict when decoding compressed responses, since sometimes
- // servers send slightly invalid responses that are still accepted
- // by common browsers.
- // Always using Z_SYNC_FLUSH is what cURL does.
- flush: zlib.constants.Z_SYNC_FLUSH,
- finishFlush: zlib.constants.Z_SYNC_FLUSH
- }))
- } else if (coding === 'deflate') {
- decoders.push(createInflate({
- flush: zlib.constants.Z_SYNC_FLUSH,
- finishFlush: zlib.constants.Z_SYNC_FLUSH
- }))
- } else if (coding === 'br') {
- decoders.push(zlib.createBrotliDecompress({
- flush: zlib.constants.BROTLI_OPERATION_FLUSH,
- finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
- }))
- } else if (coding === 'zstd' && hasZstd) {
- decoders.push(zlib.createZstdDecompress({
- flush: zlib.constants.ZSTD_e_continue,
- finishFlush: zlib.constants.ZSTD_e_end
- }))
- } else {
- decoders.length = 0
- break
- }
- }
- }
-
- const onError = this.onError.bind(this)
-
- resolve({
- status,
- statusText,
- headersList,
- body: decoders.length
- ? pipeline(this.body, ...decoders, (err) => {
- if (err) {
- this.onError(err)
- }
- }).on('error', onError)
- : this.body.on('error', onError)
- })
-
- return true
- },
-
- onData (chunk) {
- if (fetchParams.controller.dump) {
- return
- }
-
- // 1. If one or more bytes have been transmitted from response’s
- // message body, then:
-
- // 1. Let bytes be the transmitted bytes.
- const bytes = chunk
-
- // 2. Let codings be the result of extracting header list values
- // given `Content-Encoding` and response’s header list.
- // See pullAlgorithm.
-
- // 3. Increase timingInfo’s encoded body size by bytes’s length.
- timingInfo.encodedBodySize += bytes.byteLength
-
- // 4. See pullAlgorithm...
-
- return this.body.push(bytes)
- },
-
- onComplete () {
- if (this.abort) {
- fetchParams.controller.off('terminated', this.abort)
- }
-
- fetchParams.controller.ended = true
-
- this.body.push(null)
- },
-
- onError (error) {
- if (this.abort) {
- fetchParams.controller.off('terminated', this.abort)
- }
-
- this.body?.destroy(error)
-
- fetchParams.controller.terminate(error)
-
- reject(error)
- },
-
- onRequestUpgrade (_controller, status, headers, socket) {
- // We need to support 200 for websocket over h2 as per RFC-8441
- // Absence of session means H1
- if ((socket.session != null && status !== 200) || (socket.session == null && status !== 101)) {
- return false
- }
-
- const headersList = new HeadersList()
-
- for (const [name, value] of Object.entries(headers)) {
- if (value == null) {
- continue
- }
-
- const headerName = name.toLowerCase()
-
- if (Array.isArray(value)) {
- for (const entry of value) {
- headersList.append(headerName, String(entry), true)
- }
- } else {
- headersList.append(headerName, String(value), true)
- }
- }
-
- resolve({
- status,
- statusText: STATUS_CODES[status],
- headersList,
- socket
- })
-
- return true
- },
-
- onUpgrade (status, rawHeaders, socket) {
- // We need to support 200 for websocket over h2 as per RFC-8441
- // Absence of session means H1
- if ((socket.session != null && status !== 200) || (socket.session == null && status !== 101)) {
- return false
- }
-
- const headersList = new HeadersList()
-
- for (let i = 0; i < rawHeaders.length; i += 2) {
- headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true)
- }
-
- resolve({
- status,
- statusText: STATUS_CODES[status],
- headersList,
- socket
- })
-
- return true
- }
- }
- ))
- }
-}
-
-module.exports = {
- fetch,
- Fetch,
- fetching,
- finalizeAndReportTiming
-}
diff --git a/vanilla/node_modules/undici/lib/web/fetch/request.js b/vanilla/node_modules/undici/lib/web/fetch/request.js
deleted file mode 100644
index 6ef40f9..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/request.js
+++ /dev/null
@@ -1,1115 +0,0 @@
-/* globals AbortController */
-
-'use strict'
-
-const { extractBody, mixinBody, cloneBody, bodyUnusable } = require('./body')
-const { Headers, fill: fillHeaders, HeadersList, setHeadersGuard, getHeadersGuard, setHeadersList, getHeadersList } = require('./headers')
-const util = require('../../core/util')
-const nodeUtil = require('node:util')
-const {
- isValidHTTPToken,
- sameOrigin,
- environmentSettingsObject
-} = require('./util')
-const {
- forbiddenMethodsSet,
- corsSafeListedMethodsSet,
- referrerPolicy,
- requestRedirect,
- requestMode,
- requestCredentials,
- requestCache,
- requestDuplex
-} = require('./constants')
-const { kEnumerableProperty, normalizedMethodRecordsBase, normalizedMethodRecords } = util
-const { webidl } = require('../webidl')
-const { URLSerializer } = require('./data-url')
-const { kConstruct } = require('../../core/symbols')
-const assert = require('node:assert')
-const { getMaxListeners, setMaxListeners, defaultMaxListeners } = require('node:events')
-
-const kAbortController = Symbol('abortController')
-
-const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => {
- signal.removeEventListener('abort', abort)
-})
-
-const dependentControllerMap = new WeakMap()
-
-let abortSignalHasEventHandlerLeakWarning
-
-try {
- abortSignalHasEventHandlerLeakWarning = getMaxListeners(new AbortController().signal) > 0
-} catch {
- abortSignalHasEventHandlerLeakWarning = false
-}
-
-function buildAbort (acRef) {
- return abort
-
- function abort () {
- const ac = acRef.deref()
- if (ac !== undefined) {
- // Currently, there is a problem with FinalizationRegistry.
- // https://github.com/nodejs/node/issues/49344
- // https://github.com/nodejs/node/issues/47748
- // In the case of abort, the first step is to unregister from it.
- // If the controller can refer to it, it is still registered.
- // It will be removed in the future.
- requestFinalizer.unregister(abort)
-
- // Unsubscribe a listener.
- // FinalizationRegistry will no longer be called, so this must be done.
- this.removeEventListener('abort', abort)
-
- ac.abort(this.reason)
-
- const controllerList = dependentControllerMap.get(ac.signal)
-
- if (controllerList !== undefined) {
- if (controllerList.size !== 0) {
- for (const ref of controllerList) {
- const ctrl = ref.deref()
- if (ctrl !== undefined) {
- ctrl.abort(this.reason)
- }
- }
- controllerList.clear()
- }
- dependentControllerMap.delete(ac.signal)
- }
- }
- }
-}
-
-let patchMethodWarning = false
-
-// https://fetch.spec.whatwg.org/#request-class
-class Request {
- /** @type {AbortSignal} */
- #signal
-
- /** @type {import('../../dispatcher/dispatcher')} */
- #dispatcher
-
- /** @type {Headers} */
- #headers
-
- #state
-
- // https://fetch.spec.whatwg.org/#dom-request
- constructor (input, init = undefined) {
- webidl.util.markAsUncloneable(this)
-
- if (input === kConstruct) {
- return
- }
-
- const prefix = 'Request constructor'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- input = webidl.converters.RequestInfo(input)
- init = webidl.converters.RequestInit(init)
-
- // 1. Let request be null.
- let request = null
-
- // 2. Let fallbackMode be null.
- let fallbackMode = null
-
- // 3. Let baseURL be this’s relevant settings object’s API base URL.
- const baseUrl = environmentSettingsObject.settingsObject.baseUrl
-
- // 4. Let signal be null.
- let signal = null
-
- // 5. If input is a string, then:
- if (typeof input === 'string') {
- this.#dispatcher = init.dispatcher
-
- // 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(webidl.is.Request(input))
-
- // 8. Set request to input’s request.
- request = input.#state
-
- // 9. Set signal to input’s signal.
- signal = input.#signal
-
- this.#dispatcher = init.dispatcher || input.#dispatcher
- }
-
- // 7. Let origin be this’s relevant settings object’s origin.
- const origin = environmentSettingsObject.settingsObject.origin
-
- // 8. Let window be "client".
- let window = 'client'
-
- // 9. If request’s window is an environment settings object and its origin
- // is same origin with origin, then set window to request’s 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 request’s URL.
- // undici implementation note: this is set as the first item in request's urlList in makeRequest
- // method request’s method.
- method: request.method,
- // header list A copy of request’s header list.
- // undici implementation note: headersList is cloned in makeRequest
- headersList: request.headersList,
- // unsafe-request flag Set.
- unsafeRequest: request.unsafeRequest,
- // client This’s relevant settings object.
- client: environmentSettingsObject.settingsObject,
- // window window.
- window,
- // priority request’s priority.
- priority: request.priority,
- // origin request’s 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 request’s referrer.
- referrer: request.referrer,
- // referrer policy request’s referrer policy.
- referrerPolicy: request.referrerPolicy,
- // mode request’s mode.
- mode: request.mode,
- // credentials mode request’s credentials mode.
- credentials: request.credentials,
- // cache mode request’s cache mode.
- cache: request.cache,
- // redirect mode request’s redirect mode.
- redirect: request.redirect,
- // integrity metadata request’s integrity metadata.
- integrity: request.integrity,
- // keepalive request’s keepalive.
- keepalive: request.keepalive,
- // reload-navigation flag request’s reload-navigation flag.
- reloadNavigation: request.reloadNavigation,
- // history-navigation flag request’s history-navigation flag.
- historyNavigation: request.historyNavigation,
- // URL list A clone of request’s URL list.
- urlList: [...request.urlList]
- })
-
- const initHasKey = Object.keys(init).length !== 0
-
- // 13. If init is not empty, then:
- if (initHasKey) {
- // 1. If request’s mode is "navigate", then set it to "same-origin".
- if (request.mode === 'navigate') {
- request.mode = 'same-origin'
- }
-
- // 2. Unset request’s reload-navigation flag.
- request.reloadNavigation = false
-
- // 3. Unset request’s history-navigation flag.
- request.historyNavigation = false
-
- // 4. Set request’s origin to "client".
- request.origin = 'client'
-
- // 5. Set request’s referrer to "client"
- request.referrer = 'client'
-
- // 6. Set request’s referrer policy to the empty string.
- request.referrerPolicy = ''
-
- // 7. Set request’s URL to request’s current URL.
- request.url = request.urlList[request.urlList.length - 1]
-
- // 8. Set request’s URL list to « request’s 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 request’s 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
- // - parsedReferrer’s scheme is "about" and path is the string "client"
- // - parsedReferrer’s origin is not same origin with origin
- // then set request’s referrer to "client".
- if (
- (parsedReferrer.protocol === 'about:' && parsedReferrer.hostname === 'client') ||
- (origin && !sameOrigin(parsedReferrer, environmentSettingsObject.settingsObject.baseUrl))
- ) {
- request.referrer = 'client'
- } else {
- // 4. Otherwise, set request’s referrer to parsedReferrer.
- request.referrer = parsedReferrer
- }
- }
- }
-
- // 15. If init["referrerPolicy"] exists, then set request’s 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 request’s mode to mode.
- if (mode != null) {
- request.mode = mode
- }
-
- // 19. If init["credentials"] exists, then set request’s credentials mode
- // to it.
- if (init.credentials !== undefined) {
- request.credentials = init.credentials
- }
-
- // 18. If init["cache"] exists, then set request’s cache mode to it.
- if (init.cache !== undefined) {
- request.cache = init.cache
- }
-
- // 21. If request’s cache mode is "only-if-cached" and request’s 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 request’s redirect mode to it.
- if (init.redirect !== undefined) {
- request.redirect = init.redirect
- }
-
- // 23. If init["integrity"] exists, then set request’s integrity metadata to it.
- if (init.integrity != null) {
- request.integrity = String(init.integrity)
- }
-
- // 24. If init["keepalive"] exists, then set request’s 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
-
- const mayBeNormalized = normalizedMethodRecords[method]
-
- if (mayBeNormalized !== undefined) {
- // Note: Bypass validation DELETE, GET, HEAD, OPTIONS, POST, PUT, PATCH and these lowercase ones
- request.method = mayBeNormalized
- } else {
- // 2. If method is not a method or method is a forbidden method, then
- // throw a TypeError.
- if (!isValidHTTPToken(method)) {
- throw new TypeError(`'${method}' is not a valid HTTP method.`)
- }
-
- const upperCase = method.toUpperCase()
-
- if (forbiddenMethodsSet.has(upperCase)) {
- throw new TypeError(`'${method}' HTTP method is unsupported.`)
- }
-
- // 3. Normalize method.
- // https://fetch.spec.whatwg.org/#concept-method-normalize
- // Note: must be in uppercase
- method = normalizedMethodRecordsBase[upperCase] ?? method
-
- // 4. Set request’s method to method.
- request.method = method
- }
-
- if (!patchMethodWarning && request.method === 'patch') {
- process.emitWarning('Using `patch` is highly likely to result in a `405 Method Not Allowed`. `PATCH` is much more likely to succeed.', {
- code: 'UNDICI-FETCH-patch'
- })
-
- patchMethodWarning = true
- }
- }
-
- // 26. If init["signal"] exists, then set signal to it.
- if (init.signal !== undefined) {
- signal = init.signal
- }
-
- // 27. Set this’s request to request.
- this.#state = request
-
- // 28. Set this’s signal to a new AbortSignal object with this’s relevant
- // Realm.
- // TODO: could this be simplified with AbortSignal.any
- // (https://dom.spec.whatwg.org/#dom-abortsignal-any)
- const ac = new AbortController()
- this.#signal = ac.signal
-
- // 29. If signal is not null, then make this’s signal follow signal.
- if (signal != null) {
- 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 = buildAbort(acRef)
-
- // If the max amount of listeners is equal to the default, increase it
- if (abortSignalHasEventHandlerLeakWarning && getMaxListeners(signal) === defaultMaxListeners) {
- setMaxListeners(1500, signal)
- }
-
- util.addAbortListener(signal, abort)
- // The third argument must be a registry key to be unregistered.
- // Without it, you cannot unregister.
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry
- // abort is used as the unregister key. (because it is unique)
- requestFinalizer.register(ac, { signal, abort }, abort)
- }
- }
-
- // 30. Set this’s headers to a new Headers object with this’s relevant
- // Realm, whose header list is request’s header list and guard is
- // "request".
- this.#headers = new Headers(kConstruct)
- setHeadersList(this.#headers, request.headersList)
- setHeadersGuard(this.#headers, 'request')
-
- // 31. If this’s request’s mode is "no-cors", then:
- if (mode === 'no-cors') {
- // 1. If this’s request’s 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 this’s headers’s guard to "request-no-cors".
- setHeadersGuard(this.#headers, 'request-no-cors')
- }
-
- // 32. If init is not empty, then:
- if (initHasKey) {
- /** @type {HeadersList} */
- const headersList = getHeadersList(this.#headers)
- // 1. Let headers be a copy of this’s headers and its associated header
- // list.
- // 2. If init["headers"] exists, then set headers to init["headers"].
- const headers = init.headers !== undefined ? init.headers : new HeadersList(headersList)
-
- // 3. Empty this’s headers’s header list.
- headersList.clear()
-
- // 4. If headers is a Headers object, then for each header in its header
- // list, append header’s name/header’s value to this’s headers.
- if (headers instanceof HeadersList) {
- for (const { name, value } of headers.rawValues()) {
- headersList.append(name, value, false)
- }
- // Note: Copy the `set-cookie` meta-data.
- headersList.cookies = headers.cookies
- } else {
- // 5. Otherwise, fill this’s headers with headers.
- fillHeaders(this.#headers, headers)
- }
- }
-
- // 33. Let inputBody be input’s request’s body if input is a Request
- // object; otherwise null.
- const inputBody = webidl.is.Request(input) ? input.#state.body : null
-
- // 34. If either init["body"] exists and is non-null or inputBody is
- // non-null, and request’s 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 request’s keepalive.
- const [extractedBody, contentType] = extractBody(
- init.body,
- request.keepalive
- )
- initBody = extractedBody
-
- // 3, If Content-Type is non-null and this’s headers’s header list does
- // not contain `Content-Type`, then append `Content-Type`/Content-Type to
- // this’s headers.
- if (contentType && !getHeadersList(this.#headers).contains('content-type', true)) {
- this.#headers.append('content-type', contentType, true)
- }
- }
-
- // 37. Let inputOrInitBody be initBody if it is non-null; otherwise
- // inputBody.
- const inputOrInitBody = initBody ?? inputBody
-
- // 38. If inputOrInitBody is non-null and inputOrInitBody’s 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 this’s request’s 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 this’s request’s 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 (bodyUnusable(input.#state)) {
- 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.
- // 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 this’s request’s body to finalBody.
- this.#state.body = finalBody
- }
-
- // Returns request’s HTTP method, which is "GET" by default.
- get method () {
- webidl.brandCheck(this, Request)
-
- // The method getter steps are to return this’s request’s method.
- return this.#state.method
- }
-
- // Returns the URL of request as a string.
- get url () {
- webidl.brandCheck(this, Request)
-
- // The url getter steps are to return this’s request’s URL, serialized.
- return URLSerializer(this.#state.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 this’s headers.
- return this.#headers
- }
-
- // 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 this’s request’s destination.
- return this.#state.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 global’s 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 this’s request’s referrer is "no-referrer", then return the
- // empty string.
- if (this.#state.referrer === 'no-referrer') {
- return ''
- }
-
- // 2. If this’s request’s referrer is "client", then return
- // "about:client".
- if (this.#state.referrer === 'client') {
- return 'about:client'
- }
-
- // Return this’s request’s referrer, serialized.
- return this.#state.referrer.toString()
- }
-
- // Returns the referrer policy associated with request.
- // This is used during fetching to compute the value of the request’s
- // referrer.
- get referrerPolicy () {
- webidl.brandCheck(this, Request)
-
- // The referrerPolicy getter steps are to return this’s request’s referrer policy.
- return this.#state.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 this’s request’s mode.
- return this.#state.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 () {
- webidl.brandCheck(this, Request)
-
- // The credentials getter steps are to return this’s request’s credentials mode.
- return this.#state.credentials
- }
-
- // Returns the cache mode associated with request,
- // which is a string indicating how the request will
- // interact with the browser’s cache when fetching.
- get cache () {
- webidl.brandCheck(this, Request)
-
- // The cache getter steps are to return this’s request’s cache mode.
- return this.#state.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 this’s request’s redirect mode.
- return this.#state.redirect
- }
-
- // Returns request’s 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 this’s request’s integrity
- // metadata.
- return this.#state.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 this’s request’s keepalive.
- return this.#state.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 this’s
- // request’s reload-navigation flag is set; otherwise false.
- return this.#state.reloadNavigation
- }
-
- // Returns a boolean indicating whether or not request is for a history
- // navigation (a.k.a. back-forward navigation).
- get isHistoryNavigation () {
- webidl.brandCheck(this, Request)
-
- // The isHistoryNavigation getter steps are to return true if this’s request’s
- // history-navigation flag is set; otherwise false.
- return this.#state.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 this’s signal.
- return this.#signal
- }
-
- get body () {
- webidl.brandCheck(this, Request)
-
- return this.#state.body ? this.#state.body.stream : null
- }
-
- get bodyUsed () {
- webidl.brandCheck(this, Request)
-
- return !!this.#state.body && util.isDisturbed(this.#state.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 (bodyUnusable(this.#state)) {
- throw new TypeError('unusable')
- }
-
- // 2. Let clonedRequest be the result of cloning this’s request.
- const clonedRequest = cloneRequest(this.#state)
-
- // 3. Let clonedRequestObject be the result of creating a Request object,
- // given clonedRequest, this’s headers’s guard, and this’s relevant Realm.
- // 4. Make clonedRequestObject’s signal follow this’s signal.
- const ac = new AbortController()
- if (this.signal.aborted) {
- ac.abort(this.signal.reason)
- } else {
- let list = dependentControllerMap.get(this.signal)
- if (list === undefined) {
- list = new Set()
- dependentControllerMap.set(this.signal, list)
- }
- const acRef = new WeakRef(ac)
- list.add(acRef)
- util.addAbortListener(
- ac.signal,
- buildAbort(acRef)
- )
- }
-
- // 4. Return clonedRequestObject.
- return fromInnerRequest(clonedRequest, this.#dispatcher, ac.signal, getHeadersGuard(this.#headers))
- }
-
- [nodeUtil.inspect.custom] (depth, options) {
- if (options.depth === null) {
- options.depth = 2
- }
-
- options.colors ??= true
-
- const properties = {
- method: this.method,
- url: this.url,
- headers: this.headers,
- destination: this.destination,
- referrer: this.referrer,
- referrerPolicy: this.referrerPolicy,
- mode: this.mode,
- credentials: this.credentials,
- cache: this.cache,
- redirect: this.redirect,
- integrity: this.integrity,
- keepalive: this.keepalive,
- isReloadNavigation: this.isReloadNavigation,
- isHistoryNavigation: this.isHistoryNavigation,
- signal: this.signal
- }
-
- return `Request ${nodeUtil.formatWithOptions(options, properties)}`
- }
-
- /**
- * @param {Request} request
- * @param {AbortSignal} newSignal
- */
- static setRequestSignal (request, newSignal) {
- request.#signal = newSignal
- return request
- }
-
- /**
- * @param {Request} request
- */
- static getRequestDispatcher (request) {
- return request.#dispatcher
- }
-
- /**
- * @param {Request} request
- * @param {import('../../dispatcher/dispatcher')} newDispatcher
- */
- static setRequestDispatcher (request, newDispatcher) {
- request.#dispatcher = newDispatcher
- }
-
- /**
- * @param {Request} request
- * @param {Headers} newHeaders
- */
- static setRequestHeaders (request, newHeaders) {
- request.#headers = newHeaders
- }
-
- /**
- * @param {Request} request
- */
- static getRequestState (request) {
- return request.#state
- }
-
- /**
- * @param {Request} request
- * @param {any} newState
- */
- static setRequestState (request, newState) {
- request.#state = newState
- }
-}
-
-const { setRequestSignal, getRequestDispatcher, setRequestDispatcher, setRequestHeaders, getRequestState, setRequestState } = Request
-Reflect.deleteProperty(Request, 'setRequestSignal')
-Reflect.deleteProperty(Request, 'getRequestDispatcher')
-Reflect.deleteProperty(Request, 'setRequestDispatcher')
-Reflect.deleteProperty(Request, 'setRequestHeaders')
-Reflect.deleteProperty(Request, 'getRequestState')
-Reflect.deleteProperty(Request, 'setRequestState')
-
-mixinBody(Request, getRequestState)
-
-// https://fetch.spec.whatwg.org/#requests
-function makeRequest (init) {
- return {
- method: init.method ?? 'GET',
- localURLsOnly: init.localURLsOnly ?? false,
- unsafeRequest: init.unsafeRequest ?? false,
- body: init.body ?? null,
- client: init.client ?? null,
- reservedClient: init.reservedClient ?? null,
- replacesClientId: init.replacesClientId ?? '',
- window: init.window ?? 'client',
- keepalive: init.keepalive ?? false,
- serviceWorkers: init.serviceWorkers ?? 'all',
- initiator: init.initiator ?? '',
- destination: init.destination ?? '',
- priority: init.priority ?? null,
- origin: init.origin ?? 'client',
- policyContainer: init.policyContainer ?? 'client',
- referrer: init.referrer ?? 'client',
- referrerPolicy: init.referrerPolicy ?? '',
- mode: init.mode ?? 'no-cors',
- useCORSPreflightFlag: init.useCORSPreflightFlag ?? false,
- credentials: init.credentials ?? 'same-origin',
- useCredentials: init.useCredentials ?? false,
- cache: init.cache ?? 'default',
- redirect: init.redirect ?? 'follow',
- integrity: init.integrity ?? '',
- cryptoGraphicsNonceMetadata: init.cryptoGraphicsNonceMetadata ?? '',
- parserMetadata: init.parserMetadata ?? '',
- reloadNavigation: init.reloadNavigation ?? false,
- historyNavigation: init.historyNavigation ?? false,
- userActivation: init.userActivation ?? false,
- taintedOrigin: init.taintedOrigin ?? false,
- redirectCount: init.redirectCount ?? 0,
- responseTainting: init.responseTainting ?? 'basic',
- preventNoCacheCacheControlHeaderModification: init.preventNoCacheCacheControlHeaderModification ?? false,
- done: init.done ?? false,
- timingAllowFailed: init.timingAllowFailed ?? false,
- useURLCredentials: init.useURLCredentials ?? undefined,
- traversableForUserPrompts: init.traversableForUserPrompts ?? 'client',
- urlList: init.urlList,
- url: init.urlList[0],
- headersList: init.headersList
- ? new HeadersList(init.headersList)
- : new HeadersList()
- }
-}
-
-// 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 request’s body is non-null, set newRequest’s body to the
- // result of cloning request’s body.
- if (request.body != null) {
- newRequest.body = cloneBody(request.body)
- }
-
- // 3. Return newRequest.
- return newRequest
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#request-create
- * @param {any} innerRequest
- * @param {import('../../dispatcher/agent')} dispatcher
- * @param {AbortSignal} signal
- * @param {'request' | 'immutable' | 'request-no-cors' | 'response' | 'none'} guard
- * @returns {Request}
- */
-function fromInnerRequest (innerRequest, dispatcher, signal, guard) {
- const request = new Request(kConstruct)
- setRequestState(request, innerRequest)
- setRequestDispatcher(request, dispatcher)
- setRequestSignal(request, signal)
- const headers = new Headers(kConstruct)
- setRequestHeaders(request, headers)
- setHeadersList(headers, innerRequest.headersList)
- setHeadersGuard(headers, guard)
- return request
-}
-
-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.is.Request = webidl.util.MakeTypeAssertion(Request)
-
-/**
- * @param {*} V
- * @returns {import('../../../types/fetch').Request|string}
- *
- * @see https://fetch.spec.whatwg.org/#requestinfo
- */
-webidl.converters.RequestInfo = function (V) {
- if (typeof V === 'string') {
- return webidl.converters.USVString(V)
- }
-
- if (webidl.is.Request(V)) {
- return V
- }
-
- return webidl.converters.USVString(V)
-}
-
-/**
- * @param {*} V
- * @returns {import('../../../types/fetch').RequestInit}
- * @see 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,
- 'RequestInit',
- 'signal'
- )
- )
- },
- {
- key: 'window',
- converter: webidl.converters.any
- },
- {
- key: 'duplex',
- converter: webidl.converters.DOMString,
- allowedValues: requestDuplex
- },
- {
- key: 'dispatcher', // undici specific option
- converter: webidl.converters.any
- },
- {
- key: 'priority',
- converter: webidl.converters.DOMString,
- allowedValues: ['high', 'low', 'auto'],
- defaultValue: () => 'auto'
- }
-])
-
-module.exports = {
- Request,
- makeRequest,
- fromInnerRequest,
- cloneRequest,
- getRequestDispatcher,
- getRequestState
-}
diff --git a/vanilla/node_modules/undici/lib/web/fetch/response.js b/vanilla/node_modules/undici/lib/web/fetch/response.js
deleted file mode 100644
index ffb7ce1..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/response.js
+++ /dev/null
@@ -1,641 +0,0 @@
-'use strict'
-
-const { Headers, HeadersList, fill, getHeadersGuard, setHeadersGuard, setHeadersList } = require('./headers')
-const { extractBody, cloneBody, mixinBody, streamRegistry, bodyUnusable } = require('./body')
-const util = require('../../core/util')
-const nodeUtil = require('node:util')
-const { kEnumerableProperty } = util
-const {
- isValidReasonPhrase,
- isCancelled,
- isAborted,
- isErrorLike,
- environmentSettingsObject: relevantRealm
-} = require('./util')
-const {
- redirectStatusSet,
- nullBodyStatus
-} = require('./constants')
-const { webidl } = require('../webidl')
-const { URLSerializer } = require('./data-url')
-const { kConstruct } = require('../../core/symbols')
-const assert = require('node:assert')
-const { isomorphicEncode, serializeJavascriptValueToJSONString } = require('../infra')
-
-const textEncoder = new TextEncoder('utf-8')
-
-// https://fetch.spec.whatwg.org/#response-class
-class Response {
- /** @type {Headers} */
- #headers
-
- #state
-
- // Creates network error Response.
- static error () {
- // The static error() method steps are to return the result of creating a
- // Response object, given a new network error, "immutable", and this’s
- // relevant Realm.
- const responseObject = fromInnerResponse(makeNetworkError(), 'immutable')
-
- return responseObject
- }
-
- // https://fetch.spec.whatwg.org/#dom-response-json
- static json (data, init = undefined) {
- webidl.argumentLengthCheck(arguments, 1, '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 this’s relevant Realm.
- const responseObject = fromInnerResponse(makeResponse({}), 'response')
-
- // 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) {
- webidl.argumentLengthCheck(arguments, 1, '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
- // object’s API base URL.
- // 2. If parsedURL is failure, then throw a TypeError.
- // TODO: base-URL?
- let parsedURL
- try {
- parsedURL = new URL(url, relevantRealm.settingsObject.baseUrl)
- } catch (err) {
- throw 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 this’s relevant Realm.
- const responseObject = fromInnerResponse(makeResponse({}), 'immutable')
-
- // 5. Set responseObject’s response’s status to status.
- responseObject.#state.status = status
-
- // 6. Let value be parsedURL, serialized and isomorphic encoded.
- const value = isomorphicEncode(URLSerializer(parsedURL))
-
- // 7. Append `Location`/value to responseObject’s response’s header list.
- responseObject.#state.headersList.append('location', value, true)
-
- // 8. Return responseObject.
- return responseObject
- }
-
- // https://fetch.spec.whatwg.org/#dom-response
- constructor (body = null, init = undefined) {
- webidl.util.markAsUncloneable(this)
-
- if (body === kConstruct) {
- return
- }
-
- if (body !== null) {
- body = webidl.converters.BodyInit(body, 'Response', 'body')
- }
-
- init = webidl.converters.ResponseInit(init)
-
- // 1. Set this’s response to a new response.
- this.#state = makeResponse({})
-
- // 2. Set this’s headers to a new Headers object with this’s relevant
- // Realm, whose header list is this’s response’s header list and guard
- // is "response".
- this.#headers = new Headers(kConstruct)
- setHeadersGuard(this.#headers, 'response')
- setHeadersList(this.#headers, this.#state.headersList)
-
- // 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 response’s type, e.g., "cors".
- get type () {
- webidl.brandCheck(this, Response)
-
- // The type getter steps are to return this’s response’s type.
- return this.#state.type
- }
-
- // Returns response’s URL, if it has one; otherwise the empty string.
- get url () {
- webidl.brandCheck(this, Response)
-
- const urlList = this.#state.urlList
-
- // The url getter steps are to return the empty string if this’s
- // response’s URL is null; otherwise this’s response’s 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 this’s response’s URL
- // list has more than one item; otherwise false.
- return this.#state.urlList.length > 1
- }
-
- // Returns response’s status.
- get status () {
- webidl.brandCheck(this, Response)
-
- // The status getter steps are to return this’s response’s status.
- return this.#state.status
- }
-
- // Returns whether response’s status is an ok status.
- get ok () {
- webidl.brandCheck(this, Response)
-
- // The ok getter steps are to return true if this’s response’s status is an
- // ok status; otherwise false.
- return this.#state.status >= 200 && this.#state.status <= 299
- }
-
- // Returns response’s status message.
- get statusText () {
- webidl.brandCheck(this, Response)
-
- // The statusText getter steps are to return this’s response’s status
- // message.
- return this.#state.statusText
- }
-
- // Returns response’s headers as Headers.
- get headers () {
- webidl.brandCheck(this, Response)
-
- // The headers getter steps are to return this’s headers.
- return this.#headers
- }
-
- get body () {
- webidl.brandCheck(this, Response)
-
- return this.#state.body ? this.#state.body.stream : null
- }
-
- get bodyUsed () {
- webidl.brandCheck(this, Response)
-
- return !!this.#state.body && util.isDisturbed(this.#state.body.stream)
- }
-
- // Returns a clone of response.
- clone () {
- webidl.brandCheck(this, Response)
-
- // 1. If this is unusable, then throw a TypeError.
- if (bodyUnusable(this.#state)) {
- throw webidl.errors.exception({
- header: 'Response.clone',
- message: 'Body has already been consumed.'
- })
- }
-
- // 2. Let clonedResponse be the result of cloning this’s response.
- const clonedResponse = cloneResponse(this.#state)
-
- // Note: To re-register because of a new stream.
- // Don't set finalizers other than for fetch responses.
- if (this.#state.urlList.length !== 0 && this.#state.body?.stream) {
- streamRegistry.register(this, new WeakRef(this.#state.body.stream))
- }
-
- // 3. Return the result of creating a Response object, given
- // clonedResponse, this’s headers’s guard, and this’s relevant Realm.
- return fromInnerResponse(clonedResponse, getHeadersGuard(this.#headers))
- }
-
- [nodeUtil.inspect.custom] (depth, options) {
- if (options.depth === null) {
- options.depth = 2
- }
-
- options.colors ??= true
-
- const properties = {
- status: this.status,
- statusText: this.statusText,
- headers: this.headers,
- body: this.body,
- bodyUsed: this.bodyUsed,
- ok: this.ok,
- redirected: this.redirected,
- type: this.type,
- url: this.url
- }
-
- return `Response ${nodeUtil.formatWithOptions(options, properties)}`
- }
-
- /**
- * @param {Response} response
- */
- static getResponseHeaders (response) {
- return response.#headers
- }
-
- /**
- * @param {Response} response
- * @param {Headers} newHeaders
- */
- static setResponseHeaders (response, newHeaders) {
- response.#headers = newHeaders
- }
-
- /**
- * @param {Response} response
- */
- static getResponseState (response) {
- return response.#state
- }
-
- /**
- * @param {Response} response
- * @param {any} newState
- */
- static setResponseState (response, newState) {
- response.#state = newState
- }
-}
-
-const { getResponseHeaders, setResponseHeaders, getResponseState, setResponseState } = Response
-Reflect.deleteProperty(Response, 'getResponseHeaders')
-Reflect.deleteProperty(Response, 'setResponseHeaders')
-Reflect.deleteProperty(Response, 'getResponseState')
-Reflect.deleteProperty(Response, 'setResponseState')
-
-mixinBody(Response, getResponseState)
-
-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 response’s
- // 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 response’s body is non-null, then set newResponse’s body to the
- // result of cloning response’s 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'
- })
-}
-
-// @see https://fetch.spec.whatwg.org/#concept-network-error
-function isNetworkError (response) {
- return (
- // A network error is a response whose type is "error",
- response.type === 'error' &&
- // status is 0
- response.status === 0
- )
-}
-
-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 request’s 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 response’s 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 response’s header
- // list whose name is not a CORS-safelisted response-header name, given
- // internal response’s 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: [],
- 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 response’s response’s status to init["status"].
- if ('status' in init && init.status != null) {
- getResponseState(response).status = init.status
- }
-
- // 4. Set response’s response’s status message to init["statusText"].
- if ('statusText' in init && init.statusText != null) {
- getResponseState(response).statusText = init.statusText
- }
-
- // 5. If init["headers"] exists, then fill response’s headers with init["headers"].
- if ('headers' in init && init.headers != null) {
- fill(getResponseHeaders(response), 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.
- getResponseState(response).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 && !getResponseState(response).headersList.contains('content-type', true)) {
- getResponseState(response).headersList.append('content-type', body.type, true)
- }
- }
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#response-create
- * @param {any} innerResponse
- * @param {'request' | 'immutable' | 'request-no-cors' | 'response' | 'none'} guard
- * @returns {Response}
- */
-function fromInnerResponse (innerResponse, guard) {
- const response = new Response(kConstruct)
- setResponseState(response, innerResponse)
- const headers = new Headers(kConstruct)
- setResponseHeaders(response, headers)
- setHeadersList(headers, innerResponse.headersList)
- setHeadersGuard(headers, guard)
-
- // Note: If innerResponse's urlList contains a URL, it is a fetch response.
- if (innerResponse.urlList.length !== 0 && innerResponse.body?.stream) {
- // If the target (response) is reclaimed, the cleanup callback may be called at some point with
- // the held value provided for it (innerResponse.body.stream). The held value can be any value:
- // a primitive or an object, even undefined. If the held value is an object, the registry keeps
- // a strong reference to it (so it can pass it to the cleanup callback later). Reworded from
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry
- streamRegistry.register(response, new WeakRef(innerResponse.body.stream))
- }
-
- return response
-}
-
-// https://fetch.spec.whatwg.org/#typedefdef-xmlhttprequestbodyinit
-webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {
- if (typeof V === 'string') {
- return webidl.converters.USVString(V, prefix, name)
- }
-
- if (webidl.is.Blob(V)) {
- return V
- }
-
- if (webidl.is.BufferSource(V)) {
- return V
- }
-
- if (webidl.is.FormData(V)) {
- return V
- }
-
- if (webidl.is.URLSearchParams(V)) {
- return V
- }
-
- return webidl.converters.DOMString(V, prefix, name)
-}
-
-// https://fetch.spec.whatwg.org/#bodyinit
-webidl.converters.BodyInit = function (V, prefix, argument) {
- if (webidl.is.ReadableStream(V)) {
- return 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, prefix, argument)
-}
-
-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
- }
-])
-
-webidl.is.Response = webidl.util.MakeTypeAssertion(Response)
-
-module.exports = {
- isNetworkError,
- makeNetworkError,
- makeResponse,
- makeAppropriateNetworkError,
- filterResponse,
- Response,
- cloneResponse,
- fromInnerResponse,
- getResponseState
-}
diff --git a/vanilla/node_modules/undici/lib/web/fetch/util.js b/vanilla/node_modules/undici/lib/web/fetch/util.js
deleted file mode 100644
index fe63cb3..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/util.js
+++ /dev/null
@@ -1,1520 +0,0 @@
-'use strict'
-
-const { Transform } = require('node:stream')
-const zlib = require('node:zlib')
-const { redirectStatusSet, referrerPolicyTokens, badPortsSet } = require('./constants')
-const { getGlobalOrigin } = require('./global')
-const { collectAnHTTPQuotedString, parseMIMEType } = require('./data-url')
-const { performance } = require('node:perf_hooks')
-const { ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require('../../core/util')
-const assert = require('node:assert')
-const { isUint8Array } = require('node:util/types')
-const { webidl } = require('../webidl')
-const { isomorphicEncode, collectASequenceOfCodePoints, removeChars } = require('../infra')
-
-function responseURL (response) {
- // https://fetch.spec.whatwg.org/#responses
- // A response has an associated URL. It is a pointer to the last URL
- // in response’s URL list and null if response’s URL list is empty.
- const urlList = response.urlList
- const length = urlList.length
- return length === 0 ? null : urlList[length - 1].toString()
-}
-
-// https://fetch.spec.whatwg.org/#concept-response-location-url
-function responseLocationURL (response, requestFragment) {
- // 1. If response’s status is not a redirect status, then return null.
- if (!redirectStatusSet.has(response.status)) {
- return null
- }
-
- // 2. Let location be the result of extracting header list values given
- // `Location` and response’s header list.
- let location = response.headersList.get('location', true)
-
- // 3. If location is a header value, then set location to the result of
- // parsing location with response’s URL.
- if (location !== null && isValidHeaderValue(location)) {
- if (!isValidEncodedURL(location)) {
- // Some websites respond location header in UTF-8 form without encoding them as ASCII
- // and major browsers redirect them to correctly UTF-8 encoded addresses.
- // Here, we handle that behavior in the same way.
- location = normalizeBinaryStringToUtf8(location)
- }
- location = new URL(location, responseURL(response))
- }
-
- // 4. If location is a URL whose fragment is null, then set location’s
- // fragment to requestFragment.
- if (location && !location.hash) {
- location.hash = requestFragment
- }
-
- // 5. Return location.
- return location
-}
-
-/**
- * @see https://www.rfc-editor.org/rfc/rfc1738#section-2.2
- * @param {string} url
- * @returns {boolean}
- */
-function isValidEncodedURL (url) {
- for (let i = 0; i < url.length; ++i) {
- const code = url.charCodeAt(i)
-
- if (
- code > 0x7E || // Non-US-ASCII + DEL
- code < 0x20 // Control characters NUL - US
- ) {
- return false
- }
- }
- return true
-}
-
-/**
- * If string contains non-ASCII characters, assumes it's UTF-8 encoded and decodes it.
- * Since UTF-8 is a superset of ASCII, this will work for ASCII strings as well.
- * @param {string} value
- * @returns {string}
- */
-function normalizeBinaryStringToUtf8 (value) {
- return Buffer.from(value, 'binary').toString('utf8')
-}
-
-/** @returns {URL} */
-function requestCurrentURL (request) {
- return request.urlList[request.urlList.length - 1]
-}
-
-function requestBadPort (request) {
- // 1. Let url be request’s current URL.
- const url = requestCurrentURL(request)
-
- // 2. If url’s scheme is an HTTP(S) scheme and url’s port is a bad port,
- // then return blocked.
- if (urlIsHttpHttpsScheme(url) && badPortsSet.has(url.port)) {
- return 'blocked'
- }
-
- // 3. Return allowed.
- return 'allowed'
-}
-
-function isErrorLike (object) {
- return object instanceof Error || (
- object?.constructor?.name === 'Error' ||
- object?.constructor?.name === 'DOMException'
- )
-}
-
-// Check whether |statusText| is a ByteString and
-// matches the Reason-Phrase token production.
-// RFC 2616: https://tools.ietf.org/html/rfc2616
-// RFC 7230: https://tools.ietf.org/html/rfc7230
-// "reason-phrase = *( HTAB / SP / VCHAR / obs-text )"
-// https://github.com/chromium/chromium/blob/94.0.4604.1/third_party/blink/renderer/core/fetch/response.cc#L116
-function isValidReasonPhrase (statusText) {
- for (let i = 0; i < statusText.length; ++i) {
- const c = statusText.charCodeAt(i)
- if (
- !(
- (
- c === 0x09 || // HTAB
- (c >= 0x20 && c <= 0x7e) || // SP / VCHAR
- (c >= 0x80 && c <= 0xff)
- ) // obs-text
- )
- ) {
- return false
- }
- }
- return true
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#header-name
- * @param {string} potentialValue
- */
-const isValidHeaderName = isValidHTTPToken
-
-/**
- * @see https://fetch.spec.whatwg.org/#header-value
- * @param {string} potentialValue
- */
-function isValidHeaderValue (potentialValue) {
- // - Has no leading or trailing HTTP tab or space bytes.
- // - Contains no 0x00 (NUL) or HTTP newline bytes.
- return (
- potentialValue[0] === '\t' ||
- potentialValue[0] === ' ' ||
- potentialValue[potentialValue.length - 1] === '\t' ||
- potentialValue[potentialValue.length - 1] === ' ' ||
- potentialValue.includes('\n') ||
- potentialValue.includes('\r') ||
- potentialValue.includes('\0')
- ) === false
-}
-
-/**
- * Parse a referrer policy from a Referrer-Policy header
- * @see https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header
- */
-function parseReferrerPolicy (actualResponse) {
- // 1. Let policy-tokens be the result of extracting header list values given `Referrer-Policy` and response’s header list.
- const policyHeader = (actualResponse.headersList.get('referrer-policy', true) ?? '').split(',')
-
- // 2. Let policy be the empty string.
- let policy = ''
-
- // 3. For each token in policy-tokens, if token is a referrer policy and token is not the empty string, then set policy to token.
-
- // Note: As the referrer-policy can contain multiple policies
- // separated by comma, we need to loop through all of them
- // and pick the first valid one.
- // Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#specify_a_fallback_policy
- if (policyHeader.length) {
- // The right-most policy takes precedence.
- // The left-most policy is the fallback.
- for (let i = policyHeader.length; i !== 0; i--) {
- const token = policyHeader[i - 1].trim()
- if (referrerPolicyTokens.has(token)) {
- policy = token
- break
- }
- }
- }
-
- // 4. Return policy.
- return policy
-}
-
-/**
- * Given a request request and a response actualResponse, this algorithm
- * updates request’s referrer policy according to the Referrer-Policy
- * header (if any) in actualResponse.
- * @see https://w3c.github.io/webappsec-referrer-policy/#set-requests-referrer-policy-on-redirect
- * @param {import('./request').Request} request
- * @param {import('./response').Response} actualResponse
- */
-function setRequestReferrerPolicyOnRedirect (request, actualResponse) {
- // 1. Let policy be the result of executing § 8.1 Parse a referrer policy
- // from a Referrer-Policy header on actualResponse.
- const policy = parseReferrerPolicy(actualResponse)
-
- // 2. If policy is not the empty string, then set request’s referrer policy to policy.
- if (policy !== '') {
- request.referrerPolicy = policy
- }
-}
-
-// https://fetch.spec.whatwg.org/#cross-origin-resource-policy-check
-function crossOriginResourcePolicyCheck () {
- // TODO
- return 'allowed'
-}
-
-// https://fetch.spec.whatwg.org/#concept-cors-check
-function corsCheck () {
- // TODO
- return 'success'
-}
-
-// https://fetch.spec.whatwg.org/#concept-tao-check
-function TAOCheck () {
- // TODO
- return 'success'
-}
-
-function appendFetchMetadata (httpRequest) {
- // https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-dest-header
- // TODO
-
- // https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-mode-header
-
- // 1. Assert: r’s url is a potentially trustworthy URL.
- // TODO
-
- // 2. Let header be a Structured Header whose value is a token.
- let header = null
-
- // 3. Set header’s value to r’s mode.
- header = httpRequest.mode
-
- // 4. Set a structured field value `Sec-Fetch-Mode`/header in r’s header list.
- httpRequest.headersList.set('sec-fetch-mode', header, true)
-
- // https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-site-header
- // TODO
-
- // https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-user-header
- // TODO
-}
-
-// https://fetch.spec.whatwg.org/#append-a-request-origin-header
-function appendRequestOriginHeader (request) {
- // 1. Let serializedOrigin be the result of byte-serializing a request origin
- // with request.
- // TODO: implement "byte-serializing a request origin"
- let serializedOrigin = request.origin
-
- // - "'client' is changed to an origin during fetching."
- // This doesn't happen in undici (in most cases) because undici, by default,
- // has no concept of origin.
- // - request.origin can also be set to request.client.origin (client being
- // an environment settings object), which is undefined without using
- // setGlobalOrigin.
- if (serializedOrigin === 'client' || serializedOrigin === undefined) {
- return
- }
-
- // 2. If request’s response tainting is "cors" or request’s mode is "websocket",
- // then append (`Origin`, serializedOrigin) to request’s header list.
- // 3. Otherwise, if request’s method is neither `GET` nor `HEAD`, then:
- if (request.responseTainting === 'cors' || request.mode === 'websocket') {
- request.headersList.append('origin', serializedOrigin, true)
- } else if (request.method !== 'GET' && request.method !== 'HEAD') {
- // 1. Switch on request’s referrer policy:
- switch (request.referrerPolicy) {
- case 'no-referrer':
- // Set serializedOrigin to `null`.
- serializedOrigin = null
- break
- case 'no-referrer-when-downgrade':
- case 'strict-origin':
- case 'strict-origin-when-cross-origin':
- // If request’s origin is a tuple origin, its scheme is "https", and
- // request’s current URL’s scheme is not "https", then set
- // serializedOrigin to `null`.
- if (request.origin && urlHasHttpsScheme(request.origin) && !urlHasHttpsScheme(requestCurrentURL(request))) {
- serializedOrigin = null
- }
- break
- case 'same-origin':
- // If request’s origin is not same origin with request’s current URL’s
- // origin, then set serializedOrigin to `null`.
- if (!sameOrigin(request, requestCurrentURL(request))) {
- serializedOrigin = null
- }
- break
- default:
- // Do nothing.
- }
-
- // 2. Append (`Origin`, serializedOrigin) to request’s header list.
- request.headersList.append('origin', serializedOrigin, true)
- }
-}
-
-// https://w3c.github.io/hr-time/#dfn-coarsen-time
-function coarsenTime (timestamp, crossOriginIsolatedCapability) {
- // TODO
- return timestamp
-}
-
-// https://fetch.spec.whatwg.org/#clamp-and-coarsen-connection-timing-info
-function clampAndCoarsenConnectionTimingInfo (connectionTimingInfo, defaultStartTime, crossOriginIsolatedCapability) {
- if (!connectionTimingInfo?.startTime || connectionTimingInfo.startTime < defaultStartTime) {
- return {
- domainLookupStartTime: defaultStartTime,
- domainLookupEndTime: defaultStartTime,
- connectionStartTime: defaultStartTime,
- connectionEndTime: defaultStartTime,
- secureConnectionStartTime: defaultStartTime,
- ALPNNegotiatedProtocol: connectionTimingInfo?.ALPNNegotiatedProtocol
- }
- }
-
- return {
- domainLookupStartTime: coarsenTime(connectionTimingInfo.domainLookupStartTime, crossOriginIsolatedCapability),
- domainLookupEndTime: coarsenTime(connectionTimingInfo.domainLookupEndTime, crossOriginIsolatedCapability),
- connectionStartTime: coarsenTime(connectionTimingInfo.connectionStartTime, crossOriginIsolatedCapability),
- connectionEndTime: coarsenTime(connectionTimingInfo.connectionEndTime, crossOriginIsolatedCapability),
- secureConnectionStartTime: coarsenTime(connectionTimingInfo.secureConnectionStartTime, crossOriginIsolatedCapability),
- ALPNNegotiatedProtocol: connectionTimingInfo.ALPNNegotiatedProtocol
- }
-}
-
-// https://w3c.github.io/hr-time/#dfn-coarsened-shared-current-time
-function coarsenedSharedCurrentTime (crossOriginIsolatedCapability) {
- return coarsenTime(performance.now(), crossOriginIsolatedCapability)
-}
-
-// https://fetch.spec.whatwg.org/#create-an-opaque-timing-info
-function createOpaqueTimingInfo (timingInfo) {
- return {
- startTime: timingInfo.startTime ?? 0,
- redirectStartTime: 0,
- redirectEndTime: 0,
- postRedirectStartTime: timingInfo.startTime ?? 0,
- finalServiceWorkerStartTime: 0,
- finalNetworkResponseStartTime: 0,
- finalNetworkRequestStartTime: 0,
- endTime: 0,
- encodedBodySize: 0,
- decodedBodySize: 0,
- finalConnectionTimingInfo: null
- }
-}
-
-// https://html.spec.whatwg.org/multipage/origin.html#policy-container
-function makePolicyContainer () {
- // Note: the fetch spec doesn't make use of embedder policy or CSP list
- return {
- referrerPolicy: 'strict-origin-when-cross-origin'
- }
-}
-
-// https://html.spec.whatwg.org/multipage/origin.html#clone-a-policy-container
-function clonePolicyContainer (policyContainer) {
- return {
- referrerPolicy: policyContainer.referrerPolicy
- }
-}
-
-/**
- * Determine request’s Referrer
- *
- * @see https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
- */
-function determineRequestsReferrer (request) {
- // Given a request request, we can determine the correct referrer information
- // to send by examining its referrer policy as detailed in the following
- // steps, which return either no referrer or a URL:
-
- // 1. Let policy be request's referrer policy.
- const policy = request.referrerPolicy
-
- // Note: policy cannot (shouldn't) be null or an empty string.
- assert(policy)
-
- // 2. Let environment be request’s client.
-
- let referrerSource = null
-
- // 3. Switch on request’s referrer:
-
- // "client"
- if (request.referrer === 'client') {
- // Note: node isn't a browser and doesn't implement document/iframes,
- // so we bypass this step and replace it with our own.
-
- const globalOrigin = getGlobalOrigin()
-
- if (!globalOrigin || globalOrigin.origin === 'null') {
- return 'no-referrer'
- }
-
- // Note: we need to clone it as it's mutated
- referrerSource = new URL(globalOrigin)
- // a URL
- } else if (webidl.is.URL(request.referrer)) {
- // Let referrerSource be request’s referrer.
- referrerSource = request.referrer
- }
-
- // 4. Let request’s referrerURL be the result of stripping referrerSource for
- // use as a referrer.
- let referrerURL = stripURLForReferrer(referrerSource)
-
- // 5. Let referrerOrigin be the result of stripping referrerSource for use as
- // a referrer, with the origin-only flag set to true.
- const referrerOrigin = stripURLForReferrer(referrerSource, true)
-
- // 6. If the result of serializing referrerURL is a string whose length is
- // greater than 4096, set referrerURL to referrerOrigin.
- if (referrerURL.toString().length > 4096) {
- referrerURL = referrerOrigin
- }
-
- // 7. The user agent MAY alter referrerURL or referrerOrigin at this point
- // to enforce arbitrary policy considerations in the interests of minimizing
- // data leakage. For example, the user agent could strip the URL down to an
- // origin, modify its host, replace it with an empty string, etc.
-
- // 8. Execute the switch statements corresponding to the value of policy:
- switch (policy) {
- case 'no-referrer':
- // Return no referrer
- return 'no-referrer'
- case 'origin':
- // Return referrerOrigin
- if (referrerOrigin != null) {
- return referrerOrigin
- }
- return stripURLForReferrer(referrerSource, true)
- case 'unsafe-url':
- // Return referrerURL.
- return referrerURL
- case 'strict-origin': {
- const currentURL = requestCurrentURL(request)
-
- // 1. If referrerURL is a potentially trustworthy URL and request’s
- // current URL is not a potentially trustworthy URL, then return no
- // referrer.
- if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) {
- return 'no-referrer'
- }
- // 2. Return referrerOrigin
- return referrerOrigin
- }
- case 'strict-origin-when-cross-origin': {
- const currentURL = requestCurrentURL(request)
-
- // 1. If the origin of referrerURL and the origin of request’s current
- // URL are the same, then return referrerURL.
- if (sameOrigin(referrerURL, currentURL)) {
- return referrerURL
- }
-
- // 2. If referrerURL is a potentially trustworthy URL and request’s
- // current URL is not a potentially trustworthy URL, then return no
- // referrer.
- if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) {
- return 'no-referrer'
- }
-
- // 3. Return referrerOrigin.
- return referrerOrigin
- }
- case 'same-origin':
- // 1. If the origin of referrerURL and the origin of request’s current
- // URL are the same, then return referrerURL.
- if (sameOrigin(request, referrerURL)) {
- return referrerURL
- }
- // 2. Return no referrer.
- return 'no-referrer'
- case 'origin-when-cross-origin':
- // 1. If the origin of referrerURL and the origin of request’s current
- // URL are the same, then return referrerURL.
- if (sameOrigin(request, referrerURL)) {
- return referrerURL
- }
- // 2. Return referrerOrigin.
- return referrerOrigin
- case 'no-referrer-when-downgrade': {
- const currentURL = requestCurrentURL(request)
-
- // 1. If referrerURL is a potentially trustworthy URL and request’s
- // current URL is not a potentially trustworthy URL, then return no
- // referrer.
- if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) {
- return 'no-referrer'
- }
- // 2. Return referrerURL.
- return referrerURL
- }
- }
-}
-
-/**
- * Certain portions of URLs must not be included when sending a URL as the
- * value of a `Referer` header: a URLs fragment, username, and password
- * components must be stripped from the URL before it’s sent out. This
- * algorithm accepts a origin-only flag, which defaults to false. If set to
- * true, the algorithm will additionally remove the URL’s path and query
- * components, leaving only the scheme, host, and port.
- *
- * @see https://w3c.github.io/webappsec-referrer-policy/#strip-url
- * @param {URL} url
- * @param {boolean} [originOnly=false]
- */
-function stripURLForReferrer (url, originOnly = false) {
- // 1. Assert: url is a URL.
- assert(webidl.is.URL(url))
-
- // Note: Create a new URL instance to avoid mutating the original URL.
- url = new URL(url)
-
- // 2. If url’s scheme is a local scheme, then return no referrer.
- if (urlIsLocal(url)) {
- return 'no-referrer'
- }
-
- // 3. Set url’s username to the empty string.
- url.username = ''
-
- // 4. Set url’s password to the empty string.
- url.password = ''
-
- // 5. Set url’s fragment to null.
- url.hash = ''
-
- // 6. If the origin-only flag is true, then:
- if (originOnly === true) {
- // 1. Set url’s path to « the empty string ».
- url.pathname = ''
-
- // 2. Set url’s query to null.
- url.search = ''
- }
-
- // 7. Return url.
- return url
-}
-
-const isPotentialleTrustworthyIPv4 = RegExp.prototype.test
- .bind(/^127\.(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){2}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)$/)
-
-const isPotentiallyTrustworthyIPv6 = RegExp.prototype.test
- .bind(/^(?:(?:0{1,4}:){7}|(?:0{1,4}:){1,6}:|::)0{0,3}1$/)
-
-/**
- * Check if host matches one of the CIDR notations 127.0.0.0/8 or ::1/128.
- *
- * @param {string} origin
- * @returns {boolean}
- */
-function isOriginIPPotentiallyTrustworthy (origin) {
- // IPv6
- if (origin.includes(':')) {
- // Remove brackets from IPv6 addresses
- if (origin[0] === '[' && origin[origin.length - 1] === ']') {
- origin = origin.slice(1, -1)
- }
- return isPotentiallyTrustworthyIPv6(origin)
- }
-
- // IPv4
- return isPotentialleTrustworthyIPv4(origin)
-}
-
-/**
- * A potentially trustworthy origin is one which a user agent can generally
- * trust as delivering data securely.
- *
- * Return value `true` means `Potentially Trustworthy`.
- * Return value `false` means `Not Trustworthy`.
- *
- * @see https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
- * @param {string} origin
- * @returns {boolean}
- */
-function isOriginPotentiallyTrustworthy (origin) {
- // 1. If origin is an opaque origin, return "Not Trustworthy".
- if (origin == null || origin === 'null') {
- return false
- }
-
- // 2. Assert: origin is a tuple origin.
- origin = new URL(origin)
-
- // 3. If origin’s scheme is either "https" or "wss",
- // return "Potentially Trustworthy".
- if (origin.protocol === 'https:' || origin.protocol === 'wss:') {
- return true
- }
-
- // 4. If origin’s host matches one of the CIDR notations 127.0.0.0/8 or
- // ::1/128 [RFC4632], return "Potentially Trustworthy".
- if (isOriginIPPotentiallyTrustworthy(origin.hostname)) {
- return true
- }
-
- // 5. If the user agent conforms to the name resolution rules in
- // [let-localhost-be-localhost] and one of the following is true:
-
- // origin’s host is "localhost" or "localhost."
- if (origin.hostname === 'localhost' || origin.hostname === 'localhost.') {
- return true
- }
-
- // origin’s host ends with ".localhost" or ".localhost."
- if (origin.hostname.endsWith('.localhost') || origin.hostname.endsWith('.localhost.')) {
- return true
- }
-
- // 6. If origin’s scheme is "file", return "Potentially Trustworthy".
- if (origin.protocol === 'file:') {
- return true
- }
-
- // 7. If origin’s scheme component is one which the user agent considers to
- // be authenticated, return "Potentially Trustworthy".
-
- // 8. If origin has been configured as a trustworthy origin, return
- // "Potentially Trustworthy".
-
- // 9. Return "Not Trustworthy".
- return false
-}
-
-/**
- * A potentially trustworthy URL is one which either inherits context from its
- * creator (about:blank, about:srcdoc, data) or one whose origin is a
- * potentially trustworthy origin.
- *
- * Return value `true` means `Potentially Trustworthy`.
- * Return value `false` means `Not Trustworthy`.
- *
- * @see https://www.w3.org/TR/secure-contexts/#is-url-trustworthy
- * @param {URL} url
- * @returns {boolean}
- */
-function isURLPotentiallyTrustworthy (url) {
- // Given a URL record (url), the following algorithm returns "Potentially
- // Trustworthy" or "Not Trustworthy" as appropriate:
- if (!webidl.is.URL(url)) {
- return false
- }
-
- // 1. If url is "about:blank" or "about:srcdoc",
- // return "Potentially Trustworthy".
- if (url.href === 'about:blank' || url.href === 'about:srcdoc') {
- return true
- }
-
- // 2. If url’s scheme is "data", return "Potentially Trustworthy".
- if (url.protocol === 'data:') return true
-
- // Note: The origin of blob: URLs is the origin of the context in which they
- // were created. Therefore, blobs created in a trustworthy origin will
- // themselves be potentially trustworthy.
- if (url.protocol === 'blob:') return true
-
- // 3. Return the result of executing § 3.1 Is origin potentially trustworthy?
- // on url’s origin.
- return isOriginPotentiallyTrustworthy(url.origin)
-}
-
-// https://w3c.github.io/webappsec-upgrade-insecure-requests/#upgrade-request
-function tryUpgradeRequestToAPotentiallyTrustworthyURL (request) {
- // TODO
-}
-
-/**
- * @link {https://html.spec.whatwg.org/multipage/origin.html#same-origin}
- * @param {URL} A
- * @param {URL} B
- */
-function sameOrigin (A, B) {
- // 1. If A and B are the same opaque origin, then return true.
- if (A.origin === B.origin && A.origin === 'null') {
- return true
- }
-
- // 2. If A and B are both tuple origins and their schemes,
- // hosts, and port are identical, then return true.
- if (A.protocol === B.protocol && A.hostname === B.hostname && A.port === B.port) {
- return true
- }
-
- // 3. Return false.
- return false
-}
-
-function isAborted (fetchParams) {
- return fetchParams.controller.state === 'aborted'
-}
-
-function isCancelled (fetchParams) {
- return fetchParams.controller.state === 'aborted' ||
- fetchParams.controller.state === 'terminated'
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#concept-method-normalize
- * @param {string} method
- */
-function normalizeMethod (method) {
- return normalizedMethodRecordsBase[method.toLowerCase()] ?? method
-}
-
-// https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object
-const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))
-
-/**
- * @see https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
- * @param {string} name name of the instance
- * @param {((target: any) => any)} kInternalIterator
- * @param {string | number} [keyIndex]
- * @param {string | number} [valueIndex]
- */
-function createIterator (name, kInternalIterator, keyIndex = 0, valueIndex = 1) {
- class FastIterableIterator {
- /** @type {any} */
- #target
- /** @type {'key' | 'value' | 'key+value'} */
- #kind
- /** @type {number} */
- #index
-
- /**
- * @see https://webidl.spec.whatwg.org/#dfn-default-iterator-object
- * @param {unknown} target
- * @param {'key' | 'value' | 'key+value'} kind
- */
- constructor (target, kind) {
- this.#target = target
- this.#kind = kind
- this.#index = 0
- }
-
- next () {
- // 1. Let interface be the interface for which the iterator prototype object exists.
- // 2. Let thisValue be the this value.
- // 3. Let object be ? ToObject(thisValue).
- // 4. If object is a platform object, then perform a security
- // check, passing:
- // 5. If object is not a default iterator object for interface,
- // then throw a TypeError.
- if (typeof this !== 'object' || this === null || !(#target in this)) {
- throw new TypeError(
- `'next' called on an object that does not implement interface ${name} Iterator.`
- )
- }
-
- // 6. Let index be object’s index.
- // 7. Let kind be object’s kind.
- // 8. Let values be object’s target's value pairs to iterate over.
- const index = this.#index
- const values = kInternalIterator(this.#target)
-
- // 9. Let len be the length of values.
- const len = values.length
-
- // 10. If index is greater than or equal to len, then return
- // CreateIterResultObject(undefined, true).
- if (index >= len) {
- return {
- value: undefined,
- done: true
- }
- }
-
- // 11. Let pair be the entry in values at index index.
- const { [keyIndex]: key, [valueIndex]: value } = values[index]
-
- // 12. Set object’s index to index + 1.
- this.#index = index + 1
-
- // 13. Return the iterator result for pair and kind.
-
- // https://webidl.spec.whatwg.org/#iterator-result
-
- // 1. Let result be a value determined by the value of kind:
- let result
- switch (this.#kind) {
- case 'key':
- // 1. Let idlKey be pair’s key.
- // 2. Let key be the result of converting idlKey to an
- // ECMAScript value.
- // 3. result is key.
- result = key
- break
- case 'value':
- // 1. Let idlValue be pair’s value.
- // 2. Let value be the result of converting idlValue to
- // an ECMAScript value.
- // 3. result is value.
- result = value
- break
- case 'key+value':
- // 1. Let idlKey be pair’s key.
- // 2. Let idlValue be pair’s value.
- // 3. Let key be the result of converting idlKey to an
- // ECMAScript value.
- // 4. Let value be the result of converting idlValue to
- // an ECMAScript value.
- // 5. Let array be ! ArrayCreate(2).
- // 6. Call ! CreateDataProperty(array, "0", key).
- // 7. Call ! CreateDataProperty(array, "1", value).
- // 8. result is array.
- result = [key, value]
- break
- }
-
- // 2. Return CreateIterResultObject(result, false).
- return {
- value: result,
- done: false
- }
- }
- }
-
- // https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
- // @ts-ignore
- delete FastIterableIterator.prototype.constructor
-
- Object.setPrototypeOf(FastIterableIterator.prototype, esIteratorPrototype)
-
- Object.defineProperties(FastIterableIterator.prototype, {
- [Symbol.toStringTag]: {
- writable: false,
- enumerable: false,
- configurable: true,
- value: `${name} Iterator`
- },
- next: { writable: true, enumerable: true, configurable: true }
- })
-
- /**
- * @param {unknown} target
- * @param {'key' | 'value' | 'key+value'} kind
- * @returns {IterableIterator<any>}
- */
- return function (target, kind) {
- return new FastIterableIterator(target, kind)
- }
-}
-
-/**
- * @see https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
- * @param {string} name name of the instance
- * @param {any} object class
- * @param {(target: any) => any} kInternalIterator
- * @param {string | number} [keyIndex]
- * @param {string | number} [valueIndex]
- */
-function iteratorMixin (name, object, kInternalIterator, keyIndex = 0, valueIndex = 1) {
- const makeIterator = createIterator(name, kInternalIterator, keyIndex, valueIndex)
-
- const properties = {
- keys: {
- writable: true,
- enumerable: true,
- configurable: true,
- value: function keys () {
- webidl.brandCheck(this, object)
- return makeIterator(this, 'key')
- }
- },
- values: {
- writable: true,
- enumerable: true,
- configurable: true,
- value: function values () {
- webidl.brandCheck(this, object)
- return makeIterator(this, 'value')
- }
- },
- entries: {
- writable: true,
- enumerable: true,
- configurable: true,
- value: function entries () {
- webidl.brandCheck(this, object)
- return makeIterator(this, 'key+value')
- }
- },
- forEach: {
- writable: true,
- enumerable: true,
- configurable: true,
- value: function forEach (callbackfn, thisArg = globalThis) {
- webidl.brandCheck(this, object)
- webidl.argumentLengthCheck(arguments, 1, `${name}.forEach`)
- if (typeof callbackfn !== 'function') {
- throw new TypeError(
- `Failed to execute 'forEach' on '${name}': parameter 1 is not of type 'Function'.`
- )
- }
- for (const { 0: key, 1: value } of makeIterator(this, 'key+value')) {
- callbackfn.call(thisArg, value, key, this)
- }
- }
- }
- }
-
- return Object.defineProperties(object.prototype, {
- ...properties,
- [Symbol.iterator]: {
- writable: true,
- enumerable: false,
- configurable: true,
- value: properties.entries.value
- }
- })
-}
-
-/**
- * @param {import('./body').ExtractBodyResult} body
- * @param {(bytes: Uint8Array) => void} processBody
- * @param {(error: Error) => void} processBodyError
- * @returns {void}
- *
- * @see https://fetch.spec.whatwg.org/#body-fully-read
- */
-function fullyReadBody (body, processBody, processBodyError) {
- // 1. If taskDestination is null, then set taskDestination to
- // the result of starting a new parallel queue.
-
- // 2. Let successSteps given a byte sequence bytes be to queue a
- // fetch task to run processBody given bytes, with taskDestination.
- const successSteps = processBody
-
- // 3. Let errorSteps be to queue a fetch task to run processBodyError,
- // with taskDestination.
- const errorSteps = processBodyError
-
- try {
- // 4. Let reader be the result of getting a reader for body’s stream.
- // If that threw an exception, then run errorSteps with that
- // exception and return.
- const reader = body.stream.getReader()
-
- // 5. Read all bytes from reader, given successSteps and errorSteps.
- readAllBytes(reader, successSteps, errorSteps)
- } catch (e) {
- errorSteps(e)
- }
-}
-
-/**
- * @param {ReadableStreamController<Uint8Array>} controller
- */
-function readableStreamClose (controller) {
- try {
- controller.close()
- controller.byobRequest?.respond(0)
- } catch (err) {
- // TODO: add comment explaining why this error occurs.
- if (!err.message.includes('Controller is already closed') && !err.message.includes('ReadableStream is already closed')) {
- throw err
- }
- }
-}
-
-/**
- * @see https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-all-bytes
- * @see https://streams.spec.whatwg.org/#read-loop
- * @param {ReadableStream<Uint8Array<ArrayBuffer>>} reader
- * @param {(bytes: Uint8Array) => void} successSteps
- * @param {(error: Error) => void} failureSteps
- * @returns {Promise<void>}
- */
-async function readAllBytes (reader, successSteps, failureSteps) {
- try {
- const bytes = []
- let byteLength = 0
-
- do {
- const { done, value: chunk } = await reader.read()
-
- if (done) {
- // 1. Call successSteps with bytes.
- successSteps(Buffer.concat(bytes, byteLength))
- return
- }
-
- // 1. If chunk is not a Uint8Array object, call failureSteps
- // with a TypeError and abort these steps.
- if (!isUint8Array(chunk)) {
- failureSteps(new TypeError('Received non-Uint8Array chunk'))
- return
- }
-
- // 2. Append the bytes represented by chunk to bytes.
- bytes.push(chunk)
- byteLength += chunk.length
-
- // 3. Read-loop given reader, bytes, successSteps, and failureSteps.
- } while (true)
- } catch (e) {
- // 1. Call failureSteps with e.
- failureSteps(e)
- }
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#is-local
- * @param {URL} url
- * @returns {boolean}
- */
-function urlIsLocal (url) {
- assert('protocol' in url) // ensure it's a url object
-
- const protocol = url.protocol
-
- // A URL is local if its scheme is a local scheme.
- // A local scheme is "about", "blob", or "data".
- return protocol === 'about:' || protocol === 'blob:' || protocol === 'data:'
-}
-
-/**
- * @param {string|URL} url
- * @returns {boolean}
- */
-function urlHasHttpsScheme (url) {
- return (
- (
- typeof url === 'string' &&
- url[5] === ':' &&
- url[0] === 'h' &&
- url[1] === 't' &&
- url[2] === 't' &&
- url[3] === 'p' &&
- url[4] === 's'
- ) ||
- url.protocol === 'https:'
- )
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#http-scheme
- * @param {URL} url
- */
-function urlIsHttpHttpsScheme (url) {
- assert('protocol' in url) // ensure it's a url object
-
- const protocol = url.protocol
-
- return protocol === 'http:' || protocol === 'https:'
-}
-
-/**
- * @typedef {Object} RangeHeaderValue
- * @property {number|null} rangeStartValue
- * @property {number|null} rangeEndValue
- */
-
-/**
- * @see https://fetch.spec.whatwg.org/#simple-range-header-value
- * @param {string} value
- * @param {boolean} allowWhitespace
- * @return {RangeHeaderValue|'failure'}
- */
-function simpleRangeHeaderValue (value, allowWhitespace) {
- // 1. Let data be the isomorphic decoding of value.
- // Note: isomorphic decoding takes a sequence of bytes (ie. a Uint8Array) and turns it into a string,
- // nothing more. We obviously don't need to do that if value is a string already.
- const data = value
-
- // 2. If data does not start with "bytes", then return failure.
- if (!data.startsWith('bytes')) {
- return 'failure'
- }
-
- // 3. Let position be a position variable for data, initially pointing at the 5th code point of data.
- const position = { position: 5 }
-
- // 4. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space,
- // from data given position.
- if (allowWhitespace) {
- collectASequenceOfCodePoints(
- (char) => char === '\t' || char === ' ',
- data,
- position
- )
- }
-
- // 5. If the code point at position within data is not U+003D (=), then return failure.
- if (data.charCodeAt(position.position) !== 0x3D) {
- return 'failure'
- }
-
- // 6. Advance position by 1.
- position.position++
-
- // 7. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from
- // data given position.
- if (allowWhitespace) {
- collectASequenceOfCodePoints(
- (char) => char === '\t' || char === ' ',
- data,
- position
- )
- }
-
- // 8. Let rangeStart be the result of collecting a sequence of code points that are ASCII digits,
- // from data given position.
- const rangeStart = collectASequenceOfCodePoints(
- (char) => {
- const code = char.charCodeAt(0)
-
- return code >= 0x30 && code <= 0x39
- },
- data,
- position
- )
-
- // 9. Let rangeStartValue be rangeStart, interpreted as decimal number, if rangeStart is not the
- // empty string; otherwise null.
- const rangeStartValue = rangeStart.length ? Number(rangeStart) : null
-
- // 10. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space,
- // from data given position.
- if (allowWhitespace) {
- collectASequenceOfCodePoints(
- (char) => char === '\t' || char === ' ',
- data,
- position
- )
- }
-
- // 11. If the code point at position within data is not U+002D (-), then return failure.
- if (data.charCodeAt(position.position) !== 0x2D) {
- return 'failure'
- }
-
- // 12. Advance position by 1.
- position.position++
-
- // 13. If allowWhitespace is true, collect a sequence of code points that are HTTP tab
- // or space, from data given position.
- // Note from Khafra: its the same step as in #8 again lol
- if (allowWhitespace) {
- collectASequenceOfCodePoints(
- (char) => char === '\t' || char === ' ',
- data,
- position
- )
- }
-
- // 14. Let rangeEnd be the result of collecting a sequence of code points that are
- // ASCII digits, from data given position.
- // Note from Khafra: you wouldn't guess it, but this is also the same step as #8
- const rangeEnd = collectASequenceOfCodePoints(
- (char) => {
- const code = char.charCodeAt(0)
-
- return code >= 0x30 && code <= 0x39
- },
- data,
- position
- )
-
- // 15. Let rangeEndValue be rangeEnd, interpreted as decimal number, if rangeEnd
- // is not the empty string; otherwise null.
- // Note from Khafra: THE SAME STEP, AGAIN!!!
- // Note: why interpret as a decimal if we only collect ascii digits?
- const rangeEndValue = rangeEnd.length ? Number(rangeEnd) : null
-
- // 16. If position is not past the end of data, then return failure.
- if (position.position < data.length) {
- return 'failure'
- }
-
- // 17. If rangeEndValue and rangeStartValue are null, then return failure.
- if (rangeEndValue === null && rangeStartValue === null) {
- return 'failure'
- }
-
- // 18. If rangeStartValue and rangeEndValue are numbers, and rangeStartValue is
- // greater than rangeEndValue, then return failure.
- // Note: ... when can they not be numbers?
- if (rangeStartValue > rangeEndValue) {
- return 'failure'
- }
-
- // 19. Return (rangeStartValue, rangeEndValue).
- return { rangeStartValue, rangeEndValue }
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#build-a-content-range
- * @param {number} rangeStart
- * @param {number} rangeEnd
- * @param {number} fullLength
- */
-function buildContentRange (rangeStart, rangeEnd, fullLength) {
- // 1. Let contentRange be `bytes `.
- let contentRange = 'bytes '
-
- // 2. Append rangeStart, serialized and isomorphic encoded, to contentRange.
- contentRange += isomorphicEncode(`${rangeStart}`)
-
- // 3. Append 0x2D (-) to contentRange.
- contentRange += '-'
-
- // 4. Append rangeEnd, serialized and isomorphic encoded to contentRange.
- contentRange += isomorphicEncode(`${rangeEnd}`)
-
- // 5. Append 0x2F (/) to contentRange.
- contentRange += '/'
-
- // 6. Append fullLength, serialized and isomorphic encoded to contentRange.
- contentRange += isomorphicEncode(`${fullLength}`)
-
- // 7. Return contentRange.
- return contentRange
-}
-
-// A Stream, which pipes the response to zlib.createInflate() or
-// zlib.createInflateRaw() depending on the first byte of the Buffer.
-// If the lower byte of the first byte is 0x08, then the stream is
-// interpreted as a zlib stream, otherwise it's interpreted as a
-// raw deflate stream.
-class InflateStream extends Transform {
- #zlibOptions
-
- /** @param {zlib.ZlibOptions} [zlibOptions] */
- constructor (zlibOptions) {
- super()
- this.#zlibOptions = zlibOptions
- }
-
- _transform (chunk, encoding, callback) {
- if (!this._inflateStream) {
- if (chunk.length === 0) {
- callback()
- return
- }
- this._inflateStream = (chunk[0] & 0x0F) === 0x08
- ? zlib.createInflate(this.#zlibOptions)
- : zlib.createInflateRaw(this.#zlibOptions)
-
- this._inflateStream.on('data', this.push.bind(this))
- this._inflateStream.on('end', () => this.push(null))
- this._inflateStream.on('error', (err) => this.destroy(err))
- }
-
- this._inflateStream.write(chunk, encoding, callback)
- }
-
- _final (callback) {
- if (this._inflateStream) {
- this._inflateStream.end()
- this._inflateStream = null
- }
- callback()
- }
-}
-
-/**
- * @param {zlib.ZlibOptions} [zlibOptions]
- * @returns {InflateStream}
- */
-function createInflate (zlibOptions) {
- return new InflateStream(zlibOptions)
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
- * @param {import('./headers').HeadersList} headers
- */
-function extractMimeType (headers) {
- // 1. Let charset be null.
- let charset = null
-
- // 2. Let essence be null.
- let essence = null
-
- // 3. Let mimeType be null.
- let mimeType = null
-
- // 4. Let values be the result of getting, decoding, and splitting `Content-Type` from headers.
- const values = getDecodeSplit('content-type', headers)
-
- // 5. If values is null, then return failure.
- if (values === null) {
- return 'failure'
- }
-
- // 6. For each value of values:
- for (const value of values) {
- // 6.1. Let temporaryMimeType be the result of parsing value.
- const temporaryMimeType = parseMIMEType(value)
-
- // 6.2. If temporaryMimeType is failure or its essence is "*/*", then continue.
- if (temporaryMimeType === 'failure' || temporaryMimeType.essence === '*/*') {
- continue
- }
-
- // 6.3. Set mimeType to temporaryMimeType.
- mimeType = temporaryMimeType
-
- // 6.4. If mimeType’s essence is not essence, then:
- if (mimeType.essence !== essence) {
- // 6.4.1. Set charset to null.
- charset = null
-
- // 6.4.2. If mimeType’s parameters["charset"] exists, then set charset to
- // mimeType’s parameters["charset"].
- if (mimeType.parameters.has('charset')) {
- charset = mimeType.parameters.get('charset')
- }
-
- // 6.4.3. Set essence to mimeType’s essence.
- essence = mimeType.essence
- } else if (!mimeType.parameters.has('charset') && charset !== null) {
- // 6.5. Otherwise, if mimeType’s parameters["charset"] does not exist, and
- // charset is non-null, set mimeType’s parameters["charset"] to charset.
- mimeType.parameters.set('charset', charset)
- }
- }
-
- // 7. If mimeType is null, then return failure.
- if (mimeType == null) {
- return 'failure'
- }
-
- // 8. Return mimeType.
- return mimeType
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#header-value-get-decode-and-split
- * @param {string|null} value
- */
-function gettingDecodingSplitting (value) {
- // 1. Let input be the result of isomorphic decoding value.
- const input = value
-
- // 2. Let position be a position variable for input, initially pointing at the start of input.
- const position = { position: 0 }
-
- // 3. Let values be a list of strings, initially empty.
- const values = []
-
- // 4. Let temporaryValue be the empty string.
- let temporaryValue = ''
-
- // 5. While position is not past the end of input:
- while (position.position < input.length) {
- // 5.1. Append the result of collecting a sequence of code points that are not U+0022 (")
- // or U+002C (,) from input, given position, to temporaryValue.
- temporaryValue += collectASequenceOfCodePoints(
- (char) => char !== '"' && char !== ',',
- input,
- position
- )
-
- // 5.2. If position is not past the end of input, then:
- if (position.position < input.length) {
- // 5.2.1. If the code point at position within input is U+0022 ("), then:
- if (input.charCodeAt(position.position) === 0x22) {
- // 5.2.1.1. Append the result of collecting an HTTP quoted string from input, given position, to temporaryValue.
- temporaryValue += collectAnHTTPQuotedString(
- input,
- position
- )
-
- // 5.2.1.2. If position is not past the end of input, then continue.
- if (position.position < input.length) {
- continue
- }
- } else {
- // 5.2.2. Otherwise:
-
- // 5.2.2.1. Assert: the code point at position within input is U+002C (,).
- assert(input.charCodeAt(position.position) === 0x2C)
-
- // 5.2.2.2. Advance position by 1.
- position.position++
- }
- }
-
- // 5.3. Remove all HTTP tab or space from the start and end of temporaryValue.
- temporaryValue = removeChars(temporaryValue, true, true, (char) => char === 0x9 || char === 0x20)
-
- // 5.4. Append temporaryValue to values.
- values.push(temporaryValue)
-
- // 5.6. Set temporaryValue to the empty string.
- temporaryValue = ''
- }
-
- // 6. Return values.
- return values
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#concept-header-list-get-decode-split
- * @param {string} name lowercase header name
- * @param {import('./headers').HeadersList} list
- */
-function getDecodeSplit (name, list) {
- // 1. Let value be the result of getting name from list.
- const value = list.get(name, true)
-
- // 2. If value is null, then return null.
- if (value === null) {
- return null
- }
-
- // 3. Return the result of getting, decoding, and splitting value.
- return gettingDecodingSplitting(value)
-}
-
-function hasAuthenticationEntry (request) {
- return false
-}
-
-/**
- * @see https://url.spec.whatwg.org/#include-credentials
- * @param {URL} url
- */
-function includesCredentials (url) {
- // A URL includes credentials if its username or password is not the empty string.
- return !!(url.username || url.password)
-}
-
-/**
- * @see https://html.spec.whatwg.org/multipage/document-sequences.html#traversable-navigable
- * @param {object|string} navigable
- */
-function isTraversableNavigable (navigable) {
- // TODO
- return true
-}
-
-class EnvironmentSettingsObjectBase {
- get baseUrl () {
- return getGlobalOrigin()
- }
-
- get origin () {
- return this.baseUrl?.origin
- }
-
- policyContainer = makePolicyContainer()
-}
-
-class EnvironmentSettingsObject {
- settingsObject = new EnvironmentSettingsObjectBase()
-}
-
-const environmentSettingsObject = new EnvironmentSettingsObject()
-
-module.exports = {
- isAborted,
- isCancelled,
- isValidEncodedURL,
- ReadableStreamFrom,
- tryUpgradeRequestToAPotentiallyTrustworthyURL,
- clampAndCoarsenConnectionTimingInfo,
- coarsenedSharedCurrentTime,
- determineRequestsReferrer,
- makePolicyContainer,
- clonePolicyContainer,
- appendFetchMetadata,
- appendRequestOriginHeader,
- TAOCheck,
- corsCheck,
- crossOriginResourcePolicyCheck,
- createOpaqueTimingInfo,
- setRequestReferrerPolicyOnRedirect,
- isValidHTTPToken,
- requestBadPort,
- requestCurrentURL,
- responseURL,
- responseLocationURL,
- isURLPotentiallyTrustworthy,
- isValidReasonPhrase,
- sameOrigin,
- normalizeMethod,
- iteratorMixin,
- createIterator,
- isValidHeaderName,
- isValidHeaderValue,
- isErrorLike,
- fullyReadBody,
- readableStreamClose,
- urlIsLocal,
- urlHasHttpsScheme,
- urlIsHttpHttpsScheme,
- readAllBytes,
- simpleRangeHeaderValue,
- buildContentRange,
- createInflate,
- extractMimeType,
- getDecodeSplit,
- environmentSettingsObject,
- isOriginIPPotentiallyTrustworthy,
- hasAuthenticationEntry,
- includesCredentials,
- isTraversableNavigable
-}
diff --git a/vanilla/node_modules/undici/lib/web/infra/index.js b/vanilla/node_modules/undici/lib/web/infra/index.js
deleted file mode 100644
index 391bb2e..0000000
--- a/vanilla/node_modules/undici/lib/web/infra/index.js
+++ /dev/null
@@ -1,229 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { utf8DecodeBytes } = require('../../encoding')
-
-/**
- * @param {(char: string) => boolean} condition
- * @param {string} input
- * @param {{ position: number }} position
- * @returns {string}
- *
- * @see https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
- */
-function collectASequenceOfCodePoints (condition, input, position) {
- // 1. Let result be the empty string.
- let result = ''
-
- // 2. While position doesn’t 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
- * @returns {string}
- *
- * @see https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
- */
-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)
-}
-
-const ASCII_WHITESPACE_REPLACE_REGEX = /[\u0009\u000A\u000C\u000D\u0020]/g // eslint-disable-line no-control-regex
-
-/**
- * @param {string} data
- * @returns {Uint8Array | 'failure'}
- *
- * @see https://infra.spec.whatwg.org/#forgiving-base64-decode
- */
-function forgivingBase64 (data) {
- // 1. Remove all ASCII whitespace from data.
- data = data.replace(ASCII_WHITESPACE_REPLACE_REGEX, '')
-
- let dataLength = data.length
- // 2. If data’s code point length divides by 4 leaving
- // no remainder, then:
- if (dataLength % 4 === 0) {
- // 1. If data ends with one or two U+003D (=) code points,
- // then remove them from data.
- if (data.charCodeAt(dataLength - 1) === 0x003D) {
- --dataLength
- if (data.charCodeAt(dataLength - 1) === 0x003D) {
- --dataLength
- }
- }
- }
-
- // 3. If data’s code point length divides by 4 leaving
- // a remainder of 1, then return failure.
- if (dataLength % 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.length === dataLength ? data : data.substring(0, dataLength))) {
- return 'failure'
- }
-
- const buffer = Buffer.from(data, 'base64')
- return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)
-}
-
-/**
- * @param {number} char
- * @returns {boolean}
- *
- * @see https://infra.spec.whatwg.org/#ascii-whitespace
- */
-function isASCIIWhitespace (char) {
- return (
- char === 0x09 || // \t
- char === 0x0a || // \n
- char === 0x0c || // \f
- char === 0x0d || // \r
- char === 0x20 // space
- )
-}
-
-/**
- * @param {Uint8Array} input
- * @returns {string}
- *
- * @see https://infra.spec.whatwg.org/#isomorphic-decode
- */
-function isomorphicDecode (input) {
- // 1. To isomorphic decode a byte sequence input, return a string whose code point
- // length is equal to input’s length and whose code points have the same values
- // as the values of input’s bytes, in the same order.
- const length = input.length
- if ((2 << 15) - 1 > length) {
- return String.fromCharCode.apply(null, input)
- }
- let result = ''
- let i = 0
- let addition = (2 << 15) - 1
- while (i < length) {
- if (i + addition > length) {
- addition = length - i
- }
- result += String.fromCharCode.apply(null, input.subarray(i, i += addition))
- }
- return result
-}
-
-const invalidIsomorphicEncodeValueRegex = /[^\x00-\xFF]/ // eslint-disable-line no-control-regex
-
-/**
- * @param {string} input
- * @returns {string}
- *
- * @see https://infra.spec.whatwg.org/#isomorphic-encode
- */
-function isomorphicEncode (input) {
- // 1. Assert: input contains no code points greater than U+00FF.
- assert(!invalidIsomorphicEncodeValueRegex.test(input))
-
- // 2. Return a byte sequence whose length is equal to input’s code
- // point length and whose bytes have the same values as the
- // values of input’s code points, in the same order
- return input
-}
-
-/**
- * @see https://infra.spec.whatwg.org/#parse-json-bytes-to-a-javascript-value
- * @param {Uint8Array} bytes
- */
-function parseJSONFromBytes (bytes) {
- return JSON.parse(utf8DecodeBytes(bytes))
-}
-
-/**
- * @param {string} str
- * @param {boolean} [leading=true]
- * @param {boolean} [trailing=true]
- * @returns {string}
- *
- * @see https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace
- */
-function removeASCIIWhitespace (str, leading = true, trailing = true) {
- return removeChars(str, leading, trailing, isASCIIWhitespace)
-}
-
-/**
- * @param {string} str
- * @param {boolean} leading
- * @param {boolean} trailing
- * @param {(charCode: number) => boolean} predicate
- * @returns {string}
- */
-function removeChars (str, leading, trailing, predicate) {
- let lead = 0
- let trail = str.length - 1
-
- if (leading) {
- while (lead < str.length && predicate(str.charCodeAt(lead))) lead++
- }
-
- if (trailing) {
- while (trail > 0 && predicate(str.charCodeAt(trail))) trail--
- }
-
- return lead === 0 && trail === str.length - 1 ? str : str.slice(lead, trail + 1)
-}
-
-// https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string
-function serializeJavascriptValueToJSONString (value) {
- // 1. Let result be ? Call(%JSON.stringify%, undefined, « value »).
- const result = JSON.stringify(value)
-
- // 2. If result is undefined, then throw a TypeError.
- if (result === undefined) {
- throw new TypeError('Value is not JSON serializable')
- }
-
- // 3. Assert: result is a string.
- assert(typeof result === 'string')
-
- // 4. Return result.
- return result
-}
-
-module.exports = {
- collectASequenceOfCodePoints,
- collectASequenceOfCodePointsFast,
- forgivingBase64,
- isASCIIWhitespace,
- isomorphicDecode,
- isomorphicEncode,
- parseJSONFromBytes,
- removeASCIIWhitespace,
- removeChars,
- serializeJavascriptValueToJSONString
-}
diff --git a/vanilla/node_modules/undici/lib/web/subresource-integrity/Readme.md b/vanilla/node_modules/undici/lib/web/subresource-integrity/Readme.md
deleted file mode 100644
index 289a2b8..0000000
--- a/vanilla/node_modules/undici/lib/web/subresource-integrity/Readme.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Subresource Integrity
-
-based on Editor’s Draft, 12 June 2025
-
-This module provides support for Subresource Integrity (SRI) in the context of web fetch operations. SRI is a security feature that allows clients to verify that fetched resources are delivered without unexpected manipulation.
-
-## Links
-
-- [Subresource Integrity](https://w3c.github.io/webappsec-subresource-integrity/) \ No newline at end of file
diff --git a/vanilla/node_modules/undici/lib/web/subresource-integrity/subresource-integrity.js b/vanilla/node_modules/undici/lib/web/subresource-integrity/subresource-integrity.js
deleted file mode 100644
index 8c8f6c4..0000000
--- a/vanilla/node_modules/undici/lib/web/subresource-integrity/subresource-integrity.js
+++ /dev/null
@@ -1,307 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { runtimeFeatures } = require('../../util/runtime-features.js')
-
-/**
- * @typedef {object} Metadata
- * @property {SRIHashAlgorithm} alg - The algorithm used for the hash.
- * @property {string} val - The base64-encoded hash value.
- */
-
-/**
- * @typedef {Metadata[]} MetadataList
- */
-
-/**
- * @typedef {('sha256' | 'sha384' | 'sha512')} SRIHashAlgorithm
- */
-
-/**
- * @type {Map<SRIHashAlgorithm, number>}
- *
- * The valid SRI hash algorithm token set is the ordered set « "sha256",
- * "sha384", "sha512" » (corresponding to SHA-256, SHA-384, and SHA-512
- * respectively). The ordering of this set is meaningful, with stronger
- * algorithms appearing later in the set.
- *
- * @see https://w3c.github.io/webappsec-subresource-integrity/#valid-sri-hash-algorithm-token-set
- */
-const validSRIHashAlgorithmTokenSet = new Map([['sha256', 0], ['sha384', 1], ['sha512', 2]])
-
-// https://nodejs.org/api/crypto.html#determining-if-crypto-support-is-unavailable
-/** @type {import('node:crypto')} */
-let crypto
-
-if (runtimeFeatures.has('crypto')) {
- crypto = require('node:crypto')
- const cryptoHashes = crypto.getHashes()
-
- // If no hashes are available, we cannot support SRI.
- if (cryptoHashes.length === 0) {
- validSRIHashAlgorithmTokenSet.clear()
- }
-
- for (const algorithm of validSRIHashAlgorithmTokenSet.keys()) {
- // If the algorithm is not supported, remove it from the list.
- if (cryptoHashes.includes(algorithm) === false) {
- validSRIHashAlgorithmTokenSet.delete(algorithm)
- }
- }
-} else {
- // If crypto is not available, we cannot support SRI.
- validSRIHashAlgorithmTokenSet.clear()
-}
-
-/**
- * @typedef GetSRIHashAlgorithmIndex
- * @type {(algorithm: SRIHashAlgorithm) => number}
- * @param {SRIHashAlgorithm} algorithm
- * @returns {number} The index of the algorithm in the valid SRI hash algorithm
- * token set.
- */
-
-const getSRIHashAlgorithmIndex = /** @type {GetSRIHashAlgorithmIndex} */ (Map.prototype.get.bind(
- validSRIHashAlgorithmTokenSet))
-
-/**
- * @typedef IsValidSRIHashAlgorithm
- * @type {(algorithm: string) => algorithm is SRIHashAlgorithm}
- * @param {*} algorithm
- * @returns {algorithm is SRIHashAlgorithm}
- */
-
-const isValidSRIHashAlgorithm = /** @type {IsValidSRIHashAlgorithm} */ (
- Map.prototype.has.bind(validSRIHashAlgorithmTokenSet)
-)
-
-/**
- * @param {Uint8Array} bytes
- * @param {string} metadataList
- * @returns {boolean}
- *
- * @see https://w3c.github.io/webappsec-subresource-integrity/#does-response-match-metadatalist
- */
-const bytesMatch = runtimeFeatures.has('crypto') === false || validSRIHashAlgorithmTokenSet.size === 0
- // If node is not built with OpenSSL support, we cannot check
- // a request's integrity, so allow it by default (the spec will
- // allow requests if an invalid hash is given, as precedence).
- ? () => true
- : (bytes, metadataList) => {
- // 1. Let parsedMetadata be the result of parsing metadataList.
- const parsedMetadata = parseMetadata(metadataList)
-
- // 2. If parsedMetadata is empty set, return true.
- if (parsedMetadata.length === 0) {
- return true
- }
-
- // 3. Let metadata be the result of getting the strongest
- // metadata from parsedMetadata.
- const metadata = getStrongestMetadata(parsedMetadata)
-
- // 4. For each item in metadata:
- for (const item of metadata) {
- // 1. Let algorithm be the item["alg"].
- const algorithm = item.alg
-
- // 2. Let expectedValue be the item["val"].
- const expectedValue = item.val
-
- // See https://github.com/web-platform-tests/wpt/commit/e4c5cc7a5e48093220528dfdd1c4012dc3837a0e
- // "be liberal with padding". This is annoying, and it's not even in the spec.
-
- // 3. Let actualValue be the result of applying algorithm to bytes .
- const actualValue = applyAlgorithmToBytes(algorithm, bytes)
-
- // 4. If actualValue is a case-sensitive match for expectedValue,
- // return true.
- if (caseSensitiveMatch(actualValue, expectedValue)) {
- return true
- }
- }
-
- // 5. Return false.
- return false
- }
-
-/**
- * @param {MetadataList} metadataList
- * @returns {MetadataList} The strongest hash algorithm from the metadata list.
- */
-function getStrongestMetadata (metadataList) {
- // 1. Let result be the empty set and strongest be the empty string.
- const result = []
- /** @type {Metadata|null} */
- let strongest = null
-
- // 2. For each item in set:
- for (const item of metadataList) {
- // 1. Assert: item["alg"] is a valid SRI hash algorithm token.
- assert(isValidSRIHashAlgorithm(item.alg), 'Invalid SRI hash algorithm token')
-
- // 2. If result is the empty set, then:
- if (result.length === 0) {
- // 1. Append item to result.
- result.push(item)
-
- // 2. Set strongest to item.
- strongest = item
-
- // 3. Continue.
- continue
- }
-
- // 3. Let currentAlgorithm be strongest["alg"], and currentAlgorithmIndex be
- // the index of currentAlgorithm in the valid SRI hash algorithm token set.
- const currentAlgorithm = /** @type {Metadata} */ (strongest).alg
- const currentAlgorithmIndex = getSRIHashAlgorithmIndex(currentAlgorithm)
-
- // 4. Let newAlgorithm be the item["alg"], and newAlgorithmIndex be the
- // index of newAlgorithm in the valid SRI hash algorithm token set.
- const newAlgorithm = item.alg
- const newAlgorithmIndex = getSRIHashAlgorithmIndex(newAlgorithm)
-
- // 5. If newAlgorithmIndex is less than currentAlgorithmIndex, then continue.
- if (newAlgorithmIndex < currentAlgorithmIndex) {
- continue
-
- // 6. Otherwise, if newAlgorithmIndex is greater than
- // currentAlgorithmIndex:
- } else if (newAlgorithmIndex > currentAlgorithmIndex) {
- // 1. Set strongest to item.
- strongest = item
-
- // 2. Set result to « item ».
- result[0] = item
- result.length = 1
-
- // 7. Otherwise, newAlgorithmIndex and currentAlgorithmIndex are the same
- // value. Append item to result.
- } else {
- result.push(item)
- }
- }
-
- // 3. Return result.
- return result
-}
-
-/**
- * @param {string} metadata
- * @returns {MetadataList}
- *
- * @see https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata
- */
-function parseMetadata (metadata) {
- // 1. Let result be the empty set.
- /** @type {MetadataList} */
- const result = []
-
- // 2. For each item returned by splitting metadata on spaces:
- for (const item of metadata.split(' ')) {
- // 1. Let expression-and-options be the result of splitting item on U+003F (?).
- const expressionAndOptions = item.split('?', 1)
-
- // 2. Let algorithm-expression be expression-and-options[0].
- const algorithmExpression = expressionAndOptions[0]
-
- // 3. Let base64-value be the empty string.
- let base64Value = ''
-
- // 4. Let algorithm-and-value be the result of splitting algorithm-expression on U+002D (-).
- const algorithmAndValue = [algorithmExpression.slice(0, 6), algorithmExpression.slice(7)]
-
- // 5. Let algorithm be algorithm-and-value[0].
- const algorithm = algorithmAndValue[0]
-
- // 6. If algorithm is not a valid SRI hash algorithm token, then continue.
- if (!isValidSRIHashAlgorithm(algorithm)) {
- continue
- }
-
- // 7. If algorithm-and-value[1] exists, set base64-value to
- // algorithm-and-value[1].
- if (algorithmAndValue[1]) {
- base64Value = algorithmAndValue[1]
- }
-
- // 8. Let metadata be the ordered map
- // «["alg" → algorithm, "val" → base64-value]».
- const metadata = {
- alg: algorithm,
- val: base64Value
- }
-
- // 9. Append metadata to result.
- result.push(metadata)
- }
-
- // 3. Return result.
- return result
-}
-
-/**
- * Applies the specified hash algorithm to the given bytes
- *
- * @typedef {(algorithm: SRIHashAlgorithm, bytes: Uint8Array) => string} ApplyAlgorithmToBytes
- * @param {SRIHashAlgorithm} algorithm
- * @param {Uint8Array} bytes
- * @returns {string}
- */
-const applyAlgorithmToBytes = (algorithm, bytes) => {
- return crypto.hash(algorithm, bytes, 'base64')
-}
-
-/**
- * Compares two base64 strings, allowing for base64url
- * in the second string.
- *
- * @param {string} actualValue base64 encoded string
- * @param {string} expectedValue base64 or base64url encoded string
- * @returns {boolean}
- */
-function caseSensitiveMatch (actualValue, expectedValue) {
- // Ignore padding characters from the end of the strings by
- // decreasing the length by 1 or 2 if the last characters are `=`.
- let actualValueLength = actualValue.length
- if (actualValueLength !== 0 && actualValue[actualValueLength - 1] === '=') {
- actualValueLength -= 1
- }
- if (actualValueLength !== 0 && actualValue[actualValueLength - 1] === '=') {
- actualValueLength -= 1
- }
- let expectedValueLength = expectedValue.length
- if (expectedValueLength !== 0 && expectedValue[expectedValueLength - 1] === '=') {
- expectedValueLength -= 1
- }
- if (expectedValueLength !== 0 && expectedValue[expectedValueLength - 1] === '=') {
- expectedValueLength -= 1
- }
-
- if (actualValueLength !== expectedValueLength) {
- return false
- }
-
- for (let i = 0; i < actualValueLength; ++i) {
- if (
- actualValue[i] === expectedValue[i] ||
- (actualValue[i] === '+' && expectedValue[i] === '-') ||
- (actualValue[i] === '/' && expectedValue[i] === '_')
- ) {
- continue
- }
- return false
- }
-
- return true
-}
-
-module.exports = {
- applyAlgorithmToBytes,
- bytesMatch,
- caseSensitiveMatch,
- isValidSRIHashAlgorithm,
- getStrongestMetadata,
- parseMetadata
-}
diff --git a/vanilla/node_modules/undici/lib/web/webidl/index.js b/vanilla/node_modules/undici/lib/web/webidl/index.js
deleted file mode 100644
index c783432..0000000
--- a/vanilla/node_modules/undici/lib/web/webidl/index.js
+++ /dev/null
@@ -1,1003 +0,0 @@
-'use strict'
-
-const assert = require('node:assert')
-const { types, inspect } = require('node:util')
-const { runtimeFeatures } = require('../../util/runtime-features')
-
-const UNDEFINED = 1
-const BOOLEAN = 2
-const STRING = 3
-const SYMBOL = 4
-const NUMBER = 5
-const BIGINT = 6
-const NULL = 7
-const OBJECT = 8 // function and object
-
-const FunctionPrototypeSymbolHasInstance = Function.call.bind(Function.prototype[Symbol.hasInstance])
-
-/** @type {import('../../../types/webidl').Webidl} */
-const webidl = {
- converters: {},
- util: {},
- errors: {},
- is: {}
-}
-
-/**
- * @description Instantiate an error.
- *
- * @param {Object} opts
- * @param {string} opts.header
- * @param {string} opts.message
- * @returns {TypeError}
- */
-webidl.errors.exception = function (message) {
- return new TypeError(`${message.header}: ${message.message}`)
-}
-
-/**
- * @description Instantiate an error when conversion from one type to another has failed.
- *
- * @param {Object} opts
- * @param {string} opts.prefix
- * @param {string} opts.argument
- * @param {string[]} opts.types
- * @returns {TypeError}
- */
-webidl.errors.conversionFailed = function (opts) {
- const plural = opts.types.length === 1 ? '' : ' one of'
- const message =
- `${opts.argument} could not be converted to` +
- `${plural}: ${opts.types.join(', ')}.`
-
- return webidl.errors.exception({
- header: opts.prefix,
- message
- })
-}
-
-/**
- * @description Instantiate an error when an invalid argument is provided
- *
- * @param {Object} context
- * @param {string} context.prefix
- * @param {string} context.value
- * @param {string} context.type
- * @returns {TypeError}
- */
-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) {
- if (!FunctionPrototypeSymbolHasInstance(I, V)) {
- const err = new TypeError('Illegal invocation')
- err.code = 'ERR_INVALID_THIS' // node compat.
- throw err
- }
-}
-
-webidl.brandCheckMultiple = function (List) {
- const prototypes = List.map((c) => webidl.util.MakeTypeAssertion(c))
-
- return (V) => {
- if (prototypes.every(typeCheck => !typeCheck(V))) {
- const err = new TypeError('Illegal invocation')
- err.code = 'ERR_INVALID_THIS' // node compat.
- throw err
- }
- }
-}
-
-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.`,
- header: ctx
- })
- }
-}
-
-webidl.illegalConstructor = function () {
- throw webidl.errors.exception({
- header: 'TypeError',
- message: 'Illegal constructor'
- })
-}
-
-webidl.util.MakeTypeAssertion = function (I) {
- return (O) => FunctionPrototypeSymbolHasInstance(I, O)
-}
-
-// 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
- }
- }
-}
-
-webidl.util.Types = {
- UNDEFINED,
- BOOLEAN,
- STRING,
- SYMBOL,
- NUMBER,
- BIGINT,
- NULL,
- OBJECT
-}
-
-webidl.util.TypeValueToString = function (o) {
- switch (webidl.util.Type(o)) {
- 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 NULL: return 'Null'
- case OBJECT: return 'Object'
- }
-}
-
-webidl.util.markAsUncloneable = runtimeFeatures.has('markAsUncloneable')
- ? require('node:worker_threads').markAsUncloneable
- : () => {}
-
-// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
-webidl.util.ConvertToInt = function (V, bitLength, signedness, flags) {
- 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 (webidl.util.HasFlag(flags, webidl.attributes.EnforceRange)) {
- // 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 ${webidl.util.Stringify(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) && webidl.util.HasFlag(flags, webidl.attributes.Clamp)) {
- // 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
-}
-
-webidl.util.Stringify = function (V) {
- const type = webidl.util.Type(V)
-
- switch (type) {
- case SYMBOL:
- return `Symbol(${V.description})`
- case OBJECT:
- return inspect(V)
- case STRING:
- return `"${V}"`
- case BIGINT:
- return `${V}n`
- default:
- return `${V}`
- }
-}
-
-webidl.util.IsResizableArrayBuffer = function (V) {
- if (types.isArrayBuffer(V)) {
- return V.resizable
- }
-
- if (types.isSharedArrayBuffer(V)) {
- return V.growable
- }
-
- throw webidl.errors.exception({
- header: 'IsResizableArrayBuffer',
- message: `"${webidl.util.Stringify(V)}" is not an array buffer.`
- })
-}
-
-webidl.util.HasFlag = function (flags, attributes) {
- return typeof flags === 'number' && (flags & attributes) === attributes
-}
-
-// https://webidl.spec.whatwg.org/#es-sequence
-webidl.sequenceConverter = function (converter) {
- return (V, prefix, argument, Iterable) => {
- // 1. If Type(V) is not Object, throw a TypeError.
- if (webidl.util.Type(V) !== OBJECT) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} (${webidl.util.Stringify(V)}) is not iterable.`
- })
- }
-
- // 2. Let method be ? GetMethod(V, @@iterator).
- /** @type {Generator} */
- const method = typeof Iterable === 'function' ? Iterable() : V?.[Symbol.iterator]?.()
- const seq = []
- let index = 0
-
- // 3. If method is undefined, throw a TypeError.
- if (
- method === undefined ||
- typeof method.next !== 'function'
- ) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} is not iterable.`
- })
- }
-
- // https://webidl.spec.whatwg.org/#create-sequence-from-iterable
- while (true) {
- const { done, value } = method.next()
-
- if (done) {
- break
- }
-
- seq.push(converter(value, prefix, `${argument}[${index++}]`))
- }
-
- return seq
- }
-}
-
-// https://webidl.spec.whatwg.org/#es-to-record
-webidl.recordConverter = function (keyConverter, valueConverter) {
- return (O, prefix, argument) => {
- // 1. If Type(O) is not Object, throw a TypeError.
- if (webidl.util.Type(O) !== OBJECT) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} ("${webidl.util.TypeValueToString(O)}") is not an Object.`
- })
- }
-
- // 2. Let result be a new empty instance of record<K, V>.
- const result = {}
-
- if (!types.isProxy(O)) {
- // 1. Let desc be ? O.[[GetOwnProperty]](key).
- const keys = [...Object.getOwnPropertyNames(O), ...Object.getOwnPropertySymbols(O)]
-
- for (const key of keys) {
- const keyName = webidl.util.Stringify(key)
-
- // 1. Let typedKey be key converted to an IDL value of type K.
- const typedKey = keyConverter(key, prefix, `Key ${keyName} in ${argument}`)
-
- // 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], prefix, `${argument}[${keyName}]`)
-
- // 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, prefix, argument)
-
- // 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], prefix, argument)
-
- // 4. Set result[typedKey] to typedValue.
- result[typedKey] = typedValue
- }
- }
-
- // 5. Return result.
- return result
- }
-}
-
-webidl.interfaceConverter = function (TypeCheck, name) {
- return (V, prefix, argument) => {
- if (!TypeCheck(V)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `Expected ${argument} ("${webidl.util.Stringify(V)}") to be an instance of ${name}.`
- })
- }
-
- return V
- }
-}
-
-webidl.dictionaryConverter = function (converters) {
- return (dictionary, prefix, argument) => {
- const dict = {}
-
- if (dictionary != null && webidl.util.Type(dictionary) !== OBJECT) {
- throw webidl.errors.exception({
- header: prefix,
- 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 (dictionary == null || !Object.hasOwn(dictionary, key)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `Missing required key "${key}".`
- })
- }
- }
-
- let value = dictionary?.[key]
- const hasDefault = defaultValue !== undefined
-
- // Only use defaultValue if value is undefined and
- // a defaultValue options was provided.
- if (hasDefault && value === undefined) {
- 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, prefix, `${argument}.${key}`)
-
- if (
- options.allowedValues &&
- !options.allowedValues.includes(value)
- ) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${value} is not an accepted type. Expected one of ${options.allowedValues.join(', ')}.`
- })
- }
-
- dict[key] = value
- }
- }
-
- return dict
- }
-}
-
-webidl.nullableConverter = function (converter) {
- return (V, prefix, argument) => {
- if (V === null) {
- return V
- }
-
- return converter(V, prefix, argument)
- }
-}
-
-/**
- * @param {*} value
- * @returns {boolean}
- */
-webidl.is.USVString = function (value) {
- return (
- typeof value === 'string' &&
- value.isWellFormed()
- )
-}
-
-webidl.is.ReadableStream = webidl.util.MakeTypeAssertion(ReadableStream)
-webidl.is.Blob = webidl.util.MakeTypeAssertion(Blob)
-webidl.is.URLSearchParams = webidl.util.MakeTypeAssertion(URLSearchParams)
-webidl.is.File = webidl.util.MakeTypeAssertion(File)
-webidl.is.URL = webidl.util.MakeTypeAssertion(URL)
-webidl.is.AbortSignal = webidl.util.MakeTypeAssertion(AbortSignal)
-webidl.is.MessagePort = webidl.util.MakeTypeAssertion(MessagePort)
-
-webidl.is.BufferSource = function (V) {
- return types.isArrayBuffer(V) || (
- ArrayBuffer.isView(V) &&
- types.isArrayBuffer(V.buffer)
- )
-}
-
-// https://webidl.spec.whatwg.org/#dfn-get-buffer-source-copy
-webidl.util.getCopyOfBytesHeldByBufferSource = function (bufferSource) {
- // 1. Let jsBufferSource be the result of converting bufferSource to a JavaScript value.
- const jsBufferSource = bufferSource
-
- // 2. Let jsArrayBuffer be jsBufferSource.
- let jsArrayBuffer = jsBufferSource
-
- // 3. Let offset be 0.
- let offset = 0
-
- // 4. Let length be 0.
- let length = 0
-
- // 5. If jsBufferSource has a [[ViewedArrayBuffer]] internal slot, then:
- if (types.isTypedArray(jsBufferSource) || types.isDataView(jsBufferSource)) {
- // 5.1. Set jsArrayBuffer to jsBufferSource.[[ViewedArrayBuffer]].
- jsArrayBuffer = jsBufferSource.buffer
-
- // 5.2. Set offset to jsBufferSource.[[ByteOffset]].
- offset = jsBufferSource.byteOffset
-
- // 5.3. Set length to jsBufferSource.[[ByteLength]].
- length = jsBufferSource.byteLength
- } else {
- // 6. Otherwise:
-
- // 6.1. Assert: jsBufferSource is an ArrayBuffer or SharedArrayBuffer object.
- assert(types.isAnyArrayBuffer(jsBufferSource))
-
- // 6.2. Set length to jsBufferSource.[[ArrayBufferByteLength]].
- length = jsBufferSource.byteLength
- }
-
- // 7. If IsDetachedBuffer(jsArrayBuffer) is true, then return the empty byte sequence.
- if (jsArrayBuffer.detached) {
- return new Uint8Array(0)
- }
-
- // 8. Let bytes be a new byte sequence of length equal to length.
- const bytes = new Uint8Array(length)
-
- // 9. For i in the range offset to offset + length − 1, inclusive,
- // set bytes[i − offset] to GetValueFromBuffer(jsArrayBuffer, i, Uint8, true, Unordered).
- const view = new Uint8Array(jsArrayBuffer, offset, length)
- bytes.set(view)
-
- // 10. Return bytes.
- return bytes
-}
-
-// https://webidl.spec.whatwg.org/#es-DOMString
-webidl.converters.DOMString = function (V, prefix, argument, flags) {
- // 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 && webidl.util.HasFlag(flags, webidl.attributes.LegacyNullToEmptyString)) {
- return ''
- }
-
- // 2. Let x be ? ToString(V).
- if (typeof V === 'symbol') {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} is a symbol, which cannot be converted to a DOMString.`
- })
- }
-
- // 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, prefix, argument) {
- // 1. Let x be ? ToString(V).
- if (typeof V === 'symbol') {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} is a symbol, which cannot be converted to a ByteString.`
- })
- }
-
- const x = String(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
-}
-
-/**
- * @param {unknown} value
- * @returns {string}
- * @see https://webidl.spec.whatwg.org/#es-USVString
- */
-webidl.converters.USVString = function (value) {
- // TODO: rewrite this so we can control the errors thrown
- if (typeof value === 'string') {
- return value.toWellFormed()
- }
- return `${value}`.toWellFormed()
-}
-
-// https://webidl.spec.whatwg.org/#es-boolean
-webidl.converters.boolean = function (V) {
- // 1. Let x be the result of computing ToBoolean(V).
- // https://262.ecma-international.org/10.0/index.html#table-10
- 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, prefix, argument) {
- // 1. Let x be ? ConvertToInt(V, 64, "signed").
- const x = webidl.util.ConvertToInt(V, 64, 'signed', 0, prefix, argument)
-
- // 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, prefix, argument) {
- // 1. Let x be ? ConvertToInt(V, 64, "unsigned").
- const x = webidl.util.ConvertToInt(V, 64, 'unsigned', 0, prefix, argument)
-
- // 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, prefix, argument) {
- // 1. Let x be ? ConvertToInt(V, 32, "unsigned").
- const x = webidl.util.ConvertToInt(V, 32, 'unsigned', 0, prefix, argument)
-
- // 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, prefix, argument, flags) {
- // 1. Let x be ? ConvertToInt(V, 16, "unsigned").
- const x = webidl.util.ConvertToInt(V, 16, 'unsigned', flags, prefix, argument)
-
- // 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, prefix, argument, flags) {
- // 1. If V is not an Object, or V does not have an
- // [[ArrayBufferData]] internal slot, then throw a
- // TypeError.
- // 2. If IsSharedArrayBuffer(V) is true, then throw a
- // TypeError.
- // see: https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-instances
- if (
- webidl.util.Type(V) !== OBJECT ||
- !types.isArrayBuffer(V)
- ) {
- throw webidl.errors.conversionFailed({
- prefix,
- argument: `${argument} ("${webidl.util.Stringify(V)}")`,
- types: ['ArrayBuffer']
- })
- }
-
- // 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.
- if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} cannot be a resizable ArrayBuffer.`
- })
- }
-
- // 4. Return the IDL ArrayBuffer value that is a
- // reference to the same object as V.
- return V
-}
-
-// https://webidl.spec.whatwg.org/#idl-SharedArrayBuffer
-webidl.converters.SharedArrayBuffer = function (V, prefix, argument, flags) {
- // 1. If V is not an Object, or V does not have an
- // [[ArrayBufferData]] internal slot, then throw a
- // TypeError.
- // 2. If IsSharedArrayBuffer(V) is false, then throw a
- // TypeError.
- // see: https://tc39.es/ecma262/#sec-properties-of-the-sharedarraybuffer-instances
- if (
- webidl.util.Type(V) !== OBJECT ||
- !types.isSharedArrayBuffer(V)
- ) {
- throw webidl.errors.conversionFailed({
- prefix,
- argument: `${argument} ("${webidl.util.Stringify(V)}")`,
- types: ['SharedArrayBuffer']
- })
- }
-
- // 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.
- if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} cannot be a resizable SharedArrayBuffer.`
- })
- }
-
- // 4. Return the IDL SharedArrayBuffer value that is a
- // reference to the same object as V.
- return V
-}
-
-// https://webidl.spec.whatwg.org/#dfn-typed-array-type
-webidl.converters.TypedArray = function (V, T, prefix, argument, flags) {
- // 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 T’s name, then throw a TypeError.
- if (
- webidl.util.Type(V) !== OBJECT ||
- !types.isTypedArray(V) ||
- V.constructor.name !== T.name
- ) {
- throw webidl.errors.conversionFailed({
- prefix,
- argument: `${argument} ("${webidl.util.Stringify(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 (!webidl.util.HasFlag(flags, webidl.attributes.AllowShared) && types.isSharedArrayBuffer(V.buffer)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} cannot be a view on a shared array buffer.`
- })
- }
-
- // 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.
- if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V.buffer)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} cannot be a view on a resizable array buffer.`
- })
- }
-
- // 5. Return the IDL value of type T that is a reference
- // to the same object as V.
- return V
-}
-
-// https://webidl.spec.whatwg.org/#idl-DataView
-webidl.converters.DataView = function (V, prefix, argument, flags) {
- // 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.conversionFailed({
- prefix,
- argument: `${argument} ("${webidl.util.Stringify(V)}")`,
- types: ['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 (!webidl.util.HasFlag(flags, webidl.attributes.AllowShared) && types.isSharedArrayBuffer(V.buffer)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} cannot be a view on a shared array buffer.`
- })
- }
-
- // 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.
- if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V.buffer)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} cannot be a view on a resizable array buffer.`
- })
- }
-
- // 4. Return the IDL DataView value that is a reference
- // to the same object as V.
- return V
-}
-
-// https://webidl.spec.whatwg.org/#ArrayBufferView
-webidl.converters.ArrayBufferView = function (V, prefix, argument, flags) {
- if (
- webidl.util.Type(V) !== OBJECT ||
- !types.isArrayBufferView(V)
- ) {
- throw webidl.errors.conversionFailed({
- prefix,
- argument: `${argument} ("${webidl.util.Stringify(V)}")`,
- types: ['ArrayBufferView']
- })
- }
-
- if (!webidl.util.HasFlag(flags, webidl.attributes.AllowShared) && types.isSharedArrayBuffer(V.buffer)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} cannot be a view on a shared array buffer.`
- })
- }
-
- if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V.buffer)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} cannot be a view on a resizable array buffer.`
- })
- }
-
- return V
-}
-
-// https://webidl.spec.whatwg.org/#BufferSource
-webidl.converters.BufferSource = function (V, prefix, argument, flags) {
- if (types.isArrayBuffer(V)) {
- return webidl.converters.ArrayBuffer(V, prefix, argument, flags)
- }
-
- if (types.isArrayBufferView(V)) {
- flags &= ~webidl.attributes.AllowShared
-
- return webidl.converters.ArrayBufferView(V, prefix, argument, flags)
- }
-
- // Make this explicit for easier debugging
- if (types.isSharedArrayBuffer(V)) {
- throw webidl.errors.exception({
- header: prefix,
- message: `${argument} cannot be a SharedArrayBuffer.`
- })
- }
-
- throw webidl.errors.conversionFailed({
- prefix,
- argument: `${argument} ("${webidl.util.Stringify(V)}")`,
- types: ['ArrayBuffer', 'ArrayBufferView']
- })
-}
-
-// https://webidl.spec.whatwg.org/#AllowSharedBufferSource
-webidl.converters.AllowSharedBufferSource = function (V, prefix, argument, flags) {
- if (types.isArrayBuffer(V)) {
- return webidl.converters.ArrayBuffer(V, prefix, argument, flags)
- }
-
- if (types.isSharedArrayBuffer(V)) {
- return webidl.converters.SharedArrayBuffer(V, prefix, argument, flags)
- }
-
- if (types.isArrayBufferView(V)) {
- flags |= webidl.attributes.AllowShared
- return webidl.converters.ArrayBufferView(V, prefix, argument, flags)
- }
-
- throw webidl.errors.conversionFailed({
- prefix,
- argument: `${argument} ("${webidl.util.Stringify(V)}")`,
- types: ['ArrayBuffer', 'SharedArrayBuffer', 'ArrayBufferView']
- })
-}
-
-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
-)
-
-webidl.converters.Blob = webidl.interfaceConverter(webidl.is.Blob, 'Blob')
-
-webidl.converters.AbortSignal = webidl.interfaceConverter(
- webidl.is.AbortSignal,
- 'AbortSignal'
-)
-
-/**
- * [LegacyTreatNonObjectAsNull]
- * callback EventHandlerNonNull = any (Event event);
- * typedef EventHandlerNonNull? EventHandler;
- * @param {*} V
- */
-webidl.converters.EventHandlerNonNull = function (V) {
- if (webidl.util.Type(V) !== OBJECT) {
- return null
- }
-
- // [I]f the value is not an object, it will be converted to null, and if the value is not callable,
- // it will be converted to a callback function value that does nothing when called.
- if (typeof V === 'function') {
- return V
- }
-
- return () => {}
-}
-
-webidl.attributes = {
- Clamp: 1 << 0,
- EnforceRange: 1 << 1,
- AllowShared: 1 << 2,
- AllowResizable: 1 << 3,
- LegacyNullToEmptyString: 1 << 4
-}
-
-module.exports = {
- webidl
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/connection.js b/vanilla/node_modules/undici/lib/web/websocket/connection.js
deleted file mode 100644
index 4ecc8a1..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/connection.js
+++ /dev/null
@@ -1,329 +0,0 @@
-'use strict'
-
-const { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = require('./constants')
-const { parseExtensions, isClosed, isClosing, isEstablished, isConnecting, validateCloseCodeAndReason } = require('./util')
-const { makeRequest } = require('../fetch/request')
-const { fetching } = require('../fetch/index')
-const { Headers, getHeadersList } = require('../fetch/headers')
-const { getDecodeSplit } = require('../fetch/util')
-const { WebsocketFrameSend } = require('./frame')
-const assert = require('node:assert')
-const { runtimeFeatures } = require('../../util/runtime-features')
-
-const crypto = runtimeFeatures.has('crypto')
- ? require('node:crypto')
- : null
-
-let warningEmitted = false
-
-/**
- * @see https://websockets.spec.whatwg.org/#concept-websocket-establish
- * @param {URL} url
- * @param {string|string[]} protocols
- * @param {import('./websocket').Handler} handler
- * @param {Partial<import('../../../types/websocket').WebSocketInit>} options
- */
-function establishWebSocketConnection (url, protocols, client, handler, options) {
- // 1. Let requestURL be a copy of url, with its scheme set to "http", if url’s
- // scheme is "ws", and to "https" otherwise.
- const requestURL = url
-
- requestURL.protocol = url.protocol === 'ws:' ? 'http:' : 'https:'
-
- // 2. Let request be a new request, whose URL is requestURL, client is client,
- // service-workers mode is "none", referrer is "no-referrer", mode is
- // "websocket", credentials mode is "include", cache mode is "no-store" ,
- // redirect mode is "error", and use-URL-credentials flag is set.
- const request = makeRequest({
- urlList: [requestURL],
- client,
- serviceWorkers: 'none',
- referrer: 'no-referrer',
- mode: 'websocket',
- credentials: 'include',
- cache: 'no-store',
- redirect: 'error',
- useURLCredentials: true
- })
-
- // Note: undici extension, allow setting custom headers.
- if (options.headers) {
- const headersList = getHeadersList(new Headers(options.headers))
-
- request.headersList = headersList
- }
-
- // 3. Append (`Upgrade`, `websocket`) to request’s header list.
- // 4. Append (`Connection`, `Upgrade`) to request’s header list.
- // Note: both of these are handled by undici currently.
- // https://github.com/nodejs/undici/blob/68c269c4144c446f3f1220951338daef4a6b5ec4/lib/client.js#L1397
-
- // 5. Let keyValue be a nonce consisting of a randomly selected
- // 16-byte value that has been forgiving-base64-encoded and
- // isomorphic encoded.
- const keyValue = crypto.randomBytes(16).toString('base64')
-
- // 6. Append (`Sec-WebSocket-Key`, keyValue) to request’s
- // header list.
- request.headersList.append('sec-websocket-key', keyValue, true)
-
- // 7. Append (`Sec-WebSocket-Version`, `13`) to request’s
- // header list.
- request.headersList.append('sec-websocket-version', '13', true)
-
- // 8. For each protocol in protocols, combine
- // (`Sec-WebSocket-Protocol`, protocol) in request’s header
- // list.
- for (const protocol of protocols) {
- request.headersList.append('sec-websocket-protocol', protocol, true)
- }
-
- // 9. Let permessageDeflate be a user-agent defined
- // "permessage-deflate" extension header value.
- // https://github.com/mozilla/gecko-dev/blob/ce78234f5e653a5d3916813ff990f053510227bc/netwerk/protocol/websocket/WebSocketChannel.cpp#L2673
- const permessageDeflate = 'permessage-deflate; client_max_window_bits'
-
- // 10. Append (`Sec-WebSocket-Extensions`, permessageDeflate) to
- // request’s header list.
- request.headersList.append('sec-websocket-extensions', permessageDeflate, true)
-
- // 11. Fetch request with useParallelQueue set to true, and
- // processResponse given response being these steps:
- const controller = fetching({
- request,
- useParallelQueue: true,
- dispatcher: options.dispatcher,
- processResponse (response) {
- // 1. If response is a network error or its status is not 101,
- // fail the WebSocket connection.
- // if (response.type === 'error' || ((response.socket?.session != null && response.status !== 200) && response.status !== 101)) {
- if (response.type === 'error' || response.status !== 101) {
- // The presence of a session property on the socket indicates HTTP2
- // HTTP1
- if (response.socket?.session == null) {
- failWebsocketConnection(handler, 1002, 'Received network error or non-101 status code.', response.error)
- return
- }
-
- // HTTP2
- if (response.status !== 200) {
- failWebsocketConnection(handler, 1002, 'Received network error or non-200 status code.', response.error)
- return
- }
- }
-
- if (warningEmitted === false && response.socket?.session != null) {
- process.emitWarning('WebSocket over HTTP2 is experimental, and subject to change.', 'ExperimentalWarning')
- warningEmitted = true
- }
-
- // 2. If protocols is not the empty list and extracting header
- // list values given `Sec-WebSocket-Protocol` and response’s
- // header list results in null, failure, or the empty byte
- // sequence, then fail the WebSocket connection.
- if (protocols.length !== 0 && !response.headersList.get('Sec-WebSocket-Protocol')) {
- failWebsocketConnection(handler, 1002, 'Server did not respond with sent protocols.')
- return
- }
-
- // 3. Follow the requirements stated step 2 to step 6, inclusive,
- // of the last set of steps in section 4.1 of The WebSocket
- // Protocol to validate response. This either results in fail
- // the WebSocket connection or the WebSocket connection is
- // established.
-
- // 2. If the response lacks an |Upgrade| header field or the |Upgrade|
- // header field contains a value that is not an ASCII case-
- // insensitive match for the value "websocket", the client MUST
- // _Fail the WebSocket Connection_.
- // For H2, no upgrade header is expected.
- if (response.socket.session == null && response.headersList.get('Upgrade')?.toLowerCase() !== 'websocket') {
- failWebsocketConnection(handler, 1002, 'Server did not set Upgrade header to "websocket".')
- return
- }
-
- // 3. If the response lacks a |Connection| header field or the
- // |Connection| header field doesn't contain a token that is an
- // ASCII case-insensitive match for the value "Upgrade", the client
- // MUST _Fail the WebSocket Connection_.
- // For H2, no connection header is expected.
- if (response.socket.session == null && response.headersList.get('Connection')?.toLowerCase() !== 'upgrade') {
- failWebsocketConnection(handler, 1002, 'Server did not set Connection header to "upgrade".')
- return
- }
-
- // 4. If the response lacks a |Sec-WebSocket-Accept| header field or
- // the |Sec-WebSocket-Accept| contains a value other than the
- // base64-encoded SHA-1 of the concatenation of the |Sec-WebSocket-
- // Key| (as a string, not base64-decoded) with the string "258EAFA5-
- // E914-47DA-95CA-C5AB0DC85B11" but ignoring any leading and
- // trailing whitespace, the client MUST _Fail the WebSocket
- // Connection_.
- const secWSAccept = response.headersList.get('Sec-WebSocket-Accept')
- const digest = crypto.hash('sha1', keyValue + uid, 'base64')
- if (secWSAccept !== digest) {
- failWebsocketConnection(handler, 1002, 'Incorrect hash received in Sec-WebSocket-Accept header.')
- return
- }
-
- // 5. If the response includes a |Sec-WebSocket-Extensions| header
- // field and this header field indicates the use of an extension
- // that was not present in the client's handshake (the server has
- // indicated an extension not requested by the client), the client
- // MUST _Fail the WebSocket Connection_. (The parsing of this
- // header field to determine which extensions are requested is
- // discussed in Section 9.1.)
- const secExtension = response.headersList.get('Sec-WebSocket-Extensions')
- let extensions
-
- if (secExtension !== null) {
- extensions = parseExtensions(secExtension)
-
- if (!extensions.has('permessage-deflate')) {
- failWebsocketConnection(handler, 1002, 'Sec-WebSocket-Extensions header does not match.')
- return
- }
- }
-
- // 6. If the response includes a |Sec-WebSocket-Protocol| header field
- // and this header field indicates the use of a subprotocol that was
- // not present in the client's handshake (the server has indicated a
- // subprotocol not requested by the client), the client MUST _Fail
- // the WebSocket Connection_.
- const secProtocol = response.headersList.get('Sec-WebSocket-Protocol')
-
- if (secProtocol !== null) {
- const requestProtocols = getDecodeSplit('sec-websocket-protocol', request.headersList)
-
- // The client can request that the server use a specific subprotocol by
- // including the |Sec-WebSocket-Protocol| field in its handshake. If it
- // is specified, the server needs to include the same field and one of
- // the selected subprotocol values in its response for the connection to
- // be established.
- if (!requestProtocols.includes(secProtocol)) {
- failWebsocketConnection(handler, 1002, 'Protocol was not set in the opening handshake.')
- return
- }
- }
-
- response.socket.on('data', handler.onSocketData)
- response.socket.on('close', handler.onSocketClose)
- response.socket.on('error', handler.onSocketError)
-
- handler.wasEverConnected = true
- handler.onConnectionEstablished(response, extensions)
- }
- })
-
- return controller
-}
-
-/**
- * @see https://whatpr.org/websockets/48.html#close-the-websocket
- * @param {import('./websocket').Handler} object
- * @param {number} [code=null]
- * @param {string} [reason='']
- */
-function closeWebSocketConnection (object, code, reason, validate = false) {
- // 1. If code was not supplied, let code be null.
- code ??= null
-
- // 2. If reason was not supplied, let reason be the empty string.
- reason ??= ''
-
- // 3. Validate close code and reason with code and reason.
- if (validate) validateCloseCodeAndReason(code, reason)
-
- // 4. Run the first matching steps from the following list:
- // - If object’s ready state is CLOSING (2) or CLOSED (3)
- // - If the WebSocket connection is not yet established [WSP]
- // - If the WebSocket closing handshake has not yet been started [WSP]
- // - Otherwise
- if (isClosed(object.readyState) || isClosing(object.readyState)) {
- // Do nothing.
- } else if (!isEstablished(object.readyState)) {
- // Fail the WebSocket connection and set object’s ready state to CLOSING (2). [WSP]
- failWebsocketConnection(object)
- object.readyState = states.CLOSING
- } else if (!object.closeState.has(sentCloseFrameState.SENT) && !object.closeState.has(sentCloseFrameState.RECEIVED)) {
- // Upon either sending or receiving a Close control frame, it is said
- // that _The WebSocket Closing Handshake is Started_ and that the
- // WebSocket connection is in the CLOSING state.
-
- const frame = new WebsocketFrameSend()
-
- // If neither code nor reason is present, the WebSocket Close
- // message must not have a body.
-
- // If code is present, then the status code to use in the
- // WebSocket Close message must be the integer given by code.
- // If code is null and reason is the empty string, the WebSocket Close frame must not have a body.
- // If reason is non-empty but code is null, then set code to 1000 ("Normal Closure").
- if (reason.length !== 0 && code === null) {
- code = 1000
- }
-
- // If code is set, then the status code to use in the WebSocket Close frame must be the integer given by code.
- assert(code === null || Number.isInteger(code))
-
- if (code === null && reason.length === 0) {
- frame.frameData = emptyBuffer
- } else if (code !== null && reason === null) {
- frame.frameData = Buffer.allocUnsafe(2)
- frame.frameData.writeUInt16BE(code, 0)
- } else if (code !== null && reason !== null) {
- // If reason is also present, then reasonBytes must be
- // provided in the Close message after the status code.
- frame.frameData = Buffer.allocUnsafe(2 + Buffer.byteLength(reason))
- frame.frameData.writeUInt16BE(code, 0)
- // the body MAY contain UTF-8-encoded data with value /reason/
- frame.frameData.write(reason, 2, 'utf-8')
- } else {
- frame.frameData = emptyBuffer
- }
-
- object.socket.write(frame.createFrame(opcodes.CLOSE))
-
- object.closeState.add(sentCloseFrameState.SENT)
-
- // Upon either sending or receiving a Close control frame, it is said
- // that _The WebSocket Closing Handshake is Started_ and that the
- // WebSocket connection is in the CLOSING state.
- object.readyState = states.CLOSING
- } else {
- // Set object’s ready state to CLOSING (2).
- object.readyState = states.CLOSING
- }
-}
-
-/**
- * @param {import('./websocket').Handler} handler
- * @param {number} code
- * @param {string|undefined} reason
- * @param {unknown} cause
- * @returns {void}
- */
-function failWebsocketConnection (handler, code, reason, cause) {
- // If _The WebSocket Connection is Established_ prior to the point where
- // the endpoint is required to _Fail the WebSocket Connection_, the
- // endpoint SHOULD send a Close frame with an appropriate status code
- // (Section 7.4) before proceeding to _Close the WebSocket Connection_.
- if (isEstablished(handler.readyState)) {
- closeWebSocketConnection(handler, code, reason, false)
- }
-
- handler.controller.abort()
-
- if (isConnecting(handler.readyState)) {
- // If the connection was not established, we must still emit an 'error' and 'close' events
- handler.onSocketClose()
- } else if (handler.socket?.destroyed === false) {
- handler.socket.destroy()
- }
-}
-
-module.exports = {
- establishWebSocketConnection,
- failWebsocketConnection,
- closeWebSocketConnection
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/constants.js b/vanilla/node_modules/undici/lib/web/websocket/constants.js
deleted file mode 100644
index e4e6990..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/constants.js
+++ /dev/null
@@ -1,126 +0,0 @@
-'use strict'
-
-/**
- * This is a Globally Unique Identifier unique used to validate that the
- * endpoint accepts websocket connections.
- * @see https://www.rfc-editor.org/rfc/rfc6455.html#section-1.3
- * @type {'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'}
- */
-const uid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
-
-/**
- * @type {PropertyDescriptor}
- */
-const staticPropertyDescriptors = {
- enumerable: true,
- writable: false,
- configurable: false
-}
-
-/**
- * The states of the WebSocket connection.
- *
- * @readonly
- * @enum
- * @property {0} CONNECTING
- * @property {1} OPEN
- * @property {2} CLOSING
- * @property {3} CLOSED
- */
-const states = {
- CONNECTING: 0,
- OPEN: 1,
- CLOSING: 2,
- CLOSED: 3
-}
-
-/**
- * @readonly
- * @enum
- * @property {0} NOT_SENT
- * @property {1} PROCESSING
- * @property {2} SENT
- */
-const sentCloseFrameState = {
- SENT: 1,
- RECEIVED: 2
-}
-
-/**
- * The WebSocket opcodes.
- *
- * @readonly
- * @enum
- * @property {0x0} CONTINUATION
- * @property {0x1} TEXT
- * @property {0x2} BINARY
- * @property {0x8} CLOSE
- * @property {0x9} PING
- * @property {0xA} PONG
- * @see https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
- */
-const opcodes = {
- CONTINUATION: 0x0,
- TEXT: 0x1,
- BINARY: 0x2,
- CLOSE: 0x8,
- PING: 0x9,
- PONG: 0xA
-}
-
-/**
- * The maximum value for an unsigned 16-bit integer.
- *
- * @type {65535} 2 ** 16 - 1
- */
-const maxUnsigned16Bit = 65535
-
-/**
- * The states of the parser.
- *
- * @readonly
- * @enum
- * @property {0} INFO
- * @property {2} PAYLOADLENGTH_16
- * @property {3} PAYLOADLENGTH_64
- * @property {4} READ_DATA
- */
-const parserStates = {
- INFO: 0,
- PAYLOADLENGTH_16: 2,
- PAYLOADLENGTH_64: 3,
- READ_DATA: 4
-}
-
-/**
- * An empty buffer.
- *
- * @type {Buffer}
- */
-const emptyBuffer = Buffer.allocUnsafe(0)
-
-/**
- * @readonly
- * @property {1} text
- * @property {2} typedArray
- * @property {3} arrayBuffer
- * @property {4} blob
- */
-const sendHints = {
- text: 1,
- typedArray: 2,
- arrayBuffer: 3,
- blob: 4
-}
-
-module.exports = {
- uid,
- sentCloseFrameState,
- staticPropertyDescriptors,
- states,
- opcodes,
- maxUnsigned16Bit,
- parserStates,
- emptyBuffer,
- sendHints
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/events.js b/vanilla/node_modules/undici/lib/web/websocket/events.js
deleted file mode 100644
index 7ac9566..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/events.js
+++ /dev/null
@@ -1,331 +0,0 @@
-'use strict'
-
-const { webidl } = require('../webidl')
-const { kEnumerableProperty } = require('../../core/util')
-const { kConstruct } = require('../../core/symbols')
-
-/**
- * @see https://html.spec.whatwg.org/multipage/comms.html#messageevent
- */
-class MessageEvent extends Event {
- #eventInit
-
- constructor (type, eventInitDict = {}) {
- if (type === kConstruct) {
- super(arguments[1], arguments[2])
- webidl.util.markAsUncloneable(this)
- return
- }
-
- const prefix = 'MessageEvent constructor'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- type = webidl.converters.DOMString(type, prefix, 'type')
- eventInitDict = webidl.converters.MessageEventInit(eventInitDict, prefix, 'eventInitDict')
-
- super(type, eventInitDict)
-
- this.#eventInit = eventInitDict
- webidl.util.markAsUncloneable(this)
- }
-
- get data () {
- webidl.brandCheck(this, MessageEvent)
-
- return this.#eventInit.data
- }
-
- get origin () {
- webidl.brandCheck(this, MessageEvent)
-
- return this.#eventInit.origin
- }
-
- get lastEventId () {
- webidl.brandCheck(this, MessageEvent)
-
- return this.#eventInit.lastEventId
- }
-
- get source () {
- webidl.brandCheck(this, MessageEvent)
-
- return this.#eventInit.source
- }
-
- get ports () {
- webidl.brandCheck(this, MessageEvent)
-
- if (!Object.isFrozen(this.#eventInit.ports)) {
- Object.freeze(this.#eventInit.ports)
- }
-
- return this.#eventInit.ports
- }
-
- initMessageEvent (
- type,
- bubbles = false,
- cancelable = false,
- data = null,
- origin = '',
- lastEventId = '',
- source = null,
- ports = []
- ) {
- webidl.brandCheck(this, MessageEvent)
-
- webidl.argumentLengthCheck(arguments, 1, 'MessageEvent.initMessageEvent')
-
- return new MessageEvent(type, {
- bubbles, cancelable, data, origin, lastEventId, source, ports
- })
- }
-
- static createFastMessageEvent (type, init) {
- const messageEvent = new MessageEvent(kConstruct, type, init)
- messageEvent.#eventInit = init
- messageEvent.#eventInit.data ??= null
- messageEvent.#eventInit.origin ??= ''
- messageEvent.#eventInit.lastEventId ??= ''
- messageEvent.#eventInit.source ??= null
- messageEvent.#eventInit.ports ??= []
- return messageEvent
- }
-}
-
-const { createFastMessageEvent } = MessageEvent
-delete MessageEvent.createFastMessageEvent
-
-/**
- * @see https://websockets.spec.whatwg.org/#the-closeevent-interface
- */
-class CloseEvent extends Event {
- #eventInit
-
- constructor (type, eventInitDict = {}) {
- const prefix = 'CloseEvent constructor'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- type = webidl.converters.DOMString(type, prefix, 'type')
- eventInitDict = webidl.converters.CloseEventInit(eventInitDict)
-
- super(type, eventInitDict)
-
- this.#eventInit = eventInitDict
- webidl.util.markAsUncloneable(this)
- }
-
- get wasClean () {
- webidl.brandCheck(this, CloseEvent)
-
- return this.#eventInit.wasClean
- }
-
- get code () {
- webidl.brandCheck(this, CloseEvent)
-
- return this.#eventInit.code
- }
-
- get reason () {
- webidl.brandCheck(this, CloseEvent)
-
- return this.#eventInit.reason
- }
-}
-
-// https://html.spec.whatwg.org/multipage/webappapis.html#the-errorevent-interface
-class ErrorEvent extends Event {
- #eventInit
-
- constructor (type, eventInitDict) {
- const prefix = 'ErrorEvent constructor'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- super(type, eventInitDict)
- webidl.util.markAsUncloneable(this)
-
- type = webidl.converters.DOMString(type, prefix, 'type')
- eventInitDict = webidl.converters.ErrorEventInit(eventInitDict ?? {})
-
- this.#eventInit = eventInitDict
- }
-
- get message () {
- webidl.brandCheck(this, ErrorEvent)
-
- return this.#eventInit.message
- }
-
- get filename () {
- webidl.brandCheck(this, ErrorEvent)
-
- return this.#eventInit.filename
- }
-
- get lineno () {
- webidl.brandCheck(this, ErrorEvent)
-
- return this.#eventInit.lineno
- }
-
- get colno () {
- webidl.brandCheck(this, ErrorEvent)
-
- return this.#eventInit.colno
- }
-
- get error () {
- webidl.brandCheck(this, ErrorEvent)
-
- return this.#eventInit.error
- }
-}
-
-Object.defineProperties(MessageEvent.prototype, {
- [Symbol.toStringTag]: {
- value: 'MessageEvent',
- configurable: true
- },
- data: kEnumerableProperty,
- origin: kEnumerableProperty,
- lastEventId: kEnumerableProperty,
- source: kEnumerableProperty,
- ports: kEnumerableProperty,
- initMessageEvent: kEnumerableProperty
-})
-
-Object.defineProperties(CloseEvent.prototype, {
- [Symbol.toStringTag]: {
- value: 'CloseEvent',
- configurable: true
- },
- reason: kEnumerableProperty,
- code: kEnumerableProperty,
- wasClean: kEnumerableProperty
-})
-
-Object.defineProperties(ErrorEvent.prototype, {
- [Symbol.toStringTag]: {
- value: 'ErrorEvent',
- configurable: true
- },
- message: kEnumerableProperty,
- filename: kEnumerableProperty,
- lineno: kEnumerableProperty,
- colno: kEnumerableProperty,
- error: kEnumerableProperty
-})
-
-webidl.converters.MessagePort = webidl.interfaceConverter(
- webidl.is.MessagePort,
- 'MessagePort'
-)
-
-webidl.converters['sequence<MessagePort>'] = webidl.sequenceConverter(
- webidl.converters.MessagePort
-)
-
-const eventInit = [
- {
- key: 'bubbles',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- },
- {
- key: 'cancelable',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- },
- {
- key: 'composed',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- }
-]
-
-webidl.converters.MessageEventInit = webidl.dictionaryConverter([
- ...eventInit,
- {
- key: 'data',
- converter: webidl.converters.any,
- defaultValue: () => null
- },
- {
- key: 'origin',
- converter: webidl.converters.USVString,
- defaultValue: () => ''
- },
- {
- key: 'lastEventId',
- converter: webidl.converters.DOMString,
- defaultValue: () => ''
- },
- {
- key: 'source',
- // Node doesn't implement WindowProxy or ServiceWorker, so the only
- // valid value for source is a MessagePort.
- converter: webidl.nullableConverter(webidl.converters.MessagePort),
- defaultValue: () => null
- },
- {
- key: 'ports',
- converter: webidl.converters['sequence<MessagePort>'],
- defaultValue: () => []
- }
-])
-
-webidl.converters.CloseEventInit = webidl.dictionaryConverter([
- ...eventInit,
- {
- key: 'wasClean',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- },
- {
- key: 'code',
- converter: webidl.converters['unsigned short'],
- defaultValue: () => 0
- },
- {
- key: 'reason',
- converter: webidl.converters.USVString,
- defaultValue: () => ''
- }
-])
-
-webidl.converters.ErrorEventInit = webidl.dictionaryConverter([
- ...eventInit,
- {
- key: 'message',
- converter: webidl.converters.DOMString,
- defaultValue: () => ''
- },
- {
- key: 'filename',
- converter: webidl.converters.USVString,
- defaultValue: () => ''
- },
- {
- key: 'lineno',
- converter: webidl.converters['unsigned long'],
- defaultValue: () => 0
- },
- {
- key: 'colno',
- converter: webidl.converters['unsigned long'],
- defaultValue: () => 0
- },
- {
- key: 'error',
- converter: webidl.converters.any
- }
-])
-
-module.exports = {
- MessageEvent,
- CloseEvent,
- ErrorEvent,
- createFastMessageEvent
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/frame.js b/vanilla/node_modules/undici/lib/web/websocket/frame.js
deleted file mode 100644
index e397c87..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/frame.js
+++ /dev/null
@@ -1,133 +0,0 @@
-'use strict'
-
-const { runtimeFeatures } = require('../../util/runtime-features')
-const { maxUnsigned16Bit, opcodes } = require('./constants')
-
-const BUFFER_SIZE = 8 * 1024
-
-let buffer = null
-let bufIdx = BUFFER_SIZE
-
-const randomFillSync = runtimeFeatures.has('crypto')
- ? require('node:crypto').randomFillSync
- // not full compatibility, but minimum.
- : function randomFillSync (buffer, _offset, _size) {
- for (let i = 0; i < buffer.length; ++i) {
- buffer[i] = Math.random() * 255 | 0
- }
- return buffer
- }
-
-function generateMask () {
- if (bufIdx === BUFFER_SIZE) {
- bufIdx = 0
- randomFillSync((buffer ??= Buffer.allocUnsafeSlow(BUFFER_SIZE)), 0, BUFFER_SIZE)
- }
- return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]]
-}
-
-class WebsocketFrameSend {
- /**
- * @param {Buffer|undefined} data
- */
- constructor (data) {
- this.frameData = data
- }
-
- createFrame (opcode) {
- const frameData = this.frameData
- const maskKey = generateMask()
- const bodyLength = frameData?.byteLength ?? 0
-
- /** @type {number} */
- let payloadLength = bodyLength // 0-125
- let offset = 6
-
- if (bodyLength > maxUnsigned16Bit) {
- offset += 8 // payload length is next 8 bytes
- payloadLength = 127
- } else if (bodyLength > 125) {
- offset += 2 // payload length is next 2 bytes
- payloadLength = 126
- }
-
- const buffer = Buffer.allocUnsafe(bodyLength + offset)
-
- // Clear first 2 bytes, everything else is overwritten
- buffer[0] = buffer[1] = 0
- buffer[0] |= 0x80 // FIN
- buffer[0] = (buffer[0] & 0xF0) + opcode // opcode
-
- /*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> */
- buffer[offset - 4] = maskKey[0]
- buffer[offset - 3] = maskKey[1]
- buffer[offset - 2] = maskKey[2]
- buffer[offset - 1] = maskKey[3]
-
- buffer[1] = payloadLength
-
- if (payloadLength === 126) {
- buffer.writeUInt16BE(bodyLength, 2)
- } else if (payloadLength === 127) {
- // Clear extended payload length
- buffer[2] = buffer[3] = 0
- buffer.writeUIntBE(bodyLength, 4, 6)
- }
-
- buffer[1] |= 0x80 // MASK
-
- // mask body
- for (let i = 0; i < bodyLength; ++i) {
- buffer[offset + i] = frameData[i] ^ maskKey[i & 3]
- }
-
- return buffer
- }
-
- /**
- * @param {Uint8Array} buffer
- */
- static createFastTextFrame (buffer) {
- const maskKey = generateMask()
-
- const bodyLength = buffer.length
-
- // mask body
- for (let i = 0; i < bodyLength; ++i) {
- buffer[i] ^= maskKey[i & 3]
- }
-
- let payloadLength = bodyLength
- let offset = 6
-
- if (bodyLength > maxUnsigned16Bit) {
- offset += 8 // payload length is next 8 bytes
- payloadLength = 127
- } else if (bodyLength > 125) {
- offset += 2 // payload length is next 2 bytes
- payloadLength = 126
- }
- const head = Buffer.allocUnsafeSlow(offset)
-
- head[0] = 0x80 /* FIN */ | opcodes.TEXT /* opcode TEXT */
- head[1] = payloadLength | 0x80 /* MASK */
- head[offset - 4] = maskKey[0]
- head[offset - 3] = maskKey[1]
- head[offset - 2] = maskKey[2]
- head[offset - 1] = maskKey[3]
-
- if (payloadLength === 126) {
- head.writeUInt16BE(bodyLength, 2)
- } else if (payloadLength === 127) {
- head[2] = head[3] = 0
- head.writeUIntBE(bodyLength, 4, 6)
- }
-
- return [head, buffer]
- }
-}
-
-module.exports = {
- WebsocketFrameSend,
- generateMask // for benchmark
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/permessage-deflate.js b/vanilla/node_modules/undici/lib/web/websocket/permessage-deflate.js
deleted file mode 100644
index 76cb366..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/permessage-deflate.js
+++ /dev/null
@@ -1,70 +0,0 @@
-'use strict'
-
-const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = require('node:zlib')
-const { isValidClientWindowBits } = require('./util')
-
-const tail = Buffer.from([0x00, 0x00, 0xff, 0xff])
-const kBuffer = Symbol('kBuffer')
-const kLength = Symbol('kLength')
-
-class PerMessageDeflate {
- /** @type {import('node:zlib').InflateRaw} */
- #inflate
-
- #options = {}
-
- constructor (extensions) {
- this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')
- this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')
- }
-
- decompress (chunk, fin, callback) {
- // An endpoint uses the following algorithm to decompress a message.
- // 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
- // payload of the message.
- // 2. Decompress the resulting data using DEFLATE.
-
- if (!this.#inflate) {
- let windowBits = Z_DEFAULT_WINDOWBITS
-
- if (this.#options.serverMaxWindowBits) { // empty values default to Z_DEFAULT_WINDOWBITS
- if (!isValidClientWindowBits(this.#options.serverMaxWindowBits)) {
- callback(new Error('Invalid server_max_window_bits'))
- return
- }
-
- windowBits = Number.parseInt(this.#options.serverMaxWindowBits)
- }
-
- this.#inflate = createInflateRaw({ windowBits })
- this.#inflate[kBuffer] = []
- this.#inflate[kLength] = 0
-
- this.#inflate.on('data', (data) => {
- this.#inflate[kBuffer].push(data)
- this.#inflate[kLength] += data.length
- })
-
- this.#inflate.on('error', (err) => {
- this.#inflate = null
- callback(err)
- })
- }
-
- this.#inflate.write(chunk)
- if (fin) {
- this.#inflate.write(tail)
- }
-
- this.#inflate.flush(() => {
- const full = Buffer.concat(this.#inflate[kBuffer], this.#inflate[kLength])
-
- this.#inflate[kBuffer].length = 0
- this.#inflate[kLength] = 0
-
- callback(null, full)
- })
- }
-}
-
-module.exports = { PerMessageDeflate }
diff --git a/vanilla/node_modules/undici/lib/web/websocket/receiver.js b/vanilla/node_modules/undici/lib/web/websocket/receiver.js
deleted file mode 100644
index ba0a5aa..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/receiver.js
+++ /dev/null
@@ -1,444 +0,0 @@
-'use strict'
-
-const { Writable } = require('node:stream')
-const assert = require('node:assert')
-const { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = require('./constants')
-const {
- isValidStatusCode,
- isValidOpcode,
- websocketMessageReceived,
- utf8Decode,
- isControlFrame,
- isTextBinaryFrame,
- isContinuationFrame
-} = require('./util')
-const { failWebsocketConnection } = require('./connection')
-const { WebsocketFrameSend } = require('./frame')
-const { PerMessageDeflate } = require('./permessage-deflate')
-
-// This code was influenced by ws released under the MIT license.
-// Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
-// Copyright (c) 2013 Arnout Kazemier and contributors
-// Copyright (c) 2016 Luigi Pinca and contributors
-
-class ByteParser extends Writable {
- #buffers = []
- #fragmentsBytes = 0
- #byteOffset = 0
- #loop = false
-
- #state = parserStates.INFO
-
- #info = {}
- #fragments = []
-
- /** @type {Map<string, PerMessageDeflate>} */
- #extensions
-
- /** @type {import('./websocket').Handler} */
- #handler
-
- constructor (handler, extensions) {
- super()
-
- this.#handler = handler
- this.#extensions = extensions == null ? new Map() : extensions
-
- if (this.#extensions.has('permessage-deflate')) {
- this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions))
- }
- }
-
- /**
- * @param {Buffer} chunk
- * @param {() => void} callback
- */
- _write (chunk, _, callback) {
- this.#buffers.push(chunk)
- this.#byteOffset += chunk.length
- this.#loop = true
-
- this.run(callback)
- }
-
- /**
- * Runs whenever a new chunk is received.
- * Callback is called whenever there are no more chunks buffering,
- * or not enough bytes are buffered to parse.
- */
- run (callback) {
- while (this.#loop) {
- if (this.#state === parserStates.INFO) {
- // If there aren't enough bytes to parse the payload length, etc.
- if (this.#byteOffset < 2) {
- return callback()
- }
-
- const buffer = this.consume(2)
- const fin = (buffer[0] & 0x80) !== 0
- const opcode = buffer[0] & 0x0F
- const masked = (buffer[1] & 0x80) === 0x80
-
- const fragmented = !fin && opcode !== opcodes.CONTINUATION
- const payloadLength = buffer[1] & 0x7F
-
- const rsv1 = buffer[0] & 0x40
- const rsv2 = buffer[0] & 0x20
- const rsv3 = buffer[0] & 0x10
-
- if (!isValidOpcode(opcode)) {
- failWebsocketConnection(this.#handler, 1002, 'Invalid opcode received')
- return callback()
- }
-
- if (masked) {
- failWebsocketConnection(this.#handler, 1002, 'Frame cannot be masked')
- return callback()
- }
-
- // MUST be 0 unless an extension is negotiated that defines meanings
- // for non-zero values. If a nonzero value is received and none of
- // the negotiated extensions defines the meaning of such a nonzero
- // value, the receiving endpoint MUST _Fail the WebSocket
- // Connection_.
- // This document allocates the RSV1 bit of the WebSocket header for
- // PMCEs and calls the bit the "Per-Message Compressed" bit. On a
- // WebSocket connection where a PMCE is in use, this bit indicates
- // whether a message is compressed or not.
- if (rsv1 !== 0 && !this.#extensions.has('permessage-deflate')) {
- failWebsocketConnection(this.#handler, 1002, 'Expected RSV1 to be clear.')
- return
- }
-
- if (rsv2 !== 0 || rsv3 !== 0) {
- failWebsocketConnection(this.#handler, 1002, 'RSV1, RSV2, RSV3 must be clear')
- return
- }
-
- if (fragmented && !isTextBinaryFrame(opcode)) {
- // Only text and binary frames can be fragmented
- failWebsocketConnection(this.#handler, 1002, 'Invalid frame type was fragmented.')
- return
- }
-
- // If we are already parsing a text/binary frame and do not receive either
- // a continuation frame or close frame, fail the connection.
- if (isTextBinaryFrame(opcode) && this.#fragments.length > 0) {
- failWebsocketConnection(this.#handler, 1002, 'Expected continuation frame')
- return
- }
-
- if (this.#info.fragmented && fragmented) {
- // A fragmented frame can't be fragmented itself
- failWebsocketConnection(this.#handler, 1002, 'Fragmented frame exceeded 125 bytes.')
- return
- }
-
- // "All control frames MUST have a payload length of 125 bytes or less
- // and MUST NOT be fragmented."
- if ((payloadLength > 125 || fragmented) && isControlFrame(opcode)) {
- failWebsocketConnection(this.#handler, 1002, 'Control frame either too large or fragmented')
- return
- }
-
- if (isContinuationFrame(opcode) && this.#fragments.length === 0 && !this.#info.compressed) {
- failWebsocketConnection(this.#handler, 1002, 'Unexpected continuation frame')
- return
- }
-
- if (payloadLength <= 125) {
- this.#info.payloadLength = payloadLength
- this.#state = parserStates.READ_DATA
- } else if (payloadLength === 126) {
- this.#state = parserStates.PAYLOADLENGTH_16
- } else if (payloadLength === 127) {
- this.#state = parserStates.PAYLOADLENGTH_64
- }
-
- if (isTextBinaryFrame(opcode)) {
- this.#info.binaryType = opcode
- this.#info.compressed = rsv1 !== 0
- }
-
- this.#info.opcode = opcode
- this.#info.masked = masked
- this.#info.fin = fin
- this.#info.fragmented = fragmented
- } else if (this.#state === parserStates.PAYLOADLENGTH_16) {
- if (this.#byteOffset < 2) {
- return callback()
- }
-
- const buffer = this.consume(2)
-
- this.#info.payloadLength = buffer.readUInt16BE(0)
- this.#state = parserStates.READ_DATA
- } else if (this.#state === parserStates.PAYLOADLENGTH_64) {
- if (this.#byteOffset < 8) {
- return callback()
- }
-
- const buffer = this.consume(8)
- const upper = buffer.readUInt32BE(0)
-
- // 2^31 is the maximum bytes an arraybuffer can contain
- // on 32-bit systems. Although, on 64-bit systems, this is
- // 2^53-1 bytes.
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length
- // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/globals.h;drc=1946212ac0100668f14eb9e2843bdd846e510a1e;bpv=1;bpt=1;l=1275
- // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-array-buffer.h;l=34;drc=1946212ac0100668f14eb9e2843bdd846e510a1e
- if (upper > 2 ** 31 - 1) {
- failWebsocketConnection(this.#handler, 1009, 'Received payload length > 2^31 bytes.')
- return
- }
-
- const lower = buffer.readUInt32BE(4)
-
- this.#info.payloadLength = (upper << 8) + lower
- this.#state = parserStates.READ_DATA
- } else if (this.#state === parserStates.READ_DATA) {
- if (this.#byteOffset < this.#info.payloadLength) {
- return callback()
- }
-
- const body = this.consume(this.#info.payloadLength)
-
- if (isControlFrame(this.#info.opcode)) {
- this.#loop = this.parseControlFrame(body)
- this.#state = parserStates.INFO
- } else {
- if (!this.#info.compressed) {
- this.writeFragments(body)
-
- // If the frame is not fragmented, a message has been received.
- // If the frame is fragmented, it will terminate with a fin bit set
- // and an opcode of 0 (continuation), therefore we handle that when
- // parsing continuation frames, not here.
- if (!this.#info.fragmented && this.#info.fin) {
- websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments())
- }
-
- this.#state = parserStates.INFO
- } else {
- this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => {
- if (error) {
- failWebsocketConnection(this.#handler, 1007, error.message)
- return
- }
-
- this.writeFragments(data)
-
- if (!this.#info.fin) {
- this.#state = parserStates.INFO
- this.#loop = true
- this.run(callback)
- return
- }
-
- websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments())
-
- this.#loop = true
- this.#state = parserStates.INFO
- this.run(callback)
- })
-
- this.#loop = false
- break
- }
- }
- }
- }
- }
-
- /**
- * Take n bytes from the buffered Buffers
- * @param {number} n
- * @returns {Buffer}
- */
- consume (n) {
- if (n > this.#byteOffset) {
- throw new Error('Called consume() before buffers satiated.')
- } else if (n === 0) {
- return emptyBuffer
- }
-
- this.#byteOffset -= n
-
- const first = this.#buffers[0]
-
- if (first.length > n) {
- // replace with remaining buffer
- this.#buffers[0] = first.subarray(n, first.length)
- return first.subarray(0, n)
- } else if (first.length === n) {
- // prefect match
- return this.#buffers.shift()
- } else {
- let offset = 0
- // If Buffer.allocUnsafe is used, extra copies will be made because the offset is non-zero.
- const buffer = Buffer.allocUnsafeSlow(n)
- while (offset !== n) {
- const next = this.#buffers[0]
- const length = next.length
-
- if (length + offset === n) {
- buffer.set(this.#buffers.shift(), offset)
- break
- } else if (length + offset > n) {
- buffer.set(next.subarray(0, n - offset), offset)
- this.#buffers[0] = next.subarray(n - offset)
- break
- } else {
- buffer.set(this.#buffers.shift(), offset)
- offset += length
- }
- }
-
- return buffer
- }
- }
-
- writeFragments (fragment) {
- this.#fragmentsBytes += fragment.length
- this.#fragments.push(fragment)
- }
-
- consumeFragments () {
- const fragments = this.#fragments
-
- if (fragments.length === 1) {
- // single fragment
- this.#fragmentsBytes = 0
- return fragments.shift()
- }
-
- let offset = 0
- // If Buffer.allocUnsafe is used, extra copies will be made because the offset is non-zero.
- const output = Buffer.allocUnsafeSlow(this.#fragmentsBytes)
-
- for (let i = 0; i < fragments.length; ++i) {
- const buffer = fragments[i]
- output.set(buffer, offset)
- offset += buffer.length
- }
-
- this.#fragments = []
- this.#fragmentsBytes = 0
-
- return output
- }
-
- parseCloseBody (data) {
- assert(data.length !== 1)
-
- // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5
- /** @type {number|undefined} */
- let code
-
- if (data.length >= 2) {
- // _The WebSocket Connection Close Code_ is
- // defined as the status code (Section 7.4) contained in the first Close
- // control frame received by the application
- code = data.readUInt16BE(0)
- }
-
- if (code !== undefined && !isValidStatusCode(code)) {
- return { code: 1002, reason: 'Invalid status code', error: true }
- }
-
- // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6
- /** @type {Buffer} */
- let reason = data.subarray(2)
-
- // Remove BOM
- if (reason[0] === 0xEF && reason[1] === 0xBB && reason[2] === 0xBF) {
- reason = reason.subarray(3)
- }
-
- try {
- reason = utf8Decode(reason)
- } catch {
- return { code: 1007, reason: 'Invalid UTF-8', error: true }
- }
-
- return { code, reason, error: false }
- }
-
- /**
- * Parses control frames.
- * @param {Buffer} body
- */
- parseControlFrame (body) {
- const { opcode, payloadLength } = this.#info
-
- if (opcode === opcodes.CLOSE) {
- if (payloadLength === 1) {
- failWebsocketConnection(this.#handler, 1002, 'Received close frame with a 1-byte body.')
- return false
- }
-
- this.#info.closeInfo = this.parseCloseBody(body)
-
- if (this.#info.closeInfo.error) {
- const { code, reason } = this.#info.closeInfo
-
- failWebsocketConnection(this.#handler, code, reason)
- return false
- }
-
- // Upon receiving such a frame, the other peer sends a
- // Close frame in response, if it hasn't already sent one.
- if (!this.#handler.closeState.has(sentCloseFrameState.SENT) && !this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
- // If an endpoint receives a Close frame and did not previously send a
- // Close frame, the endpoint MUST send a Close frame in response. (When
- // sending a Close frame in response, the endpoint typically echos the
- // status code it received.)
- let body = emptyBuffer
- if (this.#info.closeInfo.code) {
- body = Buffer.allocUnsafe(2)
- body.writeUInt16BE(this.#info.closeInfo.code, 0)
- }
- const closeFrame = new WebsocketFrameSend(body)
-
- this.#handler.socket.write(closeFrame.createFrame(opcodes.CLOSE))
- this.#handler.closeState.add(sentCloseFrameState.SENT)
- }
-
- // Upon either sending or receiving a Close control frame, it is said
- // that _The WebSocket Closing Handshake is Started_ and that the
- // WebSocket connection is in the CLOSING state.
- this.#handler.readyState = states.CLOSING
- this.#handler.closeState.add(sentCloseFrameState.RECEIVED)
-
- return false
- } else if (opcode === opcodes.PING) {
- // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
- // response, unless it already received a Close frame.
- // A Pong frame sent in response to a Ping frame must have identical
- // "Application data"
-
- if (!this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
- const frame = new WebsocketFrameSend(body)
-
- this.#handler.socket.write(frame.createFrame(opcodes.PONG))
-
- this.#handler.onPing(body)
- }
- } else if (opcode === opcodes.PONG) {
- // A Pong frame MAY be sent unsolicited. This serves as a
- // unidirectional heartbeat. A response to an unsolicited Pong frame is
- // not expected.
- this.#handler.onPong(body)
- }
-
- return true
- }
-
- get closingInfo () {
- return this.#info.closeInfo
- }
-}
-
-module.exports = {
- ByteParser
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/sender.js b/vanilla/node_modules/undici/lib/web/websocket/sender.js
deleted file mode 100644
index c647bf6..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/sender.js
+++ /dev/null
@@ -1,109 +0,0 @@
-'use strict'
-
-const { WebsocketFrameSend } = require('./frame')
-const { opcodes, sendHints } = require('./constants')
-const FixedQueue = require('../../dispatcher/fixed-queue')
-
-/**
- * @typedef {object} SendQueueNode
- * @property {Promise<void> | null} promise
- * @property {((...args: any[]) => any)} callback
- * @property {Buffer | null} frame
- */
-
-class SendQueue {
- /**
- * @type {FixedQueue}
- */
- #queue = new FixedQueue()
-
- /**
- * @type {boolean}
- */
- #running = false
-
- /** @type {import('node:net').Socket} */
- #socket
-
- constructor (socket) {
- this.#socket = socket
- }
-
- add (item, cb, hint) {
- if (hint !== sendHints.blob) {
- if (!this.#running) {
- // TODO(@tsctx): support fast-path for string on running
- if (hint === sendHints.text) {
- // special fast-path for string
- const { 0: head, 1: body } = WebsocketFrameSend.createFastTextFrame(item)
- this.#socket.cork()
- this.#socket.write(head)
- this.#socket.write(body, cb)
- this.#socket.uncork()
- } else {
- // direct writing
- this.#socket.write(createFrame(item, hint), cb)
- }
- } else {
- /** @type {SendQueueNode} */
- const node = {
- promise: null,
- callback: cb,
- frame: createFrame(item, hint)
- }
- this.#queue.push(node)
- }
- return
- }
-
- /** @type {SendQueueNode} */
- const node = {
- promise: item.arrayBuffer().then((ab) => {
- node.promise = null
- node.frame = createFrame(ab, hint)
- }),
- callback: cb,
- frame: null
- }
-
- this.#queue.push(node)
-
- if (!this.#running) {
- this.#run()
- }
- }
-
- async #run () {
- this.#running = true
- const queue = this.#queue
- while (!queue.isEmpty()) {
- const node = queue.shift()
- // wait pending promise
- if (node.promise !== null) {
- await node.promise
- }
- // write
- this.#socket.write(node.frame, node.callback)
- // cleanup
- node.callback = node.frame = null
- }
- this.#running = false
- }
-}
-
-function createFrame (data, hint) {
- return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.text ? opcodes.TEXT : opcodes.BINARY)
-}
-
-function toBuffer (data, hint) {
- switch (hint) {
- case sendHints.text:
- case sendHints.typedArray:
- return new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
- case sendHints.arrayBuffer:
- case sendHints.blob:
- return new Uint8Array(data)
- }
-}
-
-module.exports = { SendQueue }
diff --git a/vanilla/node_modules/undici/lib/web/websocket/stream/websocketerror.js b/vanilla/node_modules/undici/lib/web/websocket/stream/websocketerror.js
deleted file mode 100644
index a34c521..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/stream/websocketerror.js
+++ /dev/null
@@ -1,104 +0,0 @@
-'use strict'
-
-const { webidl } = require('../../webidl')
-const { validateCloseCodeAndReason } = require('../util')
-const { kConstruct } = require('../../../core/symbols')
-const { kEnumerableProperty } = require('../../../core/util')
-
-function createInheritableDOMException () {
- // https://github.com/nodejs/node/issues/59677
- class Test extends DOMException {
- get reason () {
- return ''
- }
- }
-
- if (new Test().reason !== undefined) {
- return DOMException
- }
-
- return new Proxy(DOMException, {
- construct (target, args, newTarget) {
- const instance = Reflect.construct(target, args, target)
- Object.setPrototypeOf(instance, newTarget.prototype)
- return instance
- }
- })
-}
-
-class WebSocketError extends createInheritableDOMException() {
- #closeCode
- #reason
-
- constructor (message = '', init = undefined) {
- message = webidl.converters.DOMString(message, 'WebSocketError', 'message')
-
- // 1. Set this 's name to " WebSocketError ".
- // 2. Set this 's message to message .
- super(message, 'WebSocketError')
-
- if (init === kConstruct) {
- return
- } else if (init !== null) {
- init = webidl.converters.WebSocketCloseInfo(init)
- }
-
- // 3. Let code be init [" closeCode "] if it exists , or null otherwise.
- let code = init.closeCode ?? null
-
- // 4. Let reason be init [" reason "] if it exists , or the empty string otherwise.
- const reason = init.reason ?? ''
-
- // 5. Validate close code and reason with code and reason .
- validateCloseCodeAndReason(code, reason)
-
- // 6. If reason is non-empty, but code is not set, then set code to 1000 ("Normal Closure").
- if (reason.length !== 0 && code === null) {
- code = 1000
- }
-
- // 7. Set this 's closeCode to code .
- this.#closeCode = code
-
- // 8. Set this 's reason to reason .
- this.#reason = reason
- }
-
- get closeCode () {
- return this.#closeCode
- }
-
- get reason () {
- return this.#reason
- }
-
- /**
- * @param {string} message
- * @param {number|null} code
- * @param {string} reason
- */
- static createUnvalidatedWebSocketError (message, code, reason) {
- const error = new WebSocketError(message, kConstruct)
- error.#closeCode = code
- error.#reason = reason
- return error
- }
-}
-
-const { createUnvalidatedWebSocketError } = WebSocketError
-delete WebSocketError.createUnvalidatedWebSocketError
-
-Object.defineProperties(WebSocketError.prototype, {
- closeCode: kEnumerableProperty,
- reason: kEnumerableProperty,
- [Symbol.toStringTag]: {
- value: 'WebSocketError',
- writable: false,
- enumerable: false,
- configurable: true
- }
-})
-
-webidl.is.WebSocketError = webidl.util.MakeTypeAssertion(WebSocketError)
-
-module.exports = { WebSocketError, createUnvalidatedWebSocketError }
diff --git a/vanilla/node_modules/undici/lib/web/websocket/stream/websocketstream.js b/vanilla/node_modules/undici/lib/web/websocket/stream/websocketstream.js
deleted file mode 100644
index ce3be84..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/stream/websocketstream.js
+++ /dev/null
@@ -1,497 +0,0 @@
-'use strict'
-
-const { createDeferredPromise } = require('../../../util/promise')
-const { environmentSettingsObject } = require('../../fetch/util')
-const { states, opcodes, sentCloseFrameState } = require('../constants')
-const { webidl } = require('../../webidl')
-const { getURLRecord, isValidSubprotocol, isEstablished, utf8Decode } = require('../util')
-const { establishWebSocketConnection, failWebsocketConnection, closeWebSocketConnection } = require('../connection')
-const { channels } = require('../../../core/diagnostics')
-const { WebsocketFrameSend } = require('../frame')
-const { ByteParser } = require('../receiver')
-const { WebSocketError, createUnvalidatedWebSocketError } = require('./websocketerror')
-const { kEnumerableProperty } = require('../../../core/util')
-const { utf8DecodeBytes } = require('../../../encoding')
-
-let emittedExperimentalWarning = false
-
-class WebSocketStream {
- // Each WebSocketStream object has an associated url , which is a URL record .
- /** @type {URL} */
- #url
-
- // Each WebSocketStream object has an associated opened promise , which is a promise.
- /** @type {import('../../../util/promise').DeferredPromise} */
- #openedPromise
-
- // Each WebSocketStream object has an associated closed promise , which is a promise.
- /** @type {import('../../../util/promise').DeferredPromise} */
- #closedPromise
-
- // Each WebSocketStream object has an associated readable stream , which is a ReadableStream .
- /** @type {ReadableStream} */
- #readableStream
- /** @type {ReadableStreamDefaultController} */
- #readableStreamController
-
- // Each WebSocketStream object has an associated writable stream , which is a WritableStream .
- /** @type {WritableStream} */
- #writableStream
-
- // Each WebSocketStream object has an associated boolean handshake aborted , which is initially false.
- #handshakeAborted = false
-
- /** @type {import('../websocket').Handler} */
- #handler = {
- // https://whatpr.org/websockets/48/7b748d3...d5570f3.html#feedback-to-websocket-stream-from-the-protocol
- onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions),
- onMessage: (opcode, data) => this.#onMessage(opcode, data),
- onParserError: (err) => failWebsocketConnection(this.#handler, null, err.message),
- onParserDrain: () => this.#handler.socket.resume(),
- onSocketData: (chunk) => {
- if (!this.#parser.write(chunk)) {
- this.#handler.socket.pause()
- }
- },
- onSocketError: (err) => {
- this.#handler.readyState = states.CLOSING
-
- if (channels.socketError.hasSubscribers) {
- channels.socketError.publish(err)
- }
-
- this.#handler.socket.destroy()
- },
- onSocketClose: () => this.#onSocketClose(),
- onPing: () => {},
- onPong: () => {},
-
- readyState: states.CONNECTING,
- socket: null,
- closeState: new Set(),
- controller: null,
- wasEverConnected: false
- }
-
- /** @type {import('../receiver').ByteParser} */
- #parser
-
- constructor (url, options = undefined) {
- if (!emittedExperimentalWarning) {
- process.emitWarning('WebSocketStream is experimental! Expect it to change at any time.', {
- code: 'UNDICI-WSS'
- })
- emittedExperimentalWarning = true
- }
-
- webidl.argumentLengthCheck(arguments, 1, 'WebSocket')
-
- url = webidl.converters.USVString(url)
- if (options !== null) {
- options = webidl.converters.WebSocketStreamOptions(options)
- }
-
- // 1. Let baseURL be this 's relevant settings object 's API base URL .
- const baseURL = environmentSettingsObject.settingsObject.baseUrl
-
- // 2. Let urlRecord be the result of getting a URL record given url and baseURL .
- const urlRecord = getURLRecord(url, baseURL)
-
- // 3. Let protocols be options [" protocols "] if it exists , otherwise an empty sequence.
- const protocols = options.protocols
-
- // 4. If any of the values in protocols occur more than once or otherwise fail to match the requirements for elements that comprise the value of ` Sec-WebSocket-Protocol ` fields as defined by The WebSocket Protocol , then throw a " SyntaxError " DOMException . [WSP]
- if (protocols.length !== new Set(protocols.map(p => p.toLowerCase())).size) {
- throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')
- }
-
- if (protocols.length > 0 && !protocols.every(p => isValidSubprotocol(p))) {
- throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')
- }
-
- // 5. Set this 's url to urlRecord .
- this.#url = urlRecord.toString()
-
- // 6. Set this 's opened promise and closed promise to new promises.
- this.#openedPromise = createDeferredPromise()
- this.#closedPromise = createDeferredPromise()
-
- // 7. Apply backpressure to the WebSocket.
- // TODO
-
- // 8. If options [" signal "] exists ,
- if (options.signal != null) {
- // 8.1. Let signal be options [" signal "].
- const signal = options.signal
-
- // 8.2. If signal is aborted , then reject this 's opened promise and closed promise with signal ’s abort reason
- // and return.
- if (signal.aborted) {
- this.#openedPromise.reject(signal.reason)
- this.#closedPromise.reject(signal.reason)
- return
- }
-
- // 8.3. Add the following abort steps to signal :
- signal.addEventListener('abort', () => {
- // 8.3.1. If the WebSocket connection is not yet established : [WSP]
- if (!isEstablished(this.#handler.readyState)) {
- // 8.3.1.1. Fail the WebSocket connection .
- failWebsocketConnection(this.#handler)
-
- // Set this 's ready state to CLOSING .
- this.#handler.readyState = states.CLOSING
-
- // Reject this 's opened promise and closed promise with signal ’s abort reason .
- this.#openedPromise.reject(signal.reason)
- this.#closedPromise.reject(signal.reason)
-
- // Set this 's handshake aborted to true.
- this.#handshakeAborted = true
- }
- }, { once: true })
- }
-
- // 9. Let client be this 's relevant settings object .
- const client = environmentSettingsObject.settingsObject
-
- // 10. Run this step in parallel :
- // 10.1. Establish a WebSocket connection given urlRecord , protocols , and client . [FETCH]
- this.#handler.controller = establishWebSocketConnection(
- urlRecord,
- protocols,
- client,
- this.#handler,
- options
- )
- }
-
- // The url getter steps are to return this 's url , serialized .
- get url () {
- return this.#url.toString()
- }
-
- // The opened getter steps are to return this 's opened promise .
- get opened () {
- return this.#openedPromise.promise
- }
-
- // The closed getter steps are to return this 's closed promise .
- get closed () {
- return this.#closedPromise.promise
- }
-
- // The close( closeInfo ) method steps are:
- close (closeInfo = undefined) {
- if (closeInfo !== null) {
- closeInfo = webidl.converters.WebSocketCloseInfo(closeInfo)
- }
-
- // 1. Let code be closeInfo [" closeCode "] if present, or null otherwise.
- const code = closeInfo.closeCode ?? null
-
- // 2. Let reason be closeInfo [" reason "].
- const reason = closeInfo.reason
-
- // 3. Close the WebSocket with this , code , and reason .
- closeWebSocketConnection(this.#handler, code, reason, true)
- }
-
- #write (chunk) {
- // See /websockets/stream/tentative/write.any.html
- chunk = webidl.converters.WebSocketStreamWrite(chunk)
-
- // 1. Let promise be a new promise created in stream ’s relevant realm .
- const promise = createDeferredPromise()
-
- // 2. Let data be null.
- let data = null
-
- // 3. Let opcode be null.
- let opcode = null
-
- // 4. If chunk is a BufferSource ,
- if (webidl.is.BufferSource(chunk)) {
- // 4.1. Set data to a copy of the bytes given chunk .
- data = new Uint8Array(ArrayBuffer.isView(chunk) ? new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength) : chunk.slice())
-
- // 4.2. Set opcode to a binary frame opcode.
- opcode = opcodes.BINARY
- } else {
- // 5. Otherwise,
-
- // 5.1. Let string be the result of converting chunk to an IDL USVString .
- // If this throws an exception, return a promise rejected with the exception.
- let string
-
- try {
- string = webidl.converters.DOMString(chunk)
- } catch (e) {
- promise.reject(e)
- return promise.promise
- }
-
- // 5.2. Set data to the result of UTF-8 encoding string .
- data = new TextEncoder().encode(string)
-
- // 5.3. Set opcode to a text frame opcode.
- opcode = opcodes.TEXT
- }
-
- // 6. In parallel,
- // 6.1. Wait until there is sufficient buffer space in stream to send the message.
-
- // 6.2. If the closing handshake has not yet started , Send a WebSocket Message to stream comprised of data using opcode .
- if (!this.#handler.closeState.has(sentCloseFrameState.SENT) && !this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
- const frame = new WebsocketFrameSend(data)
-
- this.#handler.socket.write(frame.createFrame(opcode), () => {
- promise.resolve(undefined)
- })
- }
-
- // 6.3. Queue a global task on the WebSocket task source given stream ’s relevant global object to resolve promise with undefined.
- return promise.promise
- }
-
- /** @type {import('../websocket').Handler['onConnectionEstablished']} */
- #onConnectionEstablished (response, parsedExtensions) {
- this.#handler.socket = response.socket
-
- const parser = new ByteParser(this.#handler, parsedExtensions)
- parser.on('drain', () => this.#handler.onParserDrain())
- parser.on('error', (err) => this.#handler.onParserError(err))
-
- this.#parser = parser
-
- // 1. Change stream ’s ready state to OPEN (1).
- this.#handler.readyState = states.OPEN
-
- // 2. Set stream ’s was ever connected to true.
- // This is done in the opening handshake.
-
- // 3. Let extensions be the extensions in use .
- const extensions = parsedExtensions ?? ''
-
- // 4. Let protocol be the subprotocol in use .
- const protocol = response.headersList.get('sec-websocket-protocol') ?? ''
-
- // 5. Let pullAlgorithm be an action that pulls bytes from stream .
- // 6. Let cancelAlgorithm be an action that cancels stream with reason , given reason .
- // 7. Let readable be a new ReadableStream .
- // 8. Set up readable with pullAlgorithm and cancelAlgorithm .
- const readable = new ReadableStream({
- start: (controller) => {
- this.#readableStreamController = controller
- },
- pull (controller) {
- let chunk
- while (controller.desiredSize > 0 && (chunk = response.socket.read()) !== null) {
- controller.enqueue(chunk)
- }
- },
- cancel: (reason) => this.#cancel(reason)
- })
-
- // 9. Let writeAlgorithm be an action that writes chunk to stream , given chunk .
- // 10. Let closeAlgorithm be an action that closes stream .
- // 11. Let abortAlgorithm be an action that aborts stream with reason , given reason .
- // 12. Let writable be a new WritableStream .
- // 13. Set up writable with writeAlgorithm , closeAlgorithm , and abortAlgorithm .
- const writable = new WritableStream({
- write: (chunk) => this.#write(chunk),
- close: () => closeWebSocketConnection(this.#handler, null, null),
- abort: (reason) => this.#closeUsingReason(reason)
- })
-
- // Set stream ’s readable stream to readable .
- this.#readableStream = readable
-
- // Set stream ’s writable stream to writable .
- this.#writableStream = writable
-
- // Resolve stream ’s opened promise with WebSocketOpenInfo «[ " extensions " → extensions , " protocol " → protocol , " readable " → readable , " writable " → writable ]».
- this.#openedPromise.resolve({
- extensions,
- protocol,
- readable,
- writable
- })
- }
-
- /** @type {import('../websocket').Handler['onMessage']} */
- #onMessage (type, data) {
- // 1. If stream’s ready state is not OPEN (1), then return.
- if (this.#handler.readyState !== states.OPEN) {
- return
- }
-
- // 2. Let chunk be determined by switching on type:
- // - type indicates that the data is Text
- // a new DOMString containing data
- // - type indicates that the data is Binary
- // a new Uint8Array object, created in the relevant Realm of the
- // WebSocketStream object, whose contents are data
- let chunk
-
- if (type === opcodes.TEXT) {
- try {
- chunk = utf8Decode(data)
- } catch {
- failWebsocketConnection(this.#handler, 'Received invalid UTF-8 in text frame.')
- return
- }
- } else if (type === opcodes.BINARY) {
- chunk = new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
- }
-
- // 3. Enqueue chunk into stream’s readable stream.
- this.#readableStreamController.enqueue(chunk)
-
- // 4. Apply backpressure to the WebSocket.
- }
-
- /** @type {import('../websocket').Handler['onSocketClose']} */
- #onSocketClose () {
- const wasClean =
- this.#handler.closeState.has(sentCloseFrameState.SENT) &&
- this.#handler.closeState.has(sentCloseFrameState.RECEIVED)
-
- // 1. Change the ready state to CLOSED (3).
- this.#handler.readyState = states.CLOSED
-
- // 2. If stream ’s handshake aborted is true, then return.
- if (this.#handshakeAborted) {
- return
- }
-
- // 3. If stream ’s was ever connected is false, then reject stream ’s opened promise with a new WebSocketError.
- if (!this.#handler.wasEverConnected) {
- this.#openedPromise.reject(new WebSocketError('Socket never opened'))
- }
-
- const result = this.#parser?.closingInfo
-
- // 4. Let code be the WebSocket connection close code .
- // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5
- // If this Close control frame contains no status code, _The WebSocket
- // Connection Close Code_ is considered to be 1005. If _The WebSocket
- // Connection is Closed_ and no Close control frame was received by the
- // endpoint (such as could occur if the underlying transport connection
- // is lost), _The WebSocket Connection Close Code_ is considered to be
- // 1006.
- let code = result?.code ?? 1005
-
- if (!this.#handler.closeState.has(sentCloseFrameState.SENT) && !this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
- code = 1006
- }
-
- // 5. Let reason be the result of applying UTF-8 decode without BOM to the WebSocket connection close reason .
- const reason = result?.reason == null ? '' : utf8DecodeBytes(Buffer.from(result.reason))
-
- // 6. If the connection was closed cleanly ,
- if (wasClean) {
- // 6.1. Close stream ’s readable stream .
- this.#readableStreamController.close()
-
- // 6.2. Error stream ’s writable stream with an " InvalidStateError " DOMException indicating that a closed WebSocketStream cannot be written to.
- if (!this.#writableStream.locked) {
- this.#writableStream.abort(new DOMException('A closed WebSocketStream cannot be written to', 'InvalidStateError'))
- }
-
- // 6.3. Resolve stream ’s closed promise with WebSocketCloseInfo «[ " closeCode " → code , " reason " → reason ]».
- this.#closedPromise.resolve({
- closeCode: code,
- reason
- })
- } else {
- // 7. Otherwise,
-
- // 7.1. Let error be a new WebSocketError whose closeCode is code and reason is reason .
- const error = createUnvalidatedWebSocketError('unclean close', code, reason)
-
- // 7.2. Error stream ’s readable stream with error .
- this.#readableStreamController?.error(error)
-
- // 7.3. Error stream ’s writable stream with error .
- this.#writableStream?.abort(error)
-
- // 7.4. Reject stream ’s closed promise with error .
- this.#closedPromise.reject(error)
- }
- }
-
- #closeUsingReason (reason) {
- // 1. Let code be null.
- let code = null
-
- // 2. Let reasonString be the empty string.
- let reasonString = ''
-
- // 3. If reason implements WebSocketError ,
- if (webidl.is.WebSocketError(reason)) {
- // 3.1. Set code to reason ’s closeCode .
- code = reason.closeCode
-
- // 3.2. Set reasonString to reason ’s reason .
- reasonString = reason.reason
- }
-
- // 4. Close the WebSocket with stream , code , and reasonString . If this throws an exception,
- // discard code and reasonString and close the WebSocket with stream .
- closeWebSocketConnection(this.#handler, code, reasonString)
- }
-
- // To cancel a WebSocketStream stream given reason , close using reason giving stream and reason .
- #cancel (reason) {
- this.#closeUsingReason(reason)
- }
-}
-
-Object.defineProperties(WebSocketStream.prototype, {
- url: kEnumerableProperty,
- opened: kEnumerableProperty,
- closed: kEnumerableProperty,
- close: kEnumerableProperty,
- [Symbol.toStringTag]: {
- value: 'WebSocketStream',
- writable: false,
- enumerable: false,
- configurable: true
- }
-})
-
-webidl.converters.WebSocketStreamOptions = webidl.dictionaryConverter([
- {
- key: 'protocols',
- converter: webidl.sequenceConverter(webidl.converters.USVString),
- defaultValue: () => []
- },
- {
- key: 'signal',
- converter: webidl.nullableConverter(webidl.converters.AbortSignal),
- defaultValue: () => null
- }
-])
-
-webidl.converters.WebSocketCloseInfo = webidl.dictionaryConverter([
- {
- key: 'closeCode',
- converter: (V) => webidl.converters['unsigned short'](V, webidl.attributes.EnforceRange)
- },
- {
- key: 'reason',
- converter: webidl.converters.USVString,
- defaultValue: () => ''
- }
-])
-
-webidl.converters.WebSocketStreamWrite = function (V) {
- if (typeof V === 'string') {
- return webidl.converters.USVString(V)
- }
-
- return webidl.converters.BufferSource(V)
-}
-
-module.exports = { WebSocketStream }
diff --git a/vanilla/node_modules/undici/lib/web/websocket/util.js b/vanilla/node_modules/undici/lib/web/websocket/util.js
deleted file mode 100644
index eaa5e7a..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/util.js
+++ /dev/null
@@ -1,339 +0,0 @@
-'use strict'
-
-const { states, opcodes } = require('./constants')
-const { isUtf8 } = require('node:buffer')
-const { removeHTTPWhitespace } = require('../fetch/data-url')
-const { collectASequenceOfCodePointsFast } = require('../infra')
-
-/**
- * @param {number} readyState
- * @returns {boolean}
- */
-function isConnecting (readyState) {
- // If the WebSocket connection is not yet established, and the connection
- // is not yet closed, then the WebSocket connection is in the CONNECTING state.
- return readyState === states.CONNECTING
-}
-
-/**
- * @param {number} readyState
- * @returns {boolean}
- */
-function isEstablished (readyState) {
- // If the server's response is validated as provided for above, it is
- // said that _The WebSocket Connection is Established_ and that the
- // WebSocket Connection is in the OPEN state.
- return readyState === states.OPEN
-}
-
-/**
- * @param {number} readyState
- * @returns {boolean}
- */
-function isClosing (readyState) {
- // Upon either sending or receiving a Close control frame, it is said
- // that _The WebSocket Closing Handshake is Started_ and that the
- // WebSocket connection is in the CLOSING state.
- return readyState === states.CLOSING
-}
-
-/**
- * @param {number} readyState
- * @returns {boolean}
- */
-function isClosed (readyState) {
- return readyState === states.CLOSED
-}
-
-/**
- * @see https://dom.spec.whatwg.org/#concept-event-fire
- * @param {string} e
- * @param {EventTarget} target
- * @param {(...args: ConstructorParameters<typeof Event>) => Event} eventFactory
- * @param {EventInit | undefined} eventInitDict
- * @returns {void}
- */
-function fireEvent (e, target, eventFactory = (type, init) => new Event(type, init), eventInitDict = {}) {
- // 1. If eventConstructor is not given, then let eventConstructor be Event.
-
- // 2. Let event be the result of creating an event given eventConstructor,
- // in the relevant realm of target.
- // 3. Initialize event’s type attribute to e.
- const event = eventFactory(e, eventInitDict)
-
- // 4. Initialize any other IDL attributes of event as described in the
- // invocation of this algorithm.
-
- // 5. Return the result of dispatching event at target, with legacy target
- // override flag set if set.
- target.dispatchEvent(event)
-}
-
-/**
- * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
- * @param {import('./websocket').Handler} handler
- * @param {number} type Opcode
- * @param {Buffer} data application data
- * @returns {void}
- */
-function websocketMessageReceived (handler, type, data) {
- handler.onMessage(type, data)
-}
-
-/**
- * @param {Buffer} buffer
- * @returns {ArrayBuffer}
- */
-function toArrayBuffer (buffer) {
- if (buffer.byteLength === buffer.buffer.byteLength) {
- return buffer.buffer
- }
- return new Uint8Array(buffer).buffer
-}
-
-/**
- * @see https://datatracker.ietf.org/doc/html/rfc6455
- * @see https://datatracker.ietf.org/doc/html/rfc2616
- * @see https://bugs.chromium.org/p/chromium/issues/detail?id=398407
- * @param {string} protocol
- * @returns {boolean}
- */
-function isValidSubprotocol (protocol) {
- // If present, this value indicates one
- // or more comma-separated subprotocol the client wishes to speak,
- // ordered by preference. The elements that comprise this value
- // MUST be non-empty strings with characters in the range U+0021 to
- // U+007E not including separator characters as defined in
- // [RFC2616] and MUST all be unique strings.
- if (protocol.length === 0) {
- return false
- }
-
- for (let i = 0; i < protocol.length; ++i) {
- const code = protocol.charCodeAt(i)
-
- if (
- code < 0x21 || // CTL, contains SP (0x20) and HT (0x09)
- code > 0x7E ||
- code === 0x22 || // "
- code === 0x28 || // (
- code === 0x29 || // )
- code === 0x2C || // ,
- code === 0x2F || // /
- code === 0x3A || // :
- code === 0x3B || // ;
- code === 0x3C || // <
- code === 0x3D || // =
- code === 0x3E || // >
- code === 0x3F || // ?
- code === 0x40 || // @
- code === 0x5B || // [
- code === 0x5C || // \
- code === 0x5D || // ]
- code === 0x7B || // {
- code === 0x7D // }
- ) {
- return false
- }
- }
-
- return true
-}
-
-/**
- * @see https://datatracker.ietf.org/doc/html/rfc6455#section-7-4
- * @param {number} code
- * @returns {boolean}
- */
-function isValidStatusCode (code) {
- if (code >= 1000 && code < 1015) {
- return (
- code !== 1004 && // reserved
- code !== 1005 && // "MUST NOT be set as a status code"
- code !== 1006 // "MUST NOT be set as a status code"
- )
- }
-
- return code >= 3000 && code <= 4999
-}
-
-/**
- * @see https://datatracker.ietf.org/doc/html/rfc6455#section-5.5
- * @param {number} opcode
- * @returns {boolean}
- */
-function isControlFrame (opcode) {
- return (
- opcode === opcodes.CLOSE ||
- opcode === opcodes.PING ||
- opcode === opcodes.PONG
- )
-}
-
-/**
- * @param {number} opcode
- * @returns {boolean}
- */
-function isContinuationFrame (opcode) {
- return opcode === opcodes.CONTINUATION
-}
-
-/**
- * @param {number} opcode
- * @returns {boolean}
- */
-function isTextBinaryFrame (opcode) {
- return opcode === opcodes.TEXT || opcode === opcodes.BINARY
-}
-
-/**
- *
- * @param {number} opcode
- * @returns {boolean}
- */
-function isValidOpcode (opcode) {
- return isTextBinaryFrame(opcode) || isContinuationFrame(opcode) || isControlFrame(opcode)
-}
-
-/**
- * Parses a Sec-WebSocket-Extensions header value.
- * @param {string} extensions
- * @returns {Map<string, string>}
- */
-// TODO(@Uzlopak, @KhafraDev): make compliant https://datatracker.ietf.org/doc/html/rfc6455#section-9.1
-function parseExtensions (extensions) {
- const position = { position: 0 }
- const extensionList = new Map()
-
- while (position.position < extensions.length) {
- const pair = collectASequenceOfCodePointsFast(';', extensions, position)
- const [name, value = ''] = pair.split('=', 2)
-
- extensionList.set(
- removeHTTPWhitespace(name, true, false),
- removeHTTPWhitespace(value, false, true)
- )
-
- position.position++
- }
-
- return extensionList
-}
-
-/**
- * @see https://www.rfc-editor.org/rfc/rfc7692#section-7.1.2.2
- * @description "client-max-window-bits = 1*DIGIT"
- * @param {string} value
- * @returns {boolean}
- */
-function isValidClientWindowBits (value) {
- for (let i = 0; i < value.length; i++) {
- const byte = value.charCodeAt(i)
-
- if (byte < 0x30 || byte > 0x39) {
- return false
- }
- }
-
- return true
-}
-
-/**
- * @see https://whatpr.org/websockets/48/7b748d3...d5570f3.html#get-a-url-record
- * @param {string} url
- * @param {string} [baseURL]
- */
-function getURLRecord (url, baseURL) {
- // 1. Let urlRecord be the result of applying the URL parser to url with baseURL .
- // 2. If urlRecord is failure, then throw a " SyntaxError " DOMException .
- let urlRecord
-
- try {
- urlRecord = new URL(url, baseURL)
- } catch (e) {
- throw new DOMException(e, 'SyntaxError')
- }
-
- // 3. If urlRecord ’s scheme is " http ", then set urlRecord ’s scheme to " ws ".
- // 4. Otherwise, if urlRecord ’s scheme is " https ", set urlRecord ’s scheme to " wss ".
- if (urlRecord.protocol === 'http:') {
- urlRecord.protocol = 'ws:'
- } else if (urlRecord.protocol === 'https:') {
- urlRecord.protocol = 'wss:'
- }
-
- // 5. If urlRecord ’s scheme is not " ws " or " wss ", then throw a " SyntaxError " DOMException .
- if (urlRecord.protocol !== 'ws:' && urlRecord.protocol !== 'wss:') {
- throw new DOMException('expected a ws: or wss: url', 'SyntaxError')
- }
-
- // If urlRecord ’s fragment is non-null, then throw a " SyntaxError " DOMException .
- if (urlRecord.hash.length || urlRecord.href.endsWith('#')) {
- throw new DOMException('hash', 'SyntaxError')
- }
-
- // Return urlRecord .
- return urlRecord
-}
-
-// https://whatpr.org/websockets/48.html#validate-close-code-and-reason
-function validateCloseCodeAndReason (code, reason) {
- // 1. If code is not null, but is neither an integer equal to
- // 1000 nor an integer in the range 3000 to 4999, inclusive,
- // throw an "InvalidAccessError" DOMException.
- if (code !== null) {
- if (code !== 1000 && (code < 3000 || code > 4999)) {
- throw new DOMException('invalid code', 'InvalidAccessError')
- }
- }
-
- // 2. If reason is not null, then:
- if (reason !== null) {
- // 2.1. Let reasonBytes be the result of UTF-8 encoding reason.
- // 2.2. If reasonBytes is longer than 123 bytes, then throw a
- // "SyntaxError" DOMException.
- const reasonBytesLength = Buffer.byteLength(reason)
-
- if (reasonBytesLength > 123) {
- throw new DOMException(`Reason must be less than 123 bytes; received ${reasonBytesLength}`, 'SyntaxError')
- }
- }
-}
-
-/**
- * Converts a Buffer to utf-8, even on platforms without icu.
- * @type {(buffer: Buffer) => string}
- */
-const utf8Decode = (() => {
- if (typeof process.versions.icu === 'string') {
- const fatalDecoder = new TextDecoder('utf-8', { fatal: true })
- return fatalDecoder.decode.bind(fatalDecoder)
- }
- return function (buffer) {
- if (isUtf8(buffer)) {
- return buffer.toString('utf-8')
- }
- throw new TypeError('Invalid utf-8 received.')
- }
-})()
-
-module.exports = {
- isConnecting,
- isEstablished,
- isClosing,
- isClosed,
- fireEvent,
- isValidSubprotocol,
- isValidStatusCode,
- websocketMessageReceived,
- utf8Decode,
- isControlFrame,
- isContinuationFrame,
- isTextBinaryFrame,
- isValidOpcode,
- parseExtensions,
- isValidClientWindowBits,
- toArrayBuffer,
- getURLRecord,
- validateCloseCodeAndReason
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/websocket.js b/vanilla/node_modules/undici/lib/web/websocket/websocket.js
deleted file mode 100644
index 3314976..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/websocket.js
+++ /dev/null
@@ -1,739 +0,0 @@
-'use strict'
-
-const { isArrayBuffer } = require('node:util/types')
-const { webidl } = require('../webidl')
-const { URLSerializer } = require('../fetch/data-url')
-const { environmentSettingsObject } = require('../fetch/util')
-const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints, opcodes } = require('./constants')
-const {
- isConnecting,
- isEstablished,
- isClosing,
- isClosed,
- isValidSubprotocol,
- fireEvent,
- utf8Decode,
- toArrayBuffer,
- getURLRecord
-} = require('./util')
-const { establishWebSocketConnection, closeWebSocketConnection, failWebsocketConnection } = require('./connection')
-const { ByteParser } = require('./receiver')
-const { kEnumerableProperty } = require('../../core/util')
-const { getGlobalDispatcher } = require('../../global')
-const { ErrorEvent, CloseEvent, createFastMessageEvent } = require('./events')
-const { SendQueue } = require('./sender')
-const { WebsocketFrameSend } = require('./frame')
-const { channels } = require('../../core/diagnostics')
-
-/**
- * @typedef {object} Handler
- * @property {(response: any, extensions?: string[]) => void} onConnectionEstablished
- * @property {(opcode: number, data: Buffer) => void} onMessage
- * @property {(error: Error) => void} onParserError
- * @property {() => void} onParserDrain
- * @property {(chunk: Buffer) => void} onSocketData
- * @property {(err: Error) => void} onSocketError
- * @property {() => void} onSocketClose
- * @property {(body: Buffer) => void} onPing
- * @property {(body: Buffer) => void} onPong
- *
- * @property {number} readyState
- * @property {import('stream').Duplex} socket
- * @property {Set<number>} closeState
- * @property {import('../fetch/index').Fetch} controller
- * @property {boolean} [wasEverConnected=false]
- */
-
-// https://websockets.spec.whatwg.org/#interface-definition
-class WebSocket extends EventTarget {
- #events = {
- open: null,
- error: null,
- close: null,
- message: null
- }
-
- #bufferedAmount = 0
- #protocol = ''
- #extensions = ''
-
- /** @type {SendQueue} */
- #sendQueue
-
- /** @type {Handler} */
- #handler = {
- onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions),
- onMessage: (opcode, data) => this.#onMessage(opcode, data),
- onParserError: (err) => failWebsocketConnection(this.#handler, null, err.message),
- onParserDrain: () => this.#onParserDrain(),
- onSocketData: (chunk) => {
- if (!this.#parser.write(chunk)) {
- this.#handler.socket.pause()
- }
- },
- onSocketError: (err) => {
- this.#handler.readyState = states.CLOSING
-
- if (channels.socketError.hasSubscribers) {
- channels.socketError.publish(err)
- }
-
- this.#handler.socket.destroy()
- },
- onSocketClose: () => this.#onSocketClose(),
- onPing: (body) => {
- if (channels.ping.hasSubscribers) {
- channels.ping.publish({
- payload: body,
- websocket: this
- })
- }
- },
- onPong: (body) => {
- if (channels.pong.hasSubscribers) {
- channels.pong.publish({
- payload: body,
- websocket: this
- })
- }
- },
-
- readyState: states.CONNECTING,
- socket: null,
- closeState: new Set(),
- controller: null,
- wasEverConnected: false
- }
-
- #url
- #binaryType
- /** @type {import('./receiver').ByteParser} */
- #parser
-
- /**
- * @param {string} url
- * @param {string|string[]} protocols
- */
- constructor (url, protocols = []) {
- super()
-
- webidl.util.markAsUncloneable(this)
-
- const prefix = 'WebSocket constructor'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- const options = webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'](protocols, prefix, 'options')
-
- url = webidl.converters.USVString(url)
- protocols = options.protocols
-
- // 1. Let baseURL be this's relevant settings object's API base URL.
- const baseURL = environmentSettingsObject.settingsObject.baseUrl
-
- // 2. Let urlRecord be the result of getting a URL record given url and baseURL.
- const urlRecord = getURLRecord(url, baseURL)
-
- // 3. If protocols is a string, set protocols to a sequence consisting
- // of just that string.
- if (typeof protocols === 'string') {
- protocols = [protocols]
- }
-
- // 4. If any of the values in protocols occur more than once or otherwise
- // fail to match the requirements for elements that comprise the value
- // of `Sec-WebSocket-Protocol` fields as defined by The WebSocket
- // protocol, then throw a "SyntaxError" DOMException.
- if (protocols.length !== new Set(protocols.map(p => p.toLowerCase())).size) {
- throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')
- }
-
- if (protocols.length > 0 && !protocols.every(p => isValidSubprotocol(p))) {
- throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')
- }
-
- // 5. Set this's url to urlRecord.
- this.#url = new URL(urlRecord.href)
-
- // 6. Let client be this's relevant settings object.
- const client = environmentSettingsObject.settingsObject
-
- // 7. Run this step in parallel:
- // 7.1. Establish a WebSocket connection given urlRecord, protocols,
- // and client.
- this.#handler.controller = establishWebSocketConnection(
- urlRecord,
- protocols,
- client,
- this.#handler,
- options
- )
-
- // Each WebSocket object has an associated ready state, which is a
- // number representing the state of the connection. Initially it must
- // be CONNECTING (0).
- this.#handler.readyState = WebSocket.CONNECTING
-
- // The extensions attribute must initially return the empty string.
-
- // The protocol attribute must initially return the empty string.
-
- // Each WebSocket object has an associated binary type, which is a
- // BinaryType. Initially it must be "blob".
- this.#binaryType = 'blob'
- }
-
- /**
- * @see https://websockets.spec.whatwg.org/#dom-websocket-close
- * @param {number|undefined} code
- * @param {string|undefined} reason
- */
- close (code = undefined, reason = undefined) {
- webidl.brandCheck(this, WebSocket)
-
- const prefix = 'WebSocket.close'
-
- if (code !== undefined) {
- code = webidl.converters['unsigned short'](code, prefix, 'code', webidl.attributes.Clamp)
- }
-
- if (reason !== undefined) {
- reason = webidl.converters.USVString(reason)
- }
-
- // 1. If code is the special value "missing", then set code to null.
- code ??= null
-
- // 2. If reason is the special value "missing", then set reason to the empty string.
- reason ??= ''
-
- // 3. Close the WebSocket with this, code, and reason.
- closeWebSocketConnection(this.#handler, code, reason, true)
- }
-
- /**
- * @see https://websockets.spec.whatwg.org/#dom-websocket-send
- * @param {NodeJS.TypedArray|ArrayBuffer|Blob|string} data
- */
- send (data) {
- webidl.brandCheck(this, WebSocket)
-
- const prefix = 'WebSocket.send'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- data = webidl.converters.WebSocketSendData(data, prefix, 'data')
-
- // 1. If this's ready state is CONNECTING, then throw an
- // "InvalidStateError" DOMException.
- if (isConnecting(this.#handler.readyState)) {
- throw new DOMException('Sent before connected.', 'InvalidStateError')
- }
-
- // 2. Run the appropriate set of steps from the following list:
- // https://datatracker.ietf.org/doc/html/rfc6455#section-6.1
- // https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
-
- if (!isEstablished(this.#handler.readyState) || isClosing(this.#handler.readyState)) {
- return
- }
-
- // If data is a string
- if (typeof data === 'string') {
- // If the WebSocket connection is established and the WebSocket
- // closing handshake has not yet started, then the user agent
- // must send a WebSocket Message comprised of the data argument
- // using a text frame opcode; if the data cannot be sent, e.g.
- // because it would need to be buffered but the buffer is full,
- // the user agent must flag the WebSocket as full and then close
- // the WebSocket connection. Any invocation of this method with a
- // string argument that does not throw an exception must increase
- // the bufferedAmount attribute by the number of bytes needed to
- // express the argument as UTF-8.
-
- const buffer = Buffer.from(data)
-
- this.#bufferedAmount += buffer.byteLength
- this.#sendQueue.add(buffer, () => {
- this.#bufferedAmount -= buffer.byteLength
- }, sendHints.text)
- } else if (isArrayBuffer(data)) {
- // If the WebSocket connection is established, and the WebSocket
- // closing handshake has not yet started, then the user agent must
- // send a WebSocket Message comprised of data using a binary frame
- // opcode; if the data cannot be sent, e.g. because it would need
- // to be buffered but the buffer is full, the user agent must flag
- // the WebSocket as full and then close the WebSocket connection.
- // The data to be sent is the data stored in the buffer described
- // by the ArrayBuffer object. Any invocation of this method with an
- // ArrayBuffer argument that does not throw an exception must
- // increase the bufferedAmount attribute by the length of the
- // ArrayBuffer in bytes.
-
- this.#bufferedAmount += data.byteLength
- this.#sendQueue.add(data, () => {
- this.#bufferedAmount -= data.byteLength
- }, sendHints.arrayBuffer)
- } else if (ArrayBuffer.isView(data)) {
- // If the WebSocket connection is established, and the WebSocket
- // closing handshake has not yet started, then the user agent must
- // send a WebSocket Message comprised of data using a binary frame
- // opcode; if the data cannot be sent, e.g. because it would need to
- // be buffered but the buffer is full, the user agent must flag the
- // WebSocket as full and then close the WebSocket connection. The
- // data to be sent is the data stored in the section of the buffer
- // described by the ArrayBuffer object that data references. Any
- // invocation of this method with this kind of argument that does
- // not throw an exception must increase the bufferedAmount attribute
- // by the length of data’s buffer in bytes.
-
- this.#bufferedAmount += data.byteLength
- this.#sendQueue.add(data, () => {
- this.#bufferedAmount -= data.byteLength
- }, sendHints.typedArray)
- } else if (webidl.is.Blob(data)) {
- // If the WebSocket connection is established, and the WebSocket
- // closing handshake has not yet started, then the user agent must
- // send a WebSocket Message comprised of data using a binary frame
- // opcode; if the data cannot be sent, e.g. because it would need to
- // be buffered but the buffer is full, the user agent must flag the
- // WebSocket as full and then close the WebSocket connection. The data
- // to be sent is the raw data represented by the Blob object. Any
- // invocation of this method with a Blob argument that does not throw
- // an exception must increase the bufferedAmount attribute by the size
- // of the Blob object’s raw data, in bytes.
-
- this.#bufferedAmount += data.size
- this.#sendQueue.add(data, () => {
- this.#bufferedAmount -= data.size
- }, sendHints.blob)
- }
- }
-
- get readyState () {
- webidl.brandCheck(this, WebSocket)
-
- // The readyState getter steps are to return this's ready state.
- return this.#handler.readyState
- }
-
- get bufferedAmount () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#bufferedAmount
- }
-
- get url () {
- webidl.brandCheck(this, WebSocket)
-
- // The url getter steps are to return this's url, serialized.
- return URLSerializer(this.#url)
- }
-
- get extensions () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#extensions
- }
-
- get protocol () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#protocol
- }
-
- get onopen () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#events.open
- }
-
- set onopen (fn) {
- webidl.brandCheck(this, WebSocket)
-
- if (this.#events.open) {
- this.removeEventListener('open', this.#events.open)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('open', listener)
- this.#events.open = fn
- } else {
- this.#events.open = null
- }
- }
-
- get onerror () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#events.error
- }
-
- set onerror (fn) {
- webidl.brandCheck(this, WebSocket)
-
- if (this.#events.error) {
- this.removeEventListener('error', this.#events.error)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('error', listener)
- this.#events.error = fn
- } else {
- this.#events.error = null
- }
- }
-
- get onclose () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#events.close
- }
-
- set onclose (fn) {
- webidl.brandCheck(this, WebSocket)
-
- if (this.#events.close) {
- this.removeEventListener('close', this.#events.close)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('close', listener)
- this.#events.close = fn
- } else {
- this.#events.close = null
- }
- }
-
- get onmessage () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#events.message
- }
-
- set onmessage (fn) {
- webidl.brandCheck(this, WebSocket)
-
- if (this.#events.message) {
- this.removeEventListener('message', this.#events.message)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('message', listener)
- this.#events.message = fn
- } else {
- this.#events.message = null
- }
- }
-
- get binaryType () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#binaryType
- }
-
- set binaryType (type) {
- webidl.brandCheck(this, WebSocket)
-
- if (type !== 'blob' && type !== 'arraybuffer') {
- this.#binaryType = 'blob'
- } else {
- this.#binaryType = type
- }
- }
-
- /**
- * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
- */
- #onConnectionEstablished (response, parsedExtensions) {
- // processResponse is called when the "response’s header list has been received and initialized."
- // once this happens, the connection is open
- this.#handler.socket = response.socket
-
- const parser = new ByteParser(this.#handler, parsedExtensions)
- parser.on('drain', () => this.#handler.onParserDrain())
- parser.on('error', (err) => this.#handler.onParserError(err))
-
- this.#parser = parser
- this.#sendQueue = new SendQueue(response.socket)
-
- // 1. Change the ready state to OPEN (1).
- this.#handler.readyState = states.OPEN
-
- // 2. Change the extensions attribute’s value to the extensions in use, if
- // it is not the null value.
- // https://datatracker.ietf.org/doc/html/rfc6455#section-9.1
- const extensions = response.headersList.get('sec-websocket-extensions')
-
- if (extensions !== null) {
- this.#extensions = extensions
- }
-
- // 3. Change the protocol attribute’s value to the subprotocol in use, if
- // it is not the null value.
- // https://datatracker.ietf.org/doc/html/rfc6455#section-1.9
- const protocol = response.headersList.get('sec-websocket-protocol')
-
- if (protocol !== null) {
- this.#protocol = protocol
- }
-
- // 4. Fire an event named open at the WebSocket object.
- fireEvent('open', this)
-
- if (channels.open.hasSubscribers) {
- // Convert headers to a plain object for the event
- const headers = response.headersList.entries
- channels.open.publish({
- address: response.socket.address(),
- protocol: this.#protocol,
- extensions: this.#extensions,
- websocket: this,
- handshakeResponse: {
- status: response.status,
- statusText: response.statusText,
- headers
- }
- })
- }
- }
-
- #onMessage (type, data) {
- // 1. If ready state is not OPEN (1), then return.
- if (this.#handler.readyState !== states.OPEN) {
- return
- }
-
- // 2. Let dataForEvent be determined by switching on type and binary type:
- let dataForEvent
-
- if (type === opcodes.TEXT) {
- // -> type indicates that the data is Text
- // a new DOMString containing data
- try {
- dataForEvent = utf8Decode(data)
- } catch {
- failWebsocketConnection(this.#handler, 1007, 'Received invalid UTF-8 in text frame.')
- return
- }
- } else if (type === opcodes.BINARY) {
- if (this.#binaryType === 'blob') {
- // -> type indicates that the data is Binary and binary type is "blob"
- // a new Blob object, created in the relevant Realm of the WebSocket
- // object, that represents data as its raw data
- dataForEvent = new Blob([data])
- } else {
- // -> type indicates that the data is Binary and binary type is "arraybuffer"
- // a new ArrayBuffer object, created in the relevant Realm of the
- // WebSocket object, whose contents are data
- dataForEvent = toArrayBuffer(data)
- }
- }
-
- // 3. Fire an event named message at the WebSocket object, using MessageEvent,
- // with the origin attribute initialized to the serialization of the WebSocket
- // object’s url's origin, and the data attribute initialized to dataForEvent.
- fireEvent('message', this, createFastMessageEvent, {
- origin: this.#url.origin,
- data: dataForEvent
- })
- }
-
- #onParserDrain () {
- this.#handler.socket.resume()
- }
-
- /**
- * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
- * @see https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.4
- */
- #onSocketClose () {
- // If the TCP connection was closed after the
- // WebSocket closing handshake was completed, the WebSocket connection
- // is said to have been closed _cleanly_.
- const wasClean =
- this.#handler.closeState.has(sentCloseFrameState.SENT) &&
- this.#handler.closeState.has(sentCloseFrameState.RECEIVED)
-
- let code = 1005
- let reason = ''
-
- const result = this.#parser?.closingInfo
-
- if (result && !result.error) {
- code = result.code ?? 1005
- reason = result.reason
- }
-
- // 1. Change the ready state to CLOSED (3).
- this.#handler.readyState = states.CLOSED
-
- // 2. If the user agent was required to fail the WebSocket
- // connection, or if the WebSocket connection was closed
- // after being flagged as full, fire an event named error
- // at the WebSocket object.
- if (!this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
- // If _The WebSocket
- // Connection is Closed_ and no Close control frame was received by the
- // endpoint (such as could occur if the underlying transport connection
- // is lost), _The WebSocket Connection Close Code_ is considered to be
- // 1006.
- code = 1006
-
- fireEvent('error', this, (type, init) => new ErrorEvent(type, init), {
- error: new TypeError(reason)
- })
- }
-
- // 3. Fire an event named close at the WebSocket object,
- // using CloseEvent, with the wasClean attribute
- // initialized to true if the connection closed cleanly
- // and false otherwise, the code attribute initialized to
- // the WebSocket connection close code, and the reason
- // attribute initialized to the result of applying UTF-8
- // decode without BOM to the WebSocket connection close
- // reason.
- // TODO: process.nextTick
- fireEvent('close', this, (type, init) => new CloseEvent(type, init), {
- wasClean, code, reason
- })
-
- if (channels.close.hasSubscribers) {
- channels.close.publish({
- websocket: this,
- code,
- reason
- })
- }
- }
-
- /**
- * @param {WebSocket} ws
- * @param {Buffer|undefined} buffer
- */
- static ping (ws, buffer) {
- if (Buffer.isBuffer(buffer)) {
- if (buffer.length > 125) {
- throw new TypeError('A PING frame cannot have a body larger than 125 bytes.')
- }
- } else if (buffer !== undefined) {
- throw new TypeError('Expected buffer payload')
- }
-
- // An endpoint MAY send a Ping frame any time after the connection is
- // established and before the connection is closed.
- const readyState = ws.#handler.readyState
-
- if (isEstablished(readyState) && !isClosing(readyState) && !isClosed(readyState)) {
- const frame = new WebsocketFrameSend(buffer)
- ws.#handler.socket.write(frame.createFrame(opcodes.PING))
- }
- }
-}
-
-const { ping } = WebSocket
-Reflect.deleteProperty(WebSocket, 'ping')
-
-// https://websockets.spec.whatwg.org/#dom-websocket-connecting
-WebSocket.CONNECTING = WebSocket.prototype.CONNECTING = states.CONNECTING
-// https://websockets.spec.whatwg.org/#dom-websocket-open
-WebSocket.OPEN = WebSocket.prototype.OPEN = states.OPEN
-// https://websockets.spec.whatwg.org/#dom-websocket-closing
-WebSocket.CLOSING = WebSocket.prototype.CLOSING = states.CLOSING
-// https://websockets.spec.whatwg.org/#dom-websocket-closed
-WebSocket.CLOSED = WebSocket.prototype.CLOSED = states.CLOSED
-
-Object.defineProperties(WebSocket.prototype, {
- CONNECTING: staticPropertyDescriptors,
- OPEN: staticPropertyDescriptors,
- CLOSING: staticPropertyDescriptors,
- CLOSED: staticPropertyDescriptors,
- url: kEnumerableProperty,
- readyState: kEnumerableProperty,
- bufferedAmount: kEnumerableProperty,
- onopen: kEnumerableProperty,
- onerror: kEnumerableProperty,
- onclose: kEnumerableProperty,
- close: kEnumerableProperty,
- onmessage: kEnumerableProperty,
- binaryType: kEnumerableProperty,
- send: kEnumerableProperty,
- extensions: kEnumerableProperty,
- protocol: kEnumerableProperty,
- [Symbol.toStringTag]: {
- value: 'WebSocket',
- writable: false,
- enumerable: false,
- configurable: true
- }
-})
-
-Object.defineProperties(WebSocket, {
- CONNECTING: staticPropertyDescriptors,
- OPEN: staticPropertyDescriptors,
- CLOSING: staticPropertyDescriptors,
- CLOSED: staticPropertyDescriptors
-})
-
-webidl.converters['sequence<DOMString>'] = webidl.sequenceConverter(
- webidl.converters.DOMString
-)
-
-webidl.converters['DOMString or sequence<DOMString>'] = function (V, prefix, argument) {
- if (webidl.util.Type(V) === webidl.util.Types.OBJECT && Symbol.iterator in V) {
- return webidl.converters['sequence<DOMString>'](V)
- }
-
- return webidl.converters.DOMString(V, prefix, argument)
-}
-
-// This implements the proposal made in https://github.com/whatwg/websockets/issues/42
-webidl.converters.WebSocketInit = webidl.dictionaryConverter([
- {
- key: 'protocols',
- converter: webidl.converters['DOMString or sequence<DOMString>'],
- defaultValue: () => []
- },
- {
- key: 'dispatcher',
- converter: webidl.converters.any,
- defaultValue: () => getGlobalDispatcher()
- },
- {
- key: 'headers',
- converter: webidl.nullableConverter(webidl.converters.HeadersInit)
- }
-])
-
-webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'] = function (V) {
- if (webidl.util.Type(V) === webidl.util.Types.OBJECT && !(Symbol.iterator in V)) {
- return webidl.converters.WebSocketInit(V)
- }
-
- return { protocols: webidl.converters['DOMString or sequence<DOMString>'](V) }
-}
-
-webidl.converters.WebSocketSendData = function (V) {
- if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {
- if (webidl.is.Blob(V)) {
- return V
- }
-
- if (webidl.is.BufferSource(V)) {
- return V
- }
- }
-
- return webidl.converters.USVString(V)
-}
-
-module.exports = {
- WebSocket,
- ping
-}