Socket
Socket
Sign inDemoInstall

undici

Package Overview
Dependencies
Maintainers
3
Versions
212
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

undici - npm Package Compare versions

Comparing version 6.19.8 to 7.0.0-alpha.1

lib/interceptor/response-error.js

3

docs/docs/api/Agent.md

@@ -22,4 +22,2 @@ # Agent

* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`
* **maxRedirections** `Integer` - Default: `0`. The number of HTTP redirection to follow unless otherwise specified in `DispatchOptions`.
* **interceptors** `{ Agent: DispatchInterceptor[] }` - Default: `[RedirectInterceptor]` - A list of interceptors that are applied to the dispatch method. Additional logic can be applied (such as, but not limited to: 302 status code handling, authentication, cookies, compression and caching). Note that the behavior of interceptors is Experimental and might change at any given time.

@@ -55,3 +53,2 @@ ## Instance Properties

* **origin** `string | URL`
* **maxRedirections** `Integer`.

@@ -58,0 +55,0 @@ Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).

@@ -32,4 +32,2 @@ # Class: Client

* **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body.
<!-- TODO: Remove once we drop its support -->
* **interceptors** `{ Client: DispatchInterceptor[] }` - Default: `[RedirectInterceptor]` - A list of interceptors that are applied to the dispatch method. Additional logic can be applied (such as, but not limited to: 302 status code handling, authentication, cookies, compression and caching). Note that the behavior of interceptors is Experimental and might change at any given time. **Note: this is deprecated in favor of [Dispatcher#compose](./Dispatcher.md#dispatcher). Support will be droped in next major.**
* **autoSelectFamily**: `boolean` (optional) - Default: depends on local Node version, on Node 18.13.0 and above is `false`. Enables a family autodetection algorithm that loosely implements section 5 of [RFC 8305](https://tools.ietf.org/html/rfc8305#section-5). See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details. This option is ignored if not supported by the current Node version.

@@ -36,0 +34,0 @@ * **autoSelectFamilyAttemptTimeout**: `number` - Default: depends on local Node version, on Node 18.13.0 and above is `250`. The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details.

@@ -204,3 +204,2 @@ # Dispatcher

* **headersTimeout** `number | null` (optional) - The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 300 seconds.
* **throwOnError** `boolean` (optional) - Default: `false` - Whether Undici should throw an error upon receiving a 4xx or 5xx response from the server.
* **expectContinue** `boolean` (optional) - Default: `false` - For H2, it appends the expect: 100-continue header, and halts the request body until a 100-continue is received from the remote server

@@ -492,7 +491,9 @@

- `text()`
- `json()`
- `arrayBuffer()`
- `body`
- `bodyUsed`
* [`.arrayBuffer()`](https://fetch.spec.whatwg.org/#dom-body-arraybuffer)
* [`.blob()`](https://fetch.spec.whatwg.org/#dom-body-blob)
* [`.bytes()`](https://fetch.spec.whatwg.org/#dom-body-bytes)
* [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)
* [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)
* `body`
* `bodyUsed`

@@ -989,2 +990,199 @@ `body` can not be consumed twice. For example, calling `text()` after `json()` throws `TypeError`.

##### `Response Error Interceptor`
**Introduction**
The Response Error Interceptor is designed to handle HTTP response errors efficiently. It intercepts responses and throws detailed errors for responses with status codes indicating failure (4xx, 5xx). This interceptor enhances error handling by providing structured error information, including response headers, data, and status codes.
**ResponseError Class**
The `ResponseError` class extends the `UndiciError` class and encapsulates detailed error information. It captures the response status code, headers, and data, providing a structured way to handle errors.
**Definition**
```js
class ResponseError extends UndiciError {
constructor (message, code, { headers, data }) {
super(message);
this.name = 'ResponseError';
this.message = message || 'Response error';
this.code = 'UND_ERR_RESPONSE';
this.statusCode = code;
this.data = data;
this.headers = headers;
}
}
```
**Interceptor Handler**
The interceptor's handler class extends `DecoratorHandler` and overrides methods to capture response details and handle errors based on the response status code.
**Methods**
- **onConnect**: Initializes response properties.
- **onHeaders**: Captures headers and status code. Decodes body if content type is `application/json` or `text/plain`.
- **onData**: Appends chunks to the body if status code indicates an error.
- **onComplete**: Finalizes error handling, constructs a `ResponseError`, and invokes the `onError` method.
- **onError**: Propagates errors to the handler.
**Definition**
```js
class Handler extends DecoratorHandler {
// Private properties
#handler;
#statusCode;
#contentType;
#decoder;
#headers;
#body;
constructor (opts, { handler }) {
super(handler);
this.#handler = handler;
}
onConnect (abort) {
this.#statusCode = 0;
this.#contentType = null;
this.#decoder = null;
this.#headers = null;
this.#body = '';
return this.#handler.onConnect(abort);
}
onHeaders (statusCode, rawHeaders, resume, statusMessage, headers = parseHeaders(rawHeaders)) {
this.#statusCode = statusCode;
this.#headers = headers;
this.#contentType = headers['content-type'];
if (this.#statusCode < 400) {
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers);
}
if (this.#contentType === 'application/json' || this.#contentType === 'text/plain') {
this.#decoder = new TextDecoder('utf-8');
}
}
onData (chunk) {
if (this.#statusCode < 400) {
return this.#handler.onData(chunk);
}
this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? '';
}
onComplete (rawTrailers) {
if (this.#statusCode >= 400) {
this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? '';
if (this.#contentType === '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, this.#headers, this.#body);
} finally {
Error.stackTraceLimit = stackTraceLimit;
}
this.#handler.onError(err);
} else {
this.#handler.onComplete(rawTrailers);
}
}
onError (err) {
this.#handler.onError(err);
}
}
module.exports = (dispatch) => (opts, handler) => opts.throwOnError
? dispatch(opts, new Handler(opts, { handler }))
: dispatch(opts, handler);
```
**Tests**
Unit tests ensure the interceptor functions correctly, handling both error and non-error responses appropriately.
**Example Tests**
- **No Error if `throwOnError` is False**:
```js
test('should not error if request is not meant to throw error', async (t) => {
const opts = { throwOnError: false };
const handler = { onError: () => {}, onData: () => {}, onComplete: () => {} };
const interceptor = createResponseErrorInterceptor((opts, handler) => handler.onComplete());
assert.doesNotThrow(() => interceptor(opts, handler));
});
```
- **Error if Status Code is in Specified Error Codes**:
```js
test('should error if request status code is in the specified error codes', async (t) => {
const opts = { throwOnError: true, statusCodes: [500] };
const response = { statusCode: 500 };
let capturedError;
const handler = {
onError: (err) => { capturedError = err; },
onData: () => {},
onComplete: () => {}
};
const interceptor = createResponseErrorInterceptor((opts, handler) => {
if (opts.throwOnError && opts.statusCodes.includes(response.statusCode)) {
handler.onError(new Error('Response Error'));
} else {
handler.onComplete();
}
});
interceptor({ ...opts, response }, handler);
await new Promise(resolve => setImmediate(resolve));
assert(capturedError, 'Expected error to be captured but it was not.');
assert.strictEqual(capturedError.message, 'Response Error');
assert.strictEqual(response.statusCode, 500);
});
```
- **No Error if Status Code is Not in Specified Error Codes**:
```js
test('should not error if request status code is not in the specified error codes', async (t) => {
const opts = { throwOnError: true, statusCodes: [500] };
const response = { statusCode: 404 };
const handler = {
onError: () => {},
onData: () => {},
onComplete: () => {}
};
const interceptor = createResponseErrorInterceptor((opts, handler) => {
if (opts.throwOnError && opts.statusCodes.includes(response.statusCode)) {
handler.onError(new Error('Response Error'));
} else {
handler.onComplete();
}
});
assert.doesNotThrow(() => interceptor({ ...opts, response }, handler));
});
```
**Conclusion**
The Response Error Interceptor provides a robust mechanism for handling HTTP response errors by capturing detailed error information and propagating it through a structured `ResponseError` class. This enhancement improves error handling and debugging capabilities in applications using the interceptor.
## Instance Events

@@ -991,0 +1189,0 @@

@@ -136,3 +136,2 @@ # Class: EnvHttpProxyAgent

* **origin** `string | URL`
* **maxRedirections** `Integer`.

@@ -139,0 +138,0 @@ Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).

@@ -31,2 +31,3 @@ # Fetch

- [`.blob()`](https://fetch.spec.whatwg.org/#dom-body-blob)
- [`.bytes()`](https://fetch.spec.whatwg.org/#dom-body-bytes)
- [`.formData()`](https://fetch.spec.whatwg.org/#dom-body-formdata)

@@ -33,0 +34,0 @@ - [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)

@@ -22,3 +22,2 @@ # Class: Pool

* **connections** `number | null` (optional) - Default: `null` - The number of `Client` instances to create. When set to `null`, the `Pool` instance will create an unlimited amount of `Client` instances.
* **interceptors** `{ Pool: DispatchInterceptor[] } }` - Default: `{ Pool: [] }` - A list of interceptors that are applied to the dispatch method. Additional logic can be applied (such as, but not limited to: 302 status code handling, authentication, cookies, compression and caching).

@@ -25,0 +24,0 @@ ## Instance Properties

@@ -22,3 +22,3 @@ # Class: RetryHandler

- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => void` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => number | null` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.
- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`

@@ -25,0 +25,0 @@ - **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)

@@ -24,3 +24,2 @@ 'use strict'

const RedirectHandler = require('./lib/handler/redirect-handler')
const createRedirectInterceptor = require('./lib/interceptor/redirect-interceptor')

@@ -41,3 +40,2 @@ Object.assign(Dispatcher.prototype, api)

module.exports.RedirectHandler = RedirectHandler
module.exports.createRedirectInterceptor = createRedirectInterceptor
module.exports.interceptors = {

@@ -124,4 +122,2 @@ redirect: require('./lib/interceptor/redirect'),

module.exports.FormData = require('./lib/web/fetch/formdata').FormData
module.exports.File = globalThis.File ?? require('node:buffer').File
module.exports.FileReader = require('./lib/web/fileapi/filereader').FileReader

@@ -128,0 +124,0 @@ const { setGlobalOrigin, getGlobalOrigin } = require('./lib/web/fetch/global')

@@ -98,3 +98,5 @@ 'use strict'

const connectHandler = new ConnectHandler(opts, callback)
this.dispatch({ ...opts, method: 'CONNECT' }, connectHandler)
const connectOptions = { ...opts, method: 'CONNECT' }
this.dispatch(connectOptions, connectHandler)
} catch (err) {

@@ -101,0 +103,0 @@ if (typeof callback !== 'function') {

@@ -8,2 +8,4 @@ 'use strict'

} = require('node:stream')
const assert = require('node:assert')
const { AsyncResource } = require('node:async_hooks')
const {

@@ -15,5 +17,3 @@ InvalidArgumentError,

const util = require('../core/util')
const { AsyncResource } = require('node:async_hooks')
const { addSignal, removeSignal } = require('./abort-signal')
const assert = require('node:assert')

@@ -150,3 +150,3 @@ const kResume = Symbol('resume')

onConnect (abort, context) {
const { ret, res } = this
const { res } = this

@@ -159,3 +159,2 @@ if (this.reason) {

assert(!res, 'pipeline cannot be retried')
assert(!ret.destroyed)

@@ -162,0 +161,0 @@ this.abort = abort

'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')
const { getResolveErrorBodyCallback } = require('./util')
const { AsyncResource } = require('node:async_hooks')

@@ -16,3 +15,3 @@ class RequestHandler extends AsyncResource {

const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError, highWaterMark } = opts
const { signal, method, opaque, body, onInfo, responseHeaders, highWaterMark } = opts

@@ -58,34 +57,18 @@ try {

this.onInfo = onInfo || null
this.throwOnError = throwOnError
this.highWaterMark = highWaterMark
this.signal = signal
this.reason = null
this.removeAbortListener = null
if (util.isStream(body)) {
body.on('error', (err) => {
this.onError(err)
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, this.reason)
} else if (this.abort) {
this.abort(this.reason)
}
})
}
if (this.signal) {
if (this.signal.aborted) {
this.reason = this.signal.reason ?? new RequestAbortedError()
} else {
this.removeAbortListener = util.addAbortListener(this.signal, () => {
this.reason = this.signal.reason ?? new RequestAbortedError()
if (this.res) {
util.destroy(this.res, this.reason)
} else if (this.abort) {
this.abort(this.reason)
}
if (this.removeAbortListener) {
this.res?.off('close', this.removeAbortListener)
this.removeAbortListener()
this.removeAbortListener = null
}
})
}
}
}

@@ -132,2 +115,3 @@

res.on('close', this.removeAbortListener)
this.removeAbortListener = null
}

@@ -138,16 +122,10 @@

if (callback !== null) {
if (this.throwOnError && statusCode >= 400) {
this.runInAsyncScope(getResolveErrorBodyCallback, null,
{ callback, body: res, contentType, statusCode, statusMessage, headers }
)
} else {
this.runInAsyncScope(callback, null, null, {
statusCode,
headers,
trailers: this.trailers,
opaque,
body: res,
context
})
}
this.runInAsyncScope(callback, null, null, {
statusCode,
headers,
trailers: this.trailers,
opaque,
body: res,
context
})
}

@@ -186,7 +164,10 @@ }

this.body = null
util.destroy(body, err)
if (util.isStream(body)) {
body.on('error', util.nop)
util.destroy(body, err)
}
}
if (this.removeAbortListener) {
res?.off('close', this.removeAbortListener)
this.removeAbortListener()

@@ -208,3 +189,5 @@ this.removeAbortListener = null

try {
this.dispatch(opts, new RequestHandler(opts, callback))
const handler = new RequestHandler(opts, callback)
this.dispatch(opts, handler)
} catch (err) {

@@ -211,0 +194,0 @@ if (typeof callback !== 'function') {

'use strict'
const assert = require('node:assert')
const { finished, PassThrough } = require('node:stream')
const { finished } = require('node:stream')
const { AsyncResource } = require('node:async_hooks')
const { InvalidArgumentError, InvalidReturnValueError } = require('../core/errors')
const util = require('../core/util')
const { getResolveErrorBodyCallback } = require('./util')
const { AsyncResource } = require('node:async_hooks')
const { addSignal, removeSignal } = require('./abort-signal')

@@ -17,3 +16,3 @@

const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError } = opts
const { signal, method, opaque, body, onInfo, responseHeaders } = opts

@@ -59,3 +58,2 @@ try {

this.onInfo = onInfo || null
this.throwOnError = throwOnError || false

@@ -84,3 +82,3 @@ if (util.isStream(body)) {

onHeaders (statusCode, rawHeaders, resume, statusMessage) {
const { factory, opaque, context, callback, responseHeaders } = this
const { factory, opaque, context, responseHeaders } = this

@@ -98,52 +96,39 @@ const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)

let res
if (factory === null) {
return
}
if (this.throwOnError && statusCode >= 400) {
const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers
const contentType = parsedHeaders['content-type']
res = new PassThrough()
const res = this.runInAsyncScope(factory, null, {
statusCode,
headers,
opaque,
context
})
this.callback = null
this.runInAsyncScope(getResolveErrorBodyCallback, null,
{ callback, body: res, contentType, statusCode, statusMessage, headers }
)
} else {
if (factory === null) {
return
}
if (
!res ||
typeof res.write !== 'function' ||
typeof res.end !== 'function' ||
typeof res.on !== 'function'
) {
throw new InvalidReturnValueError('expected Writable')
}
res = this.runInAsyncScope(factory, null, {
statusCode,
headers,
opaque,
context
})
// TODO: Avoid finished. It registers an unnecessary amount of listeners.
finished(res, { readable: false }, (err) => {
const { callback, res, opaque, trailers, abort } = this
if (
!res ||
typeof res.write !== 'function' ||
typeof res.end !== 'function' ||
typeof res.on !== 'function'
) {
throw new InvalidReturnValueError('expected Writable')
this.res = null
if (err || !res.readable) {
util.destroy(res, err)
}
// TODO: Avoid finished. It registers an unnecessary amount of listeners.
finished(res, { readable: false }, (err) => {
const { callback, res, opaque, trailers, abort } = this
this.callback = null
this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
this.res = null
if (err || !res.readable) {
util.destroy(res, err)
}
if (err) {
abort()
}
})
this.callback = null
this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
if (err) {
abort()
}
})
}
res.on('drain', resume)

@@ -214,3 +199,5 @@

try {
this.dispatch(opts, new StreamHandler(opts, factory, callback))
const handler = new StreamHandler(opts, factory, callback)
this.dispatch(opts, handler)
} catch (err) {

@@ -217,0 +204,0 @@ if (typeof callback !== 'function') {

@@ -5,5 +5,5 @@ 'use strict'

const { AsyncResource } = require('node:async_hooks')
const assert = require('node:assert')
const util = require('../core/util')
const { addSignal, removeSignal } = require('./abort-signal')
const assert = require('node:assert')

@@ -95,7 +95,9 @@ class UpgradeHandler extends AsyncResource {

const upgradeHandler = new UpgradeHandler(opts, callback)
this.dispatch({
const upgradeOpts = {
...opts,
method: opts.method || 'GET',
upgrade: opts.protocol || 'Websocket'
}, upgradeHandler)
}
this.dispatch(upgradeOpts, upgradeHandler)
} catch (err) {

@@ -102,0 +104,0 @@ if (typeof callback !== 'function') {

@@ -17,2 +17,4 @@ // Ported from https://github.com/nodejs/undici/pull/907

const kContentLength = Symbol('kContentLength')
const kUsed = Symbol('kUsed')
const kBytesRead = Symbol('kBytesRead')

@@ -39,5 +41,7 @@ const noop = () => {}

this[kConsume] = null
this[kBytesRead] = 0
this[kBody] = null
this[kUsed] = false
this[kContentType] = contentType
this[kContentLength] = contentLength
this[kContentLength] = Number.isFinite(contentLength) ? contentLength : null

@@ -51,3 +55,3 @@ // Is stream being consumed through Readable API?

destroy (err) {
_destroy (err, callback) {
if (!err && !this._readableState.endEmitted) {

@@ -61,6 +65,2 @@ err = new RequestAbortedError()

return super.destroy(err)
}
_destroy (err, callback) {
// Workaround for Node "bug". If the stream is destroyed in same

@@ -70,3 +70,3 @@ // tick as it is created, then a user who is waiting for a

// never get a chance and will always encounter an unhandled exception.
if (!this[kReading]) {
if (!this[kUsed]) {
setImmediate(() => {

@@ -83,2 +83,3 @@ callback(err)

this[kReading] = true
this[kUsed] = true
}

@@ -108,2 +109,4 @@ return super.on(ev, ...args)

push (chunk) {
this[kBytesRead] += chunk ? chunk.length : 0
if (this[kConsume] && chunk !== null) {

@@ -131,2 +134,7 @@ consumePush(this[kConsume], chunk)

// https://fetch.spec.whatwg.org/#dom-body-bytes
async bytes () {
return consume(this, 'bytes')
}
// https://fetch.spec.whatwg.org/#dom-body-arraybuffer

@@ -162,3 +170,2 @@ async arrayBuffer () {

async dump (opts) {
let limit = Number.isFinite(opts?.limit) ? opts.limit : 128 * 1024
const signal = opts?.signal

@@ -170,2 +177,4 @@

const limit = Number.isFinite(opts?.limit) ? opts.limit : 128 * 1024
signal?.throwIfAborted()

@@ -178,3 +187,3 @@

return await new Promise((resolve, reject) => {
if (this[kContentLength] > limit) {
if (this[kContentLength] > limit || this[kBytesRead] > limit) {
this.destroy(new AbortError())

@@ -199,4 +208,3 @@ }

.on('data', function (chunk) {
limit -= chunk.length
if (limit <= 0) {
if (this[kBytesRead] > limit) {
this.destroy()

@@ -208,2 +216,13 @@ }

}
/**
* @param {BufferEncoding} encoding
* @returns {BodyReadable}
*/
setEncoding (encoding) {
if (Buffer.isEncoding(encoding)) {
this._readableState.encoding = encoding
}
return this
}
}

@@ -286,6 +305,6 @@

if (state.endEmitted) {
consumeEnd(this[kConsume])
consumeEnd(this[kConsume], this._readableState.encoding)
} else {
consume.stream.on('end', function () {
consumeEnd(this[kConsume])
consumeEnd(this[kConsume], this._readableState.encoding)
})

@@ -304,4 +323,6 @@ }

* @param {number} length
* @param {BufferEncoding} encoding
* @returns {string}
*/
function chunksDecode (chunks, length) {
function chunksDecode (chunks, length, encoding) {
if (chunks.length === 0 || length === 0) {

@@ -321,6 +342,35 @@ return ''

: 0
return buffer.utf8Slice(start, bufferLength)
if (!encoding || encoding === 'utf8' || encoding === 'utf-8') {
return buffer.utf8Slice(start, bufferLength)
} else {
return buffer.subarray(start, bufferLength).toString(encoding)
}
}
function consumeEnd (consume) {
/**
* @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
}
function consumeEnd (consume, encoding) {
const { type, body, resolve, stream, length } = consume

@@ -330,17 +380,11 @@

if (type === 'text') {
resolve(chunksDecode(body, length))
resolve(chunksDecode(body, length, encoding))
} else if (type === 'json') {
resolve(JSON.parse(chunksDecode(body, length)))
resolve(JSON.parse(chunksDecode(body, length, encoding)))
} else if (type === 'arrayBuffer') {
const dst = new Uint8Array(length)
let pos = 0
for (const buf of body) {
dst.set(buf, pos)
pos += buf.byteLength
}
resolve(dst.buffer)
resolve(chunksConcat(body, length).buffer)
} else if (type === 'blob') {
resolve(new Blob(body, { type: stream[kContentType] }))
} else if (type === 'bytes') {
resolve(chunksConcat(body, length))
}

@@ -347,0 +391,0 @@

@@ -7,2 +7,3 @@ 'use strict'

const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
const timers = require('../util/timers')

@@ -134,3 +135,3 @@ let tls // include tls conditionally since it is not always available

const cancelTimeout = setupTimeout(() => onConnectTimeout(socket), timeout)
const cancelConnectTimeout = setupConnectTimeout(new WeakRef(socket), timeout)

@@ -140,3 +141,3 @@ socket

.once(protocol === 'https:' ? 'secureConnect' : 'connect', function () {
cancelTimeout()
cancelConnectTimeout()

@@ -150,3 +151,3 @@ if (callback) {

.on('error', function (err) {
cancelTimeout()
cancelConnectTimeout()

@@ -164,27 +165,41 @@ if (callback) {

function setupTimeout (onConnectTimeout, timeout) {
if (!timeout) {
return () => {}
}
const setupConnectTimeout = process.platform === 'win32'
? (socket, timeout) => {
if (!timeout) {
return () => { }
}
let s1 = null
let s2 = null
const timeoutId = setTimeout(() => {
// setImmediate is added to make sure that we prioritize socket error events over timeouts
s1 = setImmediate(() => {
if (process.platform === 'win32') {
let s1 = null
let s2 = null
const timer = timers.setTimeout(() => {
// 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())
} else {
onConnectTimeout()
s2 = setImmediate(() => onConnectTimeout(socket.deref()))
})
}, timeout)
return () => {
timers.clearTimeout(timer)
clearImmediate(s1)
clearImmediate(s2)
}
})
}, timeout)
return () => {
clearTimeout(timeoutId)
clearImmediate(s1)
clearImmediate(s2)
}
}
}
: (socket, timeout) => {
if (!timeout) {
return () => { }
}
let s1 = null
const timer = timers.setTimeout(() => {
// setImmediate is added to make sure that we prioritize socket error events over timeouts
s1 = setImmediate(() => {
onConnectTimeout(socket.deref())
})
}, timeout)
return () => {
timers.clearTimeout(timer)
clearImmediate(s1)
}
}
function onConnectTimeout (socket) {

@@ -191,0 +206,0 @@ let message = 'Connect Timeout Error'

'use strict'
class UndiciError extends Error {
constructor (message) {
super(message)
constructor (message, options) {
super(message, options)
this.name = 'UndiciError'

@@ -198,5 +198,17 @@ this.code = 'UND_ERR'

class ResponseError extends UndiciError {
constructor (message, code, { headers, data }) {
super(message)
this.name = 'ResponseError'
this.message = message || 'Response error'
this.code = 'UND_ERR_RESPONSE'
this.statusCode = code
this.data = data
this.headers = headers
}
}
class SecureProxyConnectionError extends UndiciError {
constructor (cause, message, options) {
super(message, { cause, ...(options ?? {}) })
constructor (cause, message, options = {}) {
super(message, { cause, ...options })
this.name = 'SecureProxyConnectionError'

@@ -231,3 +243,4 @@ this.message = message || 'Secure Proxy Connection failed'

RequestRetryError,
ResponseError,
SecureProxyConnectionError
}

@@ -43,5 +43,5 @@ 'use strict'

reset,
throwOnError,
expectContinue,
servername
servername,
throwOnError
}, handler) {

@@ -86,2 +86,6 @@ if (typeof path !== 'string') {

if (throwOnError != null) {
throw new InvalidArgumentError('invalid throwOnError')
}
this.headersTimeout = headersTimeout

@@ -91,4 +95,2 @@

this.throwOnError = throwOnError === true
this.method = method

@@ -189,3 +191,3 @@

this.servername = servername || getServerName(this.host)
this.servername = servername || getServerName(this.host) || null

@@ -192,0 +194,0 @@ this[kHandler] = handler

@@ -55,3 +55,2 @@ module.exports = {

kCounter: Symbol('socket request counter'),
kInterceptors: Symbol('dispatch interceptors'),
kMaxResponseSize: Symbol('max response size'),

@@ -58,0 +57,0 @@ kHTTP2Session: Symbol('http2Session'),

@@ -50,2 +50,5 @@ 'use strict'

let index = 0
/**
* @type {TstNode}
*/
let node = this

@@ -91,2 +94,5 @@ while (true) {

let index = 0
/**
* @type {TstNode}
*/
let node = this

@@ -93,0 +99,0 @@ while (node !== null && index < keylength) {

@@ -446,10 +446,2 @@ 'use strict'

function isErrored (body) {
return !!(body && stream.isErrored(body))
}
function isReadable (body) {
return !!(body && stream.isReadable(body))
}
function getSocketInfo (socket) {

@@ -522,3 +514,3 @@ return {

}
signal.addListener('abort', listener)
signal.once('abort', listener)
return () => signal.removeListener('abort', listener)

@@ -679,4 +671,2 @@ }

isDisturbed,
isErrored,
isReadable,
toUSVString,

@@ -683,0 +673,0 @@ isUSVString,

'use strict'
const { InvalidArgumentError } = require('../core/errors')
const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = require('../core/symbols')
const { kClients, kRunning, kClose, kDestroy, kDispatch } = require('../core/symbols')
const DispatcherBase = require('./dispatcher-base')

@@ -9,3 +9,2 @@ const Pool = require('./pool')

const util = require('../core/util')
const createRedirectInterceptor = require('../interceptor/redirect-interceptor')

@@ -15,3 +14,2 @@ const kOnConnect = Symbol('onConnect')

const kOnConnectionError = Symbol('onConnectionError')
const kMaxRedirections = Symbol('maxRedirections')
const kOnDrain = Symbol('onDrain')

@@ -28,5 +26,3 @@ const kFactory = Symbol('factory')

class Agent extends DispatcherBase {
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
super()
constructor ({ factory = defaultFactory, connect, ...options } = {}) {
if (typeof factory !== 'function') {

@@ -40,5 +36,3 @@ throw new InvalidArgumentError('factory must be a function.')

if (!Number.isInteger(maxRedirections) || maxRedirections < 0) {
throw new InvalidArgumentError('maxRedirections must be a positive number')
}
super()

@@ -49,11 +43,3 @@ if (connect && typeof connect !== 'function') {

this[kInterceptors] = options.interceptors?.Agent && Array.isArray(options.interceptors.Agent)
? options.interceptors.Agent
: [createRedirectInterceptor({ maxRedirections })]
this[kOptions] = { ...util.deepClone(options), connect }
this[kOptions].interceptors = options.interceptors
? { ...options.interceptors }
: undefined
this[kMaxRedirections] = maxRedirections
this[kFactory] = factory

@@ -60,0 +46,0 @@ this[kClients] = new Map()

@@ -16,3 +16,3 @@ 'use strict'

const Pool = require('./pool')
const { kUrl, kInterceptors } = require('../core/symbols')
const { kUrl } = require('../core/symbols')
const { parseOrigin } = require('../core/util')

@@ -54,2 +54,6 @@ const kFactory = Symbol('factory')

constructor (upstreams = [], { factory = defaultFactory, ...opts } = {}) {
if (typeof factory !== 'function') {
throw new InvalidArgumentError('factory must be a function.')
}
super()

@@ -68,9 +72,2 @@

if (typeof factory !== 'function') {
throw new InvalidArgumentError('factory must be a function.')
}
this[kInterceptors] = opts.interceptors?.BalancedPool && Array.isArray(opts.interceptors.BalancedPool)
? opts.interceptors.BalancedPool
: []
this[kFactory] = factory

@@ -77,0 +74,0 @@

@@ -81,3 +81,2 @@ 'use strict'

env: {
/* eslint-disable camelcase */

@@ -121,3 +120,2 @@ wasm_on_url: (p, at, len) => {

/* eslint-enable camelcase */
}

@@ -169,8 +167,8 @@ })

setTimeout (value, type) {
setTimeout (delay, type) {
this.timeoutType = type
if (value !== this.timeoutValue) {
timers.clearTimeout(this.timeout)
if (value) {
this.timeout = timers.setTimeout(onParserTimeout, value, this)
if (delay !== this.timeoutValue) {
this.timeout && timers.clearTimeout(this.timeout)
if (delay) {
this.timeout = timers.setTimeout(onParserTimeout, delay, new WeakRef(this))
// istanbul ignore else: only for jest

@@ -183,3 +181,3 @@ if (this.timeout.unref) {

}
this.timeoutValue = value
this.timeoutValue = delay
} else if (this.timeout) {

@@ -199,3 +197,3 @@ // istanbul ignore else: only for jest

assert(this.ptr != null)
assert(currentParser == null)
assert(currentParser === null)

@@ -227,5 +225,8 @@ this.llhttp.llhttp_resume(this.ptr)

execute (data) {
/**
* @param {Buffer} chunk
*/
execute (chunk) {
assert(currentParser === null)
assert(this.ptr != null)
assert(currentParser == null)
assert(!this.paused)

@@ -235,11 +236,13 @@

if (data.length > currentBufferSize) {
// Allocate a new buffer if the current buffer is too small.
if (chunk.length > currentBufferSize) {
if (currentBufferPtr) {
llhttp.free(currentBufferPtr)
}
currentBufferSize = Math.ceil(data.length / 4096) * 4096
// 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(data)
new Uint8Array(llhttp.memory.buffer, currentBufferPtr, currentBufferSize).set(chunk)

@@ -254,5 +257,5 @@ // Call `execute` on the wasm parser.

try {
currentBufferRef = data
currentBufferRef = chunk
currentParser = this
ret = llhttp.llhttp_execute(this.ptr, currentBufferPtr, data.length)
ret = llhttp.llhttp_execute(this.ptr, currentBufferPtr, chunk.length)
/* eslint-disable-next-line no-useless-catch */

@@ -267,21 +270,23 @@ } catch (err) {

const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr
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.slice(offset))
} else if (ret === constants.ERROR.PAUSED) {
this.paused = true
socket.unshift(data.slice(offset))
} else if (ret !== constants.ERROR.OK) {
const ptr = llhttp.llhttp_get_error_reason(this.ptr)
let message = ''
/* istanbul ignore else: difficult to make a test case for */
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() +
')'
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 = ''
/* istanbul ignore else: difficult to make a test case for */
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)
}
throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset))
}

@@ -294,4 +299,4 @@ } catch (err) {

destroy () {
assert(currentParser === null)
assert(this.ptr != null)
assert(currentParser == null)

@@ -301,3 +306,3 @@ this.llhttp.llhttp_free(this.ptr)

timers.clearTimeout(this.timeout)
this.timeout && timers.clearTimeout(this.timeout)
this.timeout = null

@@ -627,3 +632,3 @@ this.timeoutValue = null

function onParserTimeout (parser) {
const { socket, timeoutType, client } = parser
const { socket, timeoutType, client, paused } = parser.deref()

@@ -633,7 +638,7 @@ /* istanbul ignore else */

if (!socket[kWriting] || socket.writableNeedDrain || client[kRunning] > 1) {
assert(!parser.paused, 'cannot be paused while waiting for headers')
assert(!paused, 'cannot be paused while waiting for headers')
util.destroy(socket, new HeadersTimeoutError())
}
} else if (timeoutType === TIMEOUT_BODY) {
if (!parser.paused) {
if (!paused) {
util.destroy(socket, new BodyTimeoutError())

@@ -640,0 +645,0 @@ }

@@ -46,3 +46,2 @@ // @ts-check

kConnector,
kMaxRedirections,
kMaxRequests,

@@ -53,3 +52,2 @@ kCounter,

kDispatch,
kInterceptors,
kLocalAddress,

@@ -64,3 +62,2 @@ kMaxResponseSize,

const connectH2 = require('./client-h2.js')
let deprecatedInterceptorWarned = false

@@ -83,3 +80,2 @@ const kClosedResolve = Symbol('kClosedResolve')

constructor (url, {
interceptors,
maxHeaderSize,

@@ -102,3 +98,2 @@ headersTimeout,

maxCachedSessions,
maxRedirections,
connect,

@@ -114,4 +109,2 @@ maxRequestsPerClient,

} = {}) {
super()
if (keepAlive !== undefined) {

@@ -173,6 +166,2 @@ throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')

if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {
throw new InvalidArgumentError('maxRedirections must be a positive number')
}
if (maxRequestsPerClient != null && (!Number.isInteger(maxRequestsPerClient) || maxRequestsPerClient < 0)) {

@@ -206,2 +195,4 @@ throw new InvalidArgumentError('maxRequestsPerClient must be a positive number')

super()
if (typeof connect !== 'function') {

@@ -219,14 +210,2 @@ connect = buildConnector({

if (interceptors?.Client && Array.isArray(interceptors.Client)) {
this[kInterceptors] = interceptors.Client
if (!deprecatedInterceptorWarned) {
deprecatedInterceptorWarned = true
process.emitWarning('Client.Options#interceptor is deprecated. Use Dispatcher#compose instead.', {
code: 'UNDICI-CLIENT-INTERCEPTOR-DEPRECATED'
})
}
} else {
this[kInterceptors] = [createRedirectInterceptor({ maxRedirections })]
}
this[kUrl] = util.parseOrigin(url)

@@ -248,3 +227,2 @@ this[kConnector] = connect

this[kStrictContentLength] = strictContentLength == null ? true : strictContentLength
this[kMaxRedirections] = maxRedirections
this[kMaxRequests] = maxRequestsPerClient

@@ -375,4 +353,2 @@ this[kClosedResolve] = null

const createRedirectInterceptor = require('../interceptor/redirect-interceptor.js')
function onError (client, err) {

@@ -412,3 +388,3 @@ if (

assert(net.isIP(ip))
assert(net.isIPv6(ip))
hostname = ip

@@ -415,0 +391,0 @@ }

@@ -9,7 +9,6 @@ 'use strict'

} = require('../core/errors')
const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = require('../core/symbols')
const { kDestroy, kClose, kClosed, kDestroyed, kDispatch } = require('../core/symbols')
const kOnDestroyed = Symbol('onDestroyed')
const kOnClosed = Symbol('onClosed')
const kInterceptedDispatch = Symbol('Intercepted Dispatch')

@@ -34,19 +33,2 @@ class DispatcherBase extends Dispatcher {

get interceptors () {
return this[kInterceptors]
}
set interceptors (newInterceptors) {
if (newInterceptors) {
for (let i = newInterceptors.length - 1; i >= 0; i--) {
const interceptor = this[kInterceptors][i]
if (typeof interceptor !== 'function') {
throw new InvalidArgumentError('interceptor must be an function')
}
}
}
this[kInterceptors] = newInterceptors
}
close (callback) {

@@ -147,16 +129,2 @@ if (callback === undefined) {

[kInterceptedDispatch] (opts, handler) {
if (!this[kInterceptors] || this[kInterceptors].length === 0) {
this[kInterceptedDispatch] = this[kDispatch]
return this[kDispatch](opts, handler)
}
let dispatch = this[kDispatch].bind(this)
for (let i = this[kInterceptors].length - 1; i >= 0; i--) {
dispatch = this[kInterceptors][i](dispatch)
}
this[kInterceptedDispatch] = dispatch
return dispatch(opts, handler)
}
dispatch (opts, handler) {

@@ -180,3 +148,3 @@ if (!handler || typeof handler !== 'object') {

return this[kInterceptedDispatch](opts, handler)
return this[kDispatch](opts, handler)
} catch (err) {

@@ -183,0 +151,0 @@ if (typeof handler.onError !== 'function') {

@@ -38,29 +38,8 @@ 'use strict'

return new ComposedDispatcher(this, dispatch)
return new Proxy(this, {
get: (target, key) => key === 'dispatch' ? dispatch : target[key]
})
}
}
class ComposedDispatcher extends Dispatcher {
#dispatcher = null
#dispatch = null
constructor (dispatcher, dispatch) {
super()
this.#dispatcher = dispatcher
this.#dispatch = dispatch
}
dispatch (...args) {
this.#dispatch(...args)
}
close (...args) {
return this.#dispatcher.close(...args)
}
destroy (...args) {
return this.#dispatcher.destroy(...args)
}
}
module.exports = Dispatcher

@@ -15,3 +15,3 @@ 'use strict'

const util = require('../core/util')
const { kUrl, kInterceptors } = require('../core/symbols')
const { kUrl } = require('../core/symbols')
const buildConnector = require('../core/connect')

@@ -41,4 +41,2 @@

} = {}) {
super()
if (connections != null && (!Number.isFinite(connections) || connections < 0)) {

@@ -56,2 +54,4 @@ throw new InvalidArgumentError('invalid connections')

super()
if (typeof connect !== 'function') {

@@ -69,5 +69,2 @@ connect = buildConnector({

this[kInterceptors] = options.interceptors?.Pool && Array.isArray(options.interceptors.Pool)
? options.interceptors.Pool
: []
this[kConnections] = connections || null

@@ -74,0 +71,0 @@ this[kUrl] = util.parseOrigin(origin)

'use strict'
const { kProxy, kClose, kDestroy, kInterceptors } = require('../core/symbols')
const { kProxy, kClose, kDestroy } = require('../core/symbols')
const { URL } = require('node:url')

@@ -28,4 +28,2 @@ const Agent = require('./agent')

constructor (opts) {
super()
if (!opts || (typeof opts === 'object' && !(opts instanceof URL) && !opts.uri)) {

@@ -40,2 +38,4 @@ throw new InvalidArgumentError('Proxy uri is mandatory')

super()
const url = this.#getUrl(opts)

@@ -45,5 +45,2 @@ const { href, origin, port, protocol, username, password, hostname: proxyHostname } = url

this[kProxy] = { uri: href, protocol }
this[kInterceptors] = opts.interceptors?.ProxyAgent && Array.isArray(opts.interceptors.ProxyAgent)
? opts.interceptors.ProxyAgent
: []
this[kRequestTls] = opts.requestTls

@@ -50,0 +47,0 @@ this[kProxyTls] = opts.proxyTls

'use strict'
const assert = require('node:assert')
module.exports = class DecoratorHandler {
#handler
#onCompleteCalled = false
#onErrorCalled = false

@@ -18,2 +22,3 @@ constructor (handler) {

onError (...args) {
this.#onErrorCalled = true
return this.#handler.onError?.(...args)

@@ -23,2 +28,5 @@ }

onUpgrade (...args) {
assert(!this.#onCompleteCalled)
assert(!this.#onErrorCalled)
return this.#handler.onUpgrade?.(...args)

@@ -28,2 +36,5 @@ }

onResponseStarted (...args) {
assert(!this.#onCompleteCalled)
assert(!this.#onErrorCalled)
return this.#handler.onResponseStarted?.(...args)

@@ -33,2 +44,5 @@ }

onHeaders (...args) {
assert(!this.#onCompleteCalled)
assert(!this.#onErrorCalled)
return this.#handler.onHeaders?.(...args)

@@ -38,2 +52,5 @@ }

onData (...args) {
assert(!this.#onCompleteCalled)
assert(!this.#onErrorCalled)
return this.#handler.onData?.(...args)

@@ -43,2 +60,6 @@ }

onComplete (...args) {
assert(!this.#onCompleteCalled)
assert(!this.#onErrorCalled)
this.#onCompleteCalled = true
return this.#handler.onComplete?.(...args)

@@ -48,4 +69,7 @@ }

onBodySent (...args) {
assert(!this.#onCompleteCalled)
assert(!this.#onErrorCalled)
return this.#handler.onBodySent?.(...args)
}
}

@@ -27,2 +27,11 @@ 'use strict'

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) {

@@ -29,0 +38,0 @@ if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {

@@ -195,4 +195,14 @@ 'use strict'

if (statusCode !== 206) {
return true
// Only Partial Content 206 supposed to provide Content-Range,
// any other status code that partially consumed the payload
// should not be retry because it would result in downstream
// wrongly concatanete multiple responses.
if (statusCode !== 206 && (this.start > 0 || statusCode !== 200)) {
this.abort(
new RequestRetryError('server does not support the range header and the payload was partially consumed', statusCode, {
headers,
data: { count: this.retryCount }
})
)
return false
}

@@ -275,3 +285,7 @@

// equal
if (this.etag != null && this.etag.startsWith('W/')) {
if (
this.etag != null &&
this.etag[0] === 'W' &&
this.etag[1] === '/'
) {
this.etag = null

@@ -334,2 +348,7 @@ }

/**
* @this {RetryHandler}
* @param {Error} [err]
* @returns
*/
function onRetry (err) {

@@ -336,0 +355,0 @@ if (err != null || this.aborted || isDisturbed(this.opts.body)) {

@@ -17,4 +17,2 @@ 'use strict'

constructor ({ maxSize }, handler) {
super(handler)
if (maxSize != null && (!Number.isFinite(maxSize) || maxSize < 1)) {

@@ -24,2 +22,4 @@ throw new InvalidArgumentError('maxSize must be a number greater than 0')

super(handler)
this.#maxSize = maxSize ?? this.#maxSize

@@ -26,0 +26,0 @@ this.#handler = handler

'use strict'
const RedirectHandler = require('../handler/redirect-handler')
module.exports = opts => {
const globalMaxRedirections = opts?.maxRedirections
return dispatch => {
return function redirectInterceptor (opts, handler) {
const { maxRedirections = globalMaxRedirections, ...baseOpts } = opts
function createRedirectInterceptor ({ maxRedirections: defaultMaxRedirections } = {}) {
return (dispatch) => {
return function Intercept (opts, handler) {
const { maxRedirections = defaultMaxRedirections, ...rest } = opts
if (!maxRedirections) {
if (maxRedirections == null || maxRedirections === 0) {
return dispatch(opts, handler)
}
const redirectHandler = new RedirectHandler(
dispatch,
maxRedirections,
opts,
handler
)
return dispatch(baseOpts, redirectHandler)
const dispatchOpts = { ...rest, maxRedirections: 0 } // Stop sub dispatcher from also redirecting.
const redirectHandler = new RedirectHandler(dispatch, maxRedirections, dispatchOpts, handler)
return dispatch(dispatchOpts, redirectHandler)
}
}
}
module.exports = createRedirectInterceptor
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SPECIAL_HEADERS = exports.HEADER_STATE = exports.MINOR = exports.MAJOR = exports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS = exports.TOKEN = exports.STRICT_TOKEN = exports.HEX = exports.URL_CHAR = exports.STRICT_URL_CHAR = exports.USERINFO_CHARS = exports.MARK = exports.ALPHANUM = exports.NUM = exports.HEX_MAP = exports.NUM_MAP = exports.ALPHA = exports.FINISH = exports.H_METHOD_MAP = exports.METHOD_MAP = exports.METHODS_RTSP = exports.METHODS_ICE = exports.METHODS_HTTP = exports.METHODS = exports.LENIENT_FLAGS = exports.FLAGS = exports.TYPE = exports.ERROR = void 0;
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");
// C headers
var ERROR;
(function (ERROR) {
ERROR[ERROR["OK"] = 0] = "OK";
ERROR[ERROR["INTERNAL"] = 1] = "INTERNAL";
ERROR[ERROR["STRICT"] = 2] = "STRICT";
ERROR[ERROR["LF_EXPECTED"] = 3] = "LF_EXPECTED";
ERROR[ERROR["UNEXPECTED_CONTENT_LENGTH"] = 4] = "UNEXPECTED_CONTENT_LENGTH";
ERROR[ERROR["CLOSED_CONNECTION"] = 5] = "CLOSED_CONNECTION";
ERROR[ERROR["INVALID_METHOD"] = 6] = "INVALID_METHOD";
ERROR[ERROR["INVALID_URL"] = 7] = "INVALID_URL";
ERROR[ERROR["INVALID_CONSTANT"] = 8] = "INVALID_CONSTANT";
ERROR[ERROR["INVALID_VERSION"] = 9] = "INVALID_VERSION";
ERROR[ERROR["INVALID_HEADER_TOKEN"] = 10] = "INVALID_HEADER_TOKEN";
ERROR[ERROR["INVALID_CONTENT_LENGTH"] = 11] = "INVALID_CONTENT_LENGTH";
ERROR[ERROR["INVALID_CHUNK_SIZE"] = 12] = "INVALID_CHUNK_SIZE";
ERROR[ERROR["INVALID_STATUS"] = 13] = "INVALID_STATUS";
ERROR[ERROR["INVALID_EOF_STATE"] = 14] = "INVALID_EOF_STATE";
ERROR[ERROR["INVALID_TRANSFER_ENCODING"] = 15] = "INVALID_TRANSFER_ENCODING";
ERROR[ERROR["CB_MESSAGE_BEGIN"] = 16] = "CB_MESSAGE_BEGIN";
ERROR[ERROR["CB_HEADERS_COMPLETE"] = 17] = "CB_HEADERS_COMPLETE";
ERROR[ERROR["CB_MESSAGE_COMPLETE"] = 18] = "CB_MESSAGE_COMPLETE";
ERROR[ERROR["CB_CHUNK_HEADER"] = 19] = "CB_CHUNK_HEADER";
ERROR[ERROR["CB_CHUNK_COMPLETE"] = 20] = "CB_CHUNK_COMPLETE";
ERROR[ERROR["PAUSED"] = 21] = "PAUSED";
ERROR[ERROR["PAUSED_UPGRADE"] = 22] = "PAUSED_UPGRADE";
ERROR[ERROR["PAUSED_H2_UPGRADE"] = 23] = "PAUSED_H2_UPGRADE";
ERROR[ERROR["USER"] = 24] = "USER";
})(ERROR = exports.ERROR || (exports.ERROR = {}));
var TYPE;
(function (TYPE) {
TYPE[TYPE["BOTH"] = 0] = "BOTH";
TYPE[TYPE["REQUEST"] = 1] = "REQUEST";
TYPE[TYPE["RESPONSE"] = 2] = "RESPONSE";
})(TYPE = exports.TYPE || (exports.TYPE = {}));
var FLAGS;
(function (FLAGS) {
FLAGS[FLAGS["CONNECTION_KEEP_ALIVE"] = 1] = "CONNECTION_KEEP_ALIVE";
FLAGS[FLAGS["CONNECTION_CLOSE"] = 2] = "CONNECTION_CLOSE";
FLAGS[FLAGS["CONNECTION_UPGRADE"] = 4] = "CONNECTION_UPGRADE";
FLAGS[FLAGS["CHUNKED"] = 8] = "CHUNKED";
FLAGS[FLAGS["UPGRADE"] = 16] = "UPGRADE";
FLAGS[FLAGS["CONTENT_LENGTH"] = 32] = "CONTENT_LENGTH";
FLAGS[FLAGS["SKIPBODY"] = 64] = "SKIPBODY";
FLAGS[FLAGS["TRAILING"] = 128] = "TRAILING";
// 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,
};
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
FLAGS[FLAGS["TRANSFER_ENCODING"] = 512] = "TRANSFER_ENCODING";
})(FLAGS = exports.FLAGS || (exports.FLAGS = {}));
var LENIENT_FLAGS;
(function (LENIENT_FLAGS) {
LENIENT_FLAGS[LENIENT_FLAGS["HEADERS"] = 1] = "HEADERS";
LENIENT_FLAGS[LENIENT_FLAGS["CHUNKED_LENGTH"] = 2] = "CHUNKED_LENGTH";
LENIENT_FLAGS[LENIENT_FLAGS["KEEP_ALIVE"] = 4] = "KEEP_ALIVE";
})(LENIENT_FLAGS = exports.LENIENT_FLAGS || (exports.LENIENT_FLAGS = {}));
var METHODS;
(function (METHODS) {
METHODS[METHODS["DELETE"] = 0] = "DELETE";
METHODS[METHODS["GET"] = 1] = "GET";
METHODS[METHODS["HEAD"] = 2] = "HEAD";
METHODS[METHODS["POST"] = 3] = "POST";
METHODS[METHODS["PUT"] = 4] = "PUT";
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 */
METHODS[METHODS["CONNECT"] = 5] = "CONNECT";
METHODS[METHODS["OPTIONS"] = 6] = "OPTIONS";
METHODS[METHODS["TRACE"] = 7] = "TRACE";
'CONNECT': 5,
'OPTIONS': 6,
'TRACE': 7,
/* WebDAV */
METHODS[METHODS["COPY"] = 8] = "COPY";
METHODS[METHODS["LOCK"] = 9] = "LOCK";
METHODS[METHODS["MKCOL"] = 10] = "MKCOL";
METHODS[METHODS["MOVE"] = 11] = "MOVE";
METHODS[METHODS["PROPFIND"] = 12] = "PROPFIND";
METHODS[METHODS["PROPPATCH"] = 13] = "PROPPATCH";
METHODS[METHODS["SEARCH"] = 14] = "SEARCH";
METHODS[METHODS["UNLOCK"] = 15] = "UNLOCK";
METHODS[METHODS["BIND"] = 16] = "BIND";
METHODS[METHODS["REBIND"] = 17] = "REBIND";
METHODS[METHODS["UNBIND"] = 18] = "UNBIND";
METHODS[METHODS["ACL"] = 19] = "ACL";
'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 */
METHODS[METHODS["REPORT"] = 20] = "REPORT";
METHODS[METHODS["MKACTIVITY"] = 21] = "MKACTIVITY";
METHODS[METHODS["CHECKOUT"] = 22] = "CHECKOUT";
METHODS[METHODS["MERGE"] = 23] = "MERGE";
'REPORT': 20,
'MKACTIVITY': 21,
'CHECKOUT': 22,
'MERGE': 23,
/* upnp */
METHODS[METHODS["M-SEARCH"] = 24] = "M-SEARCH";
METHODS[METHODS["NOTIFY"] = 25] = "NOTIFY";
METHODS[METHODS["SUBSCRIBE"] = 26] = "SUBSCRIBE";
METHODS[METHODS["UNSUBSCRIBE"] = 27] = "UNSUBSCRIBE";
'M-SEARCH': 24,
'NOTIFY': 25,
'SUBSCRIBE': 26,
'UNSUBSCRIBE': 27,
/* RFC-5789 */
METHODS[METHODS["PATCH"] = 28] = "PATCH";
METHODS[METHODS["PURGE"] = 29] = "PURGE";
'PATCH': 28,
'PURGE': 29,
/* CalDAV */
METHODS[METHODS["MKCALENDAR"] = 30] = "MKCALENDAR";
'MKCALENDAR': 30,
/* RFC-2068, section 19.6.1.2 */
METHODS[METHODS["LINK"] = 31] = "LINK";
METHODS[METHODS["UNLINK"] = 32] = "UNLINK";
'LINK': 31,
'UNLINK': 32,
/* icecast */
METHODS[METHODS["SOURCE"] = 33] = "SOURCE";
'SOURCE': 33,
/* RFC-7540, section 11.6 */
METHODS[METHODS["PRI"] = 34] = "PRI";
'PRI': 34,
/* RFC-2326 RTSP */
METHODS[METHODS["DESCRIBE"] = 35] = "DESCRIBE";
METHODS[METHODS["ANNOUNCE"] = 36] = "ANNOUNCE";
METHODS[METHODS["SETUP"] = 37] = "SETUP";
METHODS[METHODS["PLAY"] = 38] = "PLAY";
METHODS[METHODS["PAUSE"] = 39] = "PAUSE";
METHODS[METHODS["TEARDOWN"] = 40] = "TEARDOWN";
METHODS[METHODS["GET_PARAMETER"] = 41] = "GET_PARAMETER";
METHODS[METHODS["SET_PARAMETER"] = 42] = "SET_PARAMETER";
METHODS[METHODS["REDIRECT"] = 43] = "REDIRECT";
METHODS[METHODS["RECORD"] = 44] = "RECORD";
'DESCRIBE': 35,
'ANNOUNCE': 36,
'SETUP': 37,
'PLAY': 38,
'PAUSE': 39,
'TEARDOWN': 40,
'GET_PARAMETER': 41,
'SET_PARAMETER': 42,
'REDIRECT': 43,
'RECORD': 44,
/* RAOP */
METHODS[METHODS["FLUSH"] = 45] = "FLUSH";
})(METHODS = exports.METHODS || (exports.METHODS = {}));
'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 = [
METHODS.DELETE,
METHODS.GET,
METHODS.HEAD,
METHODS.POST,
METHODS.PUT,
METHODS.CONNECT,
METHODS.OPTIONS,
METHODS.TRACE,
METHODS.COPY,
METHODS.LOCK,
METHODS.MKCOL,
METHODS.MOVE,
METHODS.PROPFIND,
METHODS.PROPPATCH,
METHODS.SEARCH,
METHODS.UNLOCK,
METHODS.BIND,
METHODS.REBIND,
METHODS.UNBIND,
METHODS.ACL,
METHODS.REPORT,
METHODS.MKACTIVITY,
METHODS.CHECKOUT,
METHODS.MERGE,
METHODS['M-SEARCH'],
METHODS.NOTIFY,
METHODS.SUBSCRIBE,
METHODS.UNSUBSCRIBE,
METHODS.PATCH,
METHODS.PURGE,
METHODS.MKCALENDAR,
METHODS.LINK,
METHODS.UNLINK,
METHODS.PRI,
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?
METHODS.SOURCE,
exports.METHODS.SOURCE,
exports.METHODS.QUERY,
];
exports.METHODS_ICE = [
METHODS.SOURCE,
exports.METHODS.SOURCE,
];
exports.METHODS_RTSP = [
METHODS.OPTIONS,
METHODS.DESCRIBE,
METHODS.ANNOUNCE,
METHODS.SETUP,
METHODS.PLAY,
METHODS.PAUSE,
METHODS.TEARDOWN,
METHODS.GET_PARAMETER,
METHODS.SET_PARAMETER,
METHODS.REDIRECT,
METHODS.RECORD,
METHODS.FLUSH,
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
METHODS.GET,
METHODS.POST,
exports.METHODS.GET,
exports.METHODS.POST,
];
exports.METHOD_MAP = utils_1.enumToMap(METHODS);
exports.H_METHOD_MAP = {};
Object.keys(exports.METHOD_MAP).forEach((key) => {
if (/^H/.test(key)) {
exports.H_METHOD_MAP[key] = exports.METHOD_MAP[key];
}
});
var FINISH;
(function (FINISH) {
FINISH[FINISH["SAFE"] = 0] = "SAFE";
FINISH[FINISH["SAFE_WITH_CB"] = 1] = "SAFE_WITH_CB";
FINISH[FINISH["UNSAFE"] = 2] = "UNSAFE";
})(FINISH = exports.FINISH || (exports.FINISH = {}));
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 = [];

@@ -216,3 +440,3 @@ for (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); i++) {

// TODO(indutny): use RFC
exports.STRICT_URL_CHAR = [
exports.URL_CHAR = [
'!', '"', '$', '%', '&', '\'',

@@ -225,8 +449,2 @@ '(', ')', '*', '+', ',', '-', '.', '/',

].concat(exports.ALPHANUM);
exports.URL_CHAR = exports.STRICT_URL_CHAR
.concat(['\t', '\f']);
// All characters with 0x80 bit set to 1
for (let i = 0x80; i <= 0xff; i++) {
exports.URL_CHAR.push(i);
}
exports.HEX = exports.NUM.concat(['a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F']);

@@ -240,3 +458,3 @@ /* Tokens as defined by rfc 2616. Also lowercases them.

*/
exports.STRICT_TOKEN = [
exports.TOKEN = [
'!', '#', '$', '%', '&', '\'',

@@ -247,3 +465,2 @@ '*', '+', '-', '.',

].concat(exports.ALPHANUM);
exports.TOKEN = exports.STRICT_TOKEN.concat([' ']);
/*

@@ -261,23 +478,26 @@ * Verify that a char is a valid visible (printable) US-ASCII

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;
var HEADER_STATE;
(function (HEADER_STATE) {
HEADER_STATE[HEADER_STATE["GENERAL"] = 0] = "GENERAL";
HEADER_STATE[HEADER_STATE["CONNECTION"] = 1] = "CONNECTION";
HEADER_STATE[HEADER_STATE["CONTENT_LENGTH"] = 2] = "CONTENT_LENGTH";
HEADER_STATE[HEADER_STATE["TRANSFER_ENCODING"] = 3] = "TRANSFER_ENCODING";
HEADER_STATE[HEADER_STATE["UPGRADE"] = 4] = "UPGRADE";
HEADER_STATE[HEADER_STATE["CONNECTION_KEEP_ALIVE"] = 5] = "CONNECTION_KEEP_ALIVE";
HEADER_STATE[HEADER_STATE["CONNECTION_CLOSE"] = 6] = "CONNECTION_CLOSE";
HEADER_STATE[HEADER_STATE["CONNECTION_UPGRADE"] = 7] = "CONNECTION_UPGRADE";
HEADER_STATE[HEADER_STATE["TRANSFER_ENCODING_CHUNKED"] = 8] = "TRANSFER_ENCODING_CHUNKED";
})(HEADER_STATE = exports.HEADER_STATE || (exports.HEADER_STATE = {}));
exports.SPECIAL_HEADERS = {
'connection': HEADER_STATE.CONNECTION,
'content-length': HEADER_STATE.CONTENT_LENGTH,
'proxy-connection': HEADER_STATE.CONNECTION,
'transfer-encoding': HEADER_STATE.TRANSFER_ENCODING,
'upgrade': HEADER_STATE.UPGRADE,
'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,
};
//# sourceMappingURL=constants.js.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.enumToMap = void 0;
function enumToMap(obj) {
const res = {};
Object.keys(obj).forEach((key) => {
const value = obj[key];
if (typeof value === 'number') {
res[key] = value;
}
});
return res;
function enumToMap(obj, filter = [], exceptions = []) {
var _a, _b;
const emptyFilter = ((_a = filter === null || filter === void 0 ? void 0 : filter.length) !== null && _a !== void 0 ? _a : 0) === 0;
const emptyExceptions = ((_b = exceptions === null || exceptions === void 0 ? void 0 : exceptions.length) !== null && _b !== void 0 ? _b : 0) === 0;
return Object.fromEntries(Object.entries(obj).filter(([, value]) => {
return (typeof value === 'number' &&
(emptyFilter || filter.includes(value)) &&
(emptyExceptions || !exceptions.includes(value)));
}));
}
exports.enumToMap = enumToMap;
//# sourceMappingURL=utils.js.map

@@ -24,4 +24,2 @@ 'use strict'

constructor (origin, opts) {
super(origin, opts)
if (!opts || !opts.agent || typeof opts.agent.dispatch !== 'function') {

@@ -31,2 +29,4 @@ throw new InvalidArgumentError('Argument opts.agent must implement Agent')

super(origin, opts)
this[kMockAgent] = opts.agent

@@ -33,0 +33,0 @@ this[kOrigin] = origin

@@ -24,4 +24,2 @@ 'use strict'

constructor (origin, opts) {
super(origin, opts)
if (!opts || !opts.agent || typeof opts.agent.dispatch !== 'function') {

@@ -31,2 +29,4 @@ throw new InvalidArgumentError('Argument opts.agent must implement Agent')

super(origin, opts)
this[kMockAgent] = opts.agent

@@ -33,0 +33,0 @@ this[kOrigin] = origin

@@ -18,2 +18,3 @@ 'use strict'

kOriginalClose: Symbol('original agent close'),
kOriginalDispatch: Symbol('original dispatch'),
kOrigin: Symbol('origin'),

@@ -20,0 +21,0 @@ kIsMockActive: Symbol('is mock active'),

'use strict'
const TICK_MS = 499
/**
* 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.
*/
let fastNow = Date.now()
const nativeSetTimeout = global.setTimeout
const nativeClearTimeout = global.clearTimeout
/**
* 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 = []
function onTimeout () {
fastNow = Date.now()
/**
* 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
let idx = 0
while (idx < len) {
/**
* @type {FastTimer}
*/
const timer = fastTimers[idx]
if (timer.state === 0) {
timer.state = fastNow + timer.delay - TICK_MS
} else if (timer.state > 0 && fastNow >= timer.state) {
timer.state = -1
timer.callback(timer.opaque)
// 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 === -1) {
timer.state = -2
if (idx !== len - 1) {
fastTimers[idx] = fastTimers.pop()
} else {
fastTimers.pop()
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]
}
len -= 1
} else {
idx += 1
++idx
}
}
if (fastTimers.length > 0) {
// 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()

@@ -44,7 +194,12 @@ }

function refreshTimeout () {
if (fastNowTimeout?.refresh) {
// If the fastNowTimeout is already set, refresh it.
if (fastNowTimeout) {
fastNowTimeout.refresh()
// fastNowTimeout is not instantiated yet, create a new Timer.
} else {
clearTimeout(fastNowTimeout)
fastNowTimeout = setTimeout(onTimeout, TICK_MS)
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.
if (fastNowTimeout.unref) {

@@ -56,46 +211,171 @@ fastNowTimeout.unref()

class Timeout {
constructor (callback, delay, opaque) {
this.callback = callback
this.delay = delay
this.opaque = opaque
/**
* The `FastTimer` class is a data structure designed to store and manage
* timer information.
*/
class FastTimer {
[kFastTimer] = true
// -2 not in timer list
// -1 in timer list but inactive
// 0 in timer list waiting for time
// > 0 in timer list waiting for time to expire
this.state = -2
/**
* 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 () {
if (this.state === -2) {
// 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 (!fastNowTimeout || fastTimers.length === 1) {
refreshTimeout()
}
}
this.state = 0
// 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 () {
this.state = -1
// 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 = {
setTimeout (callback, delay, opaque) {
return delay <= 1e3
? setTimeout(callback, delay, opaque)
: new Timeout(callback, delay, opaque)
/**
* 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
? nativeSetTimeout(callback, delay, arg)
: new FastTimer(callback, delay, arg)
},
/**
* The clearTimeout method cancels an instantiated Timer previously created
* by calling setTimeout.
*
* @param {FastTimer} timeout
*/
clearTimeout (timeout) {
if (timeout instanceof 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)
nativeClearTimeout(timeout)
}
}
},
/**
* The now method returns the value of the internal fast timer clock.
*
* @returns {number}
*/
now () {
return fastNow
},
/**
* Exporting for testing purposes only.
* Marking as deprecated to discourage any use outside of testing.
* @deprecated
*/
kFastTimer
}

@@ -12,10 +12,10 @@ 'use strict'

* @property {string} value
* @property {Date|number|undefined} expires
* @property {number|undefined} maxAge
* @property {string|undefined} domain
* @property {string|undefined} path
* @property {boolean|undefined} secure
* @property {boolean|undefined} httpOnly
* @property {'Strict'|'Lax'|'None'} sameSite
* @property {string[]} unparsed
* @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]
*/

@@ -30,5 +30,7 @@

webidl.brandCheck(headers, Headers, { strict: false })
webidl.brandCheckMultiple(headers, [Headers, globalThis.Headers])
const cookie = headers.get('cookie')
/** @type {Record<string, string>} */
const out = {}

@@ -56,3 +58,3 @@

function deleteCookie (headers, name, attributes) {
webidl.brandCheck(headers, Headers, { strict: false })
webidl.brandCheckMultiple(headers, [Headers, globalThis.Headers])

@@ -82,3 +84,3 @@ const prefix = 'deleteCookie'

webidl.brandCheck(headers, Headers, { strict: false })
webidl.brandCheckMultiple(headers, [Headers, globalThis.Headers])

@@ -102,3 +104,3 @@ const cookies = headers.getSetCookie()

webidl.brandCheck(headers, Headers, { strict: false })
webidl.brandCheckMultiple(headers, [Headers, globalThis.Headers])

@@ -110,3 +112,3 @@ cookie = webidl.converters.Cookie(cookie)

if (str) {
headers.append('Set-Cookie', str)
headers.append('set-cookie', str, true)
}

@@ -113,0 +115,0 @@ }

@@ -12,3 +12,3 @@ 'use strict'

* @param {string} header
* @returns if the header is invalid, null will be returned
* @returns {import('./index').Cookie|null} if the header is invalid, null will be returned
*/

@@ -89,3 +89,3 @@ function parseSetCookie (header) {

* @param {string} unparsedAttributes
* @param {[Object.<string, unknown>]={}} cookieAttributeList
* @param {Object.<string, unknown>} [cookieAttributeList={}]
*/

@@ -92,0 +92,0 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {}) {

@@ -38,5 +38,5 @@ 'use strict'

* @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.
* @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.
*/

@@ -48,3 +48,3 @@

*/
state = null
state

@@ -68,3 +68,3 @@ /**

/**
* @type {Buffer}
* @type {Buffer|null}
*/

@@ -84,4 +84,5 @@ buffer = null

* @param {object} options
* @param {eventSourceSettings} options.eventSourceSettings
* @param {Function} [options.push]
* @param {boolean} [options.readableObjectMode]
* @param {eventSourceSettings} [options.eventSourceSettings]
* @param {(chunk: any, encoding?: BufferEncoding | undefined) => boolean} [options.push]
*/

@@ -287,3 +288,3 @@ constructor (options = {}) {

* @param {Buffer} line
* @param {EventStreamEvent} event
* @param {EventSourceStreamEvent} event
*/

@@ -290,0 +291,0 @@ parseLine (line, event) {

@@ -31,3 +31,4 @@ 'use strict'

* The readyState attribute represents the state of the connection.
* @enum
* @typedef ReadyState
* @type {0|1|2}
* @readonly

@@ -84,5 +85,8 @@ * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#dom-eventsource-readystate-dev

#url = null
#url
#withCredentials = false
/**
* @type {ReadyState}
*/
#readyState = CONNECTING

@@ -103,3 +107,3 @@

* @param {string} url
* @param {EventSourceInit} [eventSourceInitDict]
* @param {EventSourceInit} [eventSourceInitDict={}]
* @see https://html.spec.whatwg.org/multipage/server-sent-events.html#the-eventsource-interface

@@ -121,3 +125,3 @@ */

url = webidl.converters.USVString(url, prefix, 'url')
url = webidl.converters.USVString(url)
eventSourceInitDict = webidl.converters.EventSourceInitDict(eventSourceInitDict, prefix, 'eventSourceInitDict')

@@ -155,3 +159,3 @@

// withCredentials attribute to true.
if (eventSourceInitDict.withCredentials) {
if (eventSourceInitDict.withCredentials === true) {
corsAttributeState = USE_CREDENTIALS

@@ -197,3 +201,3 @@ this.#withCredentials = true

* values described below.
* @returns {0|1|2}
* @returns {ReadyState}
* @readonly

@@ -200,0 +204,0 @@ */

@@ -6,4 +6,2 @@ 'use strict'

ReadableStreamFrom,
isBlobLike,
isReadableStreamLike,
readableStreamClose,

@@ -48,3 +46,3 @@ createDeferredPromise,

stream = object
} else if (isBlobLike(object)) {
} else if (object instanceof Blob) {
// 3. Otherwise, if object is a Blob object, set stream to the

@@ -72,3 +70,3 @@ // result of running object’s get stream.

// 5. Assert: stream is a ReadableStream object.
assert(isReadableStreamLike(stream))
assert(stream instanceof ReadableStream)

@@ -118,3 +116,3 @@ // 6. Let action be null.

source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength))
} else if (util.isFormDataLike(object)) {
} else if (object instanceof FormData) {
const boundary = `----formdata-undici-0${`${Math.floor(Math.random() * 1e11)}`.padStart(11, '0')}`

@@ -185,3 +183,3 @@ const prefix = `--${boundary}\r\nContent-Disposition: form-data`

type = `multipart/form-data; boundary=${boundary}`
} else if (isBlobLike(object)) {
} else if (object instanceof Blob) {
// Blob

@@ -188,0 +186,0 @@

@@ -434,3 +434,3 @@ 'use strict'

// 1. Remove all ASCII whitespace from data.
data = data.replace(ASCII_WHITESPACE_REPLACE_REGEX, '') // eslint-disable-line
data = data.replace(ASCII_WHITESPACE_REPLACE_REGEX, '')

@@ -437,0 +437,0 @@ let dataLength = data.length

@@ -6,3 +6,2 @@ 'use strict'

const { HTTP_TOKEN_CODEPOINTS, isomorphicDecode } = require('./data-url')
const { isFileLike } = require('./file')
const { makeEntry } = require('./formdata')

@@ -199,3 +198,3 @@ const assert = require('node:assert')

assert(isUSVString(name))
assert((typeof value === 'string' && isUSVString(value)) || isFileLike(value))
assert((typeof value === 'string' && isUSVString(value)) || value instanceof File)

@@ -202,0 +201,0 @@ // 5.13. Create an entry with name and value, and append it to entry list.

'use strict'
const { isBlobLike, iteratorMixin } = require('./util')
const { iteratorMixin } = require('./util')
const { kState } = require('./symbols')
const { kEnumerableProperty } = require('../../core/util')
const { FileLike, isFileLike } = require('./file')
const { webidl } = require('./webidl')

@@ -34,6 +33,12 @@ const { File: NativeFile } = require('node:buffer')

if (arguments.length === 3 && !isBlobLike(value)) {
throw new TypeError(
"Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'"
)
name = webidl.converters.USVString(name)
if (arguments.length === 3 || value instanceof Blob) {
value = webidl.converters.Blob(value, prefix, 'value')
if (filename !== undefined) {
filename = webidl.converters.USVString(filename)
}
} else {
value = webidl.converters.USVString(value)
}

@@ -43,10 +48,2 @@

name = webidl.converters.USVString(name, prefix, 'name')
value = isBlobLike(value)
? webidl.converters.Blob(value, prefix, 'value', { strict: false })
: webidl.converters.USVString(value, prefix, 'value')
filename = arguments.length === 3
? webidl.converters.USVString(filename, prefix, 'filename')
: undefined
// 2. Let entry be the result of creating an entry with

@@ -66,3 +63,3 @@ // name, value, and filename if given.

name = webidl.converters.USVString(name, prefix, 'name')
name = webidl.converters.USVString(name)

@@ -80,3 +77,3 @@ // The delete(name) method steps are to remove all entries whose name

name = webidl.converters.USVString(name, prefix, 'name')
name = webidl.converters.USVString(name)

@@ -101,3 +98,3 @@ // 1. If there is no entry whose name is name in this’s entry list,

name = webidl.converters.USVString(name, prefix, 'name')
name = webidl.converters.USVString(name)

@@ -119,3 +116,3 @@ // 1. If there is no entry whose name is name in this’s entry list,

name = webidl.converters.USVString(name, prefix, 'name')
name = webidl.converters.USVString(name)

@@ -133,6 +130,12 @@ // The has(name) method steps are to return true if there is an entry

if (arguments.length === 3 && !isBlobLike(value)) {
throw new TypeError(
"Failed to execute 'set' on 'FormData': parameter 2 is not of type 'Blob'"
)
name = webidl.converters.USVString(name)
if (arguments.length === 3 || value instanceof Blob) {
value = webidl.converters.Blob(value, prefix, 'value')
if (filename !== undefined) {
filename = webidl.converters.USVString(filename)
}
} else {
value = webidl.converters.USVString(value)
}

@@ -145,10 +148,2 @@

name = webidl.converters.USVString(name, prefix, 'name')
value = isBlobLike(value)
? webidl.converters.Blob(value, prefix, 'name', { strict: false })
: webidl.converters.USVString(value, prefix, 'name')
filename = arguments.length === 3
? webidl.converters.USVString(filename, prefix, 'name')
: undefined
// 2. Let entry be the result of creating an entry with name, value, and

@@ -233,6 +228,4 @@ // filename if given.

// representing the same bytes, whose name attribute value is "blob"
if (!isFileLike(value)) {
value = value instanceof Blob
? new File([value], 'blob', { type: value.type })
: new FileLike(value, 'blob', { type: value.type })
if (!(value instanceof File)) {
value = new File([value], 'blob', { type: value.type })
}

@@ -249,5 +242,3 @@

value = value instanceof NativeFile
? new File([value], filename, options)
: new FileLike(value, filename, options)
value = new File([value], filename, options)
}

@@ -254,0 +245,0 @@ }

@@ -648,3 +648,3 @@ // https://github.com/Ethan-Arrowood/undici-fetch

webidl.converters.HeadersInit = function (V, prefix, argument) {
if (webidl.util.Type(V) === 'Object') {
if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {
const iterator = Reflect.get(V, Symbol.iterator)

@@ -651,0 +651,0 @@

@@ -31,3 +31,3 @@ /* globals AbortController */

const assert = require('node:assert')
const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = require('node:events')
const { getMaxListeners, setMaxListeners, defaultMaxListeners } = require('node:events')

@@ -85,3 +85,3 @@ const kAbortController = Symbol('abortController')

// https://fetch.spec.whatwg.org/#dom-request
constructor (input, init = {}) {
constructor (input, init = undefined) {
if (input === kConstruct) {

@@ -405,12 +405,2 @@ return

if (signal != null) {
if (
!signal ||
typeof signal.aborted !== 'boolean' ||
typeof signal.addEventListener !== 'function'
) {
throw new TypeError(
"Failed to construct 'Request': member signal is not of type AbortSignal."
)
}
if (signal.aborted) {

@@ -435,4 +425,2 @@ ac.abort(signal.reason)

setMaxListeners(1500, signal)
} else if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) {
setMaxListeners(1500, signal)
}

@@ -529,3 +517,3 @@ } catch {}

if (contentType && !getHeadersList(this[kHeaders]).contains('content-type', true)) {
this[kHeaders].append('content-type', contentType)
this[kHeaders].append('content-type', contentType, true)
}

@@ -936,17 +924,13 @@ }

webidl.converters.Request = webidl.interfaceConverter(
Request
)
// https://fetch.spec.whatwg.org/#requestinfo
webidl.converters.RequestInfo = function (V, prefix, argument) {
if (typeof V === 'string') {
return webidl.converters.USVString(V, prefix, argument)
return webidl.converters.USVString(V)
}
if (V instanceof Request) {
return webidl.converters.Request(V, prefix, argument)
return V
}
return webidl.converters.USVString(V, prefix, argument)
return webidl.converters.USVString(V)
}

@@ -1022,4 +1006,3 @@

'RequestInit',
'signal',
{ strict: false }
'signal'
)

@@ -1026,0 +1009,0 @@ )

@@ -12,3 +12,2 @@ 'use strict'

isAborted,
isBlobLike,
serializeJavascriptValueToJSONString,

@@ -46,3 +45,3 @@ isErrorLike,

// https://fetch.spec.whatwg.org/#dom-response-json
static json (data, init = {}) {
static json (data, init = undefined) {
webidl.argumentLengthCheck(arguments, 1, 'Response.json')

@@ -114,3 +113,3 @@

// https://fetch.spec.whatwg.org/#dom-response
constructor (body = null, init = {}) {
constructor (body = null, init = undefined) {
if (body === kConstruct) {

@@ -529,14 +528,2 @@ return

webidl.converters.ReadableStream = webidl.interfaceConverter(
ReadableStream
)
webidl.converters.FormData = webidl.interfaceConverter(
FormData
)
webidl.converters.URLSearchParams = webidl.interfaceConverter(
URLSearchParams
)
// https://fetch.spec.whatwg.org/#typedefdef-xmlhttprequestbodyinit

@@ -548,16 +535,16 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {

if (isBlobLike(V)) {
return webidl.converters.Blob(V, prefix, name, { strict: false })
if (V instanceof Blob) {
return V
}
if (ArrayBuffer.isView(V) || types.isArrayBuffer(V)) {
return webidl.converters.BufferSource(V, prefix, name)
return V
}
if (util.isFormDataLike(V)) {
return webidl.converters.FormData(V, prefix, name, { strict: false })
if (V instanceof FormData) {
return V
}
if (V instanceof URLSearchParams) {
return webidl.converters.URLSearchParams(V, prefix, name)
return V
}

@@ -571,3 +558,3 @@

if (V instanceof ReadableStream) {
return webidl.converters.ReadableStream(V, prefix, argument)
return V
}

@@ -574,0 +561,0 @@

'use strict'
module.exports = {
kUrl: Symbol('url'),
kHeaders: Symbol('headers'),

@@ -6,0 +5,0 @@ kSignal: Symbol('signal'),

@@ -9,3 +9,3 @@ 'use strict'

const { performance } = require('node:perf_hooks')
const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require('../../core/util')
const { ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require('../../core/util')
const assert = require('node:assert')

@@ -453,3 +453,3 @@ const { isUint8Array } = require('node:util/types')

}
case 'strict-origin': // eslint-disable-line
case 'strict-origin':
/**

@@ -477,3 +477,3 @@ * 1. If referrerURL is a potentially trustworthy URL and

* @param {URL} url
* @param {boolean|undefined} originOnly
* @param {boolean} [originOnly]
*/

@@ -1067,9 +1067,2 @@ function stripURLForReferrer (url, originOnly) {

function isReadableStreamLike (stream) {
return stream instanceof ReadableStream || (
stream[Symbol.toStringTag] === 'ReadableStream' &&
typeof stream.tee === 'function'
)
}
/**

@@ -1596,3 +1589,2 @@ * @param {ReadableStreamController<Uint8Array>} controller

responseLocationURL,
isBlobLike,
isURLPotentiallyTrustworthy,

@@ -1610,3 +1602,2 @@ isValidReasonPhrase,

bytesMatch,
isReadableStreamLike,
readableStreamClose,

@@ -1613,0 +1604,0 @@ isomorphicEncode,

@@ -6,2 +6,11 @@ 'use strict'

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
/** @type {import('../../../types/webidl').Webidl} */

@@ -37,18 +46,18 @@ const webidl = {}

// https://webidl.spec.whatwg.org/#implements
webidl.brandCheck = function (V, I, opts) {
if (opts?.strict !== false) {
if (!(V instanceof I)) {
const err = new TypeError('Illegal invocation')
err.code = 'ERR_INVALID_THIS' // node compat.
throw err
}
} else {
if (V?.[Symbol.toStringTag] !== I.prototype[Symbol.toStringTag]) {
const err = new TypeError('Illegal invocation')
err.code = 'ERR_INVALID_THIS' // node compat.
throw err
}
webidl.brandCheck = function (V, I) {
if (!(V instanceof I)) {
const err = new TypeError('Illegal invocation')
err.code = 'ERR_INVALID_THIS' // node compat.
throw err
}
}
webidl.brandCheckMultiple = function (V, List) {
if (List.every((clazz) => !(V instanceof clazz))) {
const err = new TypeError('Illegal invocation')
err.code = 'ERR_INVALID_THIS' // node compat.
throw err
}
}
webidl.argumentLengthCheck = function ({ length }, min, ctx) {

@@ -74,15 +83,15 @@ if (length < min) {

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 '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 NULL
}
return 'Object'
return OBJECT
}

@@ -92,2 +101,26 @@ }

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'
}
}
// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint

@@ -231,7 +264,7 @@ webidl.util.ConvertToInt = function (V, bitLength, signedness, opts) {

switch (type) {
case 'Symbol':
case SYMBOL:
return `Symbol(${V.description})`
case 'Object':
case OBJECT:
return inspect(V)
case 'String':
case STRING:
return `"${V}"`

@@ -247,3 +280,3 @@ default:

// 1. If Type(V) is not Object, throw a TypeError.
if (webidl.util.Type(V) !== 'Object') {
if (webidl.util.Type(V) !== OBJECT) {
throw webidl.errors.exception({

@@ -291,6 +324,6 @@ header: prefix,

// 1. If Type(O) is not Object, throw a TypeError.
if (webidl.util.Type(O) !== 'Object') {
if (webidl.util.Type(O) !== OBJECT) {
throw webidl.errors.exception({
header: prefix,
message: `${argument} ("${webidl.util.Type(O)}") is not an Object.`
message: `${argument} ("${webidl.util.TypeValueToString(O)}") is not an Object.`
})

@@ -350,4 +383,4 @@ }

webidl.interfaceConverter = function (i) {
return (V, prefix, argument, opts) => {
if (opts?.strict !== false && !(V instanceof i)) {
return (V, prefix, argument) => {
if (!(V instanceof i)) {
throw webidl.errors.exception({

@@ -365,8 +398,5 @@ header: prefix,

return (dictionary, prefix, argument) => {
const type = webidl.util.Type(dictionary)
const dict = {}
if (type === 'Null' || type === 'Undefined') {
return dict
} else if (type !== 'Object') {
if (dictionary != null && webidl.util.Type(dictionary) !== OBJECT) {
throw webidl.errors.exception({

@@ -382,3 +412,3 @@ header: prefix,

if (required === true) {
if (!Object.hasOwn(dictionary, key)) {
if (dictionary == null || !Object.hasOwn(dictionary, key)) {
throw webidl.errors.exception({

@@ -391,9 +421,9 @@ header: prefix,

let value = dictionary[key]
const hasDefault = Object.hasOwn(options, 'defaultValue')
let value = dictionary?.[key]
const hasDefault = defaultValue !== undefined
// Only use defaultValue if value is undefined and
// a defaultValue options was provided.
if (hasDefault && value !== null) {
value ??= defaultValue()
if (hasDefault && value === undefined) {
value = defaultValue()
}

@@ -549,3 +579,3 @@

if (
webidl.util.Type(V) !== 'Object' ||
webidl.util.Type(V) !== OBJECT ||
!types.isAnyArrayBuffer(V)

@@ -594,3 +624,3 @@ ) {

if (
webidl.util.Type(V) !== 'Object' ||
webidl.util.Type(V) !== OBJECT ||
!types.isTypedArray(V) ||

@@ -636,3 +666,3 @@ V.constructor.name !== T.name

// [[DataView]] internal slot, then throw a TypeError.
if (webidl.util.Type(V) !== 'Object' || !types.isDataView(V)) {
if (webidl.util.Type(V) !== OBJECT || !types.isDataView(V)) {
throw webidl.errors.exception({

@@ -671,23 +701,2 @@ header: prefix,

// https://webidl.spec.whatwg.org/#BufferSource
webidl.converters.BufferSource = function (V, prefix, name, opts) {
if (types.isAnyArrayBuffer(V)) {
return webidl.converters.ArrayBuffer(V, prefix, name, { ...opts, allowShared: false })
}
if (types.isTypedArray(V)) {
return webidl.converters.TypedArray(V, V.constructor, prefix, name, { ...opts, allowShared: false })
}
if (types.isDataView(V)) {
return webidl.converters.DataView(V, prefix, name, { ...opts, allowShared: false })
}
throw webidl.errors.conversionFailed({
prefix,
argument: `${name} ("${webidl.util.Stringify(V)}")`,
types: ['BufferSource']
})
}
webidl.converters['sequence<ByteString>'] = webidl.sequenceConverter(

@@ -706,4 +715,6 @@ webidl.converters.ByteString

webidl.converters.Blob = webidl.interfaceConverter(Blob)
module.exports = {
webidl
}
'use strict'
const { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = require('./constants')
const {
kReadyState,
kSentClose,
kByteParser,
kReceivedClose,
kResponse
} = require('./symbols')
const { fireEvent, failWebsocketConnection, isClosing, isClosed, isEstablished, parseExtensions } = require('./util')
const { uid } = require('./constants')
const { failWebsocketConnection, parseExtensions } = require('./util')
const { channels } = require('../../core/diagnostics')
const { CloseEvent } = require('./events')
const { makeRequest } = require('../fetch/request')

@@ -18,3 +10,2 @@ const { fetching } = require('../fetch/index')

const { getDecodeSplit } = require('../fetch/util')
const { WebsocketFrameSend } = require('./frame')

@@ -34,7 +25,6 @@ /** @type {import('crypto')} */

* @param {string|string[]} protocols
* @param {import('./websocket').WebSocket} ws
* @param {(response: any, extensions: string[] | undefined) => void} onEstablish
* @param {Partial<import('../../types/websocket').WebSocketInit>} options
* @param {import('./websocket').Handler} handler
* @param {Partial<import('../../../types/websocket').WebSocketInit>} options
*/
function establishWebSocketConnection (url, protocols, client, ws, onEstablish, 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

@@ -80,7 +70,7 @@ // scheme is "ws", and to "https" otherwise.

// header list.
request.headersList.append('sec-websocket-key', keyValue)
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')
request.headersList.append('sec-websocket-version', '13', true)

@@ -91,3 +81,3 @@ // 8. For each protocol in protocols, combine

for (const protocol of protocols) {
request.headersList.append('sec-websocket-protocol', protocol)
request.headersList.append('sec-websocket-protocol', protocol, true)
}

@@ -102,3 +92,3 @@

// request’s header list.
request.headersList.append('sec-websocket-extensions', permessageDeflate)
request.headersList.append('sec-websocket-extensions', permessageDeflate, true)

@@ -115,3 +105,3 @@ // 11. Fetch request with useParallelQueue set to true, and

if (response.type === 'error' || response.status !== 101) {
failWebsocketConnection(ws, 'Received network error or non-101 status code.')
failWebsocketConnection(handler, 'Received network error or non-101 status code.')
return

@@ -125,3 +115,3 @@ }

if (protocols.length !== 0 && !response.headersList.get('Sec-WebSocket-Protocol')) {
failWebsocketConnection(ws, 'Server did not respond with sent protocols.')
failWebsocketConnection(handler, 'Server did not respond with sent protocols.')
return

@@ -141,3 +131,3 @@ }

if (response.headersList.get('Upgrade')?.toLowerCase() !== 'websocket') {
failWebsocketConnection(ws, 'Server did not set Upgrade header to "websocket".')
failWebsocketConnection(handler, 'Server did not set Upgrade header to "websocket".')
return

@@ -151,3 +141,3 @@ }

if (response.headersList.get('Connection')?.toLowerCase() !== 'upgrade') {
failWebsocketConnection(ws, 'Server did not set Connection header to "upgrade".')
failWebsocketConnection(handler, 'Server did not set Connection header to "upgrade".')
return

@@ -166,3 +156,3 @@ }

if (secWSAccept !== digest) {
failWebsocketConnection(ws, 'Incorrect hash received in Sec-WebSocket-Accept header.')
failWebsocketConnection(handler, 'Incorrect hash received in Sec-WebSocket-Accept header.')
return

@@ -185,3 +175,3 @@ }

if (!extensions.has('permessage-deflate')) {
failWebsocketConnection(ws, 'Sec-WebSocket-Extensions header does not match.')
failWebsocketConnection(handler, 'Sec-WebSocket-Extensions header does not match.')
return

@@ -207,3 +197,3 @@ }

if (!requestProtocols.includes(secProtocol)) {
failWebsocketConnection(ws, 'Protocol was not set in the opening handshake.')
failWebsocketConnection(handler, 'Protocol was not set in the opening handshake.')
return

@@ -213,5 +203,5 @@ }

response.socket.on('data', onSocketData)
response.socket.on('close', onSocketClose)
response.socket.on('error', onSocketError)
response.socket.on('data', handler.onSocketData)
response.socket.on('close', handler.onSocketClose)
response.socket.on('error', handler.onSocketError)

@@ -226,3 +216,3 @@ if (channels.open.hasSubscribers) {

onEstablish(response, extensions)
handler.onConnectionEstablished(response, extensions)
}

@@ -234,150 +224,12 @@ })

function closeWebSocketConnection (ws, code, reason, reasonByteLength) {
if (isClosing(ws) || isClosed(ws)) {
// If this's ready state is CLOSING (2) or CLOSED (3)
// Do nothing.
} else if (!isEstablished(ws)) {
// If the WebSocket connection is not yet established
// Fail the WebSocket connection and set this's ready state
// to CLOSING (2).
failWebsocketConnection(ws, 'Connection was closed before it was established.')
ws[kReadyState] = states.CLOSING
} else if (ws[kSentClose] === sentCloseFrameState.NOT_SENT) {
// If the WebSocket closing handshake has not yet been started
// Start the WebSocket closing handshake and set this's ready
// state to CLOSING (2).
// - 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 reason is also present, then reasonBytes must be
// provided in the Close message after the status code.
ws[kSentClose] = sentCloseFrameState.PROCESSING
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 !== undefined && reason === undefined) {
frame.frameData = Buffer.allocUnsafe(2)
frame.frameData.writeUInt16BE(code, 0)
} else if (code !== undefined && reason !== undefined) {
// If reason is also present, then reasonBytes must be
// provided in the Close message after the status code.
frame.frameData = Buffer.allocUnsafe(2 + reasonByteLength)
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
}
/** @type {import('stream').Duplex} */
const socket = ws[kResponse].socket
socket.write(frame.createFrame(opcodes.CLOSE))
ws[kSentClose] = 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.
ws[kReadyState] = states.CLOSING
} else {
// Otherwise
// Set this's ready state to CLOSING (2).
ws[kReadyState] = states.CLOSING
}
}
/**
* @param {Buffer} chunk
* @param {import('./websocket').Handler} handler
* @param {number} code
* @param {any} reason
* @param {number} reasonByteLength
*/
function onSocketData (chunk) {
if (!this.ws[kByteParser].write(chunk)) {
this.pause()
}
function closeWebSocketConnection (handler, code, reason, reasonByteLength) {
handler.onClose(code, reason, reasonByteLength)
}
/**
* @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
* @see https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.4
*/
function onSocketClose () {
const { ws } = this
const { [kResponse]: response } = ws
response.socket.off('data', onSocketData)
response.socket.off('close', onSocketClose)
response.socket.off('error', onSocketError)
// 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 = ws[kSentClose] === sentCloseFrameState.SENT && ws[kReceivedClose]
let code = 1005
let reason = ''
const result = ws[kByteParser].closingInfo
if (result && !result.error) {
code = result.code ?? 1005
reason = result.reason
} else if (!ws[kReceivedClose]) {
// 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
}
// 1. Change the ready state to CLOSED (3).
ws[kReadyState] = 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.
// TODO
// 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', ws, (type, init) => new CloseEvent(type, init), {
wasClean, code, reason
})
if (channels.close.hasSubscribers) {
channels.close.publish({
websocket: ws,
code,
reason
})
}
}
function onSocketError (error) {
const { ws } = this
ws[kReadyState] = states.CLOSING
if (channels.socketError.hasSubscribers) {
channels.socketError.publish(error)
}
this.destroy()
}
module.exports = {

@@ -384,0 +236,0 @@ establishWebSocketConnection,

@@ -50,3 +50,3 @@ 'use strict'

const sendHints = {
string: 1,
text: 1,
typedArray: 2,

@@ -53,0 +53,0 @@ arrayBuffer: 3,

'use strict'
const { maxUnsigned16Bit } = require('./constants')
const { maxUnsigned16Bit, opcodes } = require('./constants')
const BUFFER_SIZE = 16386
const BUFFER_SIZE = 8 * 1024

@@ -30,3 +30,3 @@ /** @type {import('crypto')} */

bufIdx = 0
crypto.randomFillSync((buffer ??= Buffer.allocUnsafe(BUFFER_SIZE)), 0, BUFFER_SIZE)
crypto.randomFillSync((buffer ??= Buffer.allocUnsafeSlow(BUFFER_SIZE)), 0, BUFFER_SIZE)
}

@@ -93,2 +93,44 @@ return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]]

}
/**
* @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]
}
}

@@ -95,0 +137,0 @@

@@ -6,3 +6,2 @@ 'use strict'

const { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = require('./constants')
const { kReadyState, kSentClose, kResponse, kReceivedClose } = require('./symbols')
const { channels } = require('../../core/diagnostics')

@@ -41,6 +40,9 @@ const {

constructor (ws, extensions) {
/** @type {import('./websocket').Handler} */
#handler
constructor (handler, extensions) {
super()
this.ws = ws
this.#handler = handler
this.#extensions = extensions == null ? new Map() : extensions

@@ -91,3 +93,3 @@

if (!isValidOpcode(opcode)) {
failWebsocketConnection(this.ws, 'Invalid opcode received')
failWebsocketConnection(this.#handler, 'Invalid opcode received')
return callback()

@@ -97,3 +99,3 @@ }

if (masked) {
failWebsocketConnection(this.ws, 'Frame cannot be masked')
failWebsocketConnection(this.#handler, 'Frame cannot be masked')
return callback()

@@ -112,3 +114,3 @@ }

if (rsv1 !== 0 && !this.#extensions.has('permessage-deflate')) {
failWebsocketConnection(this.ws, 'Expected RSV1 to be clear.')
failWebsocketConnection(this.#handler, 'Expected RSV1 to be clear.')
return

@@ -118,3 +120,3 @@ }

if (rsv2 !== 0 || rsv3 !== 0) {
failWebsocketConnection(this.ws, 'RSV1, RSV2, RSV3 must be clear')
failWebsocketConnection(this.#handler, 'RSV1, RSV2, RSV3 must be clear')
return

@@ -125,3 +127,3 @@ }

// Only text and binary frames can be fragmented
failWebsocketConnection(this.ws, 'Invalid frame type was fragmented.')
failWebsocketConnection(this.#handler, 'Invalid frame type was fragmented.')
return

@@ -133,3 +135,3 @@ }

if (isTextBinaryFrame(opcode) && this.#fragments.length > 0) {
failWebsocketConnection(this.ws, 'Expected continuation frame')
failWebsocketConnection(this.#handler, 'Expected continuation frame')
return

@@ -140,3 +142,3 @@ }

// A fragmented frame can't be fragmented itself
failWebsocketConnection(this.ws, 'Fragmented frame exceeded 125 bytes.')
failWebsocketConnection(this.#handler, 'Fragmented frame exceeded 125 bytes.')
return

@@ -148,3 +150,3 @@ }

if ((payloadLength > 125 || fragmented) && isControlFrame(opcode)) {
failWebsocketConnection(this.ws, 'Control frame either too large or fragmented')
failWebsocketConnection(this.#handler, 'Control frame either too large or fragmented')
return

@@ -154,3 +156,3 @@ }

if (isContinuationFrame(opcode) && this.#fragments.length === 0 && !this.#info.compressed) {
failWebsocketConnection(this.ws, 'Unexpected continuation frame')
failWebsocketConnection(this.#handler, 'Unexpected continuation frame')
return

@@ -201,3 +203,3 @@ }

if (upper > 2 ** 31 - 1) {
failWebsocketConnection(this.ws, 'Received payload length > 2^31 bytes.')
failWebsocketConnection(this.#handler, 'Received payload length > 2^31 bytes.')
return

@@ -230,3 +232,3 @@ }

const fullMessage = Buffer.concat(this.#fragments)
websocketMessageReceived(this.ws, this.#info.binaryType, fullMessage)
websocketMessageReceived(this.#handler, this.#info.binaryType, fullMessage)
this.#fragments.length = 0

@@ -239,3 +241,3 @@ }

if (error) {
closeWebSocketConnection(this.ws, 1007, error.message, error.message.length)
closeWebSocketConnection(this.#handler, 1007, error.message, error.message.length)
return

@@ -253,3 +255,3 @@ }

websocketMessageReceived(this.ws, this.#info.binaryType, Buffer.concat(this.#fragments))
websocketMessageReceived(this.#handler, this.#info.binaryType, Buffer.concat(this.#fragments))

@@ -357,3 +359,3 @@ this.#loop = true

if (payloadLength === 1) {
failWebsocketConnection(this.ws, 'Received close frame with a 1-byte body.')
failWebsocketConnection(this.#handler, 'Received close frame with a 1-byte body.')
return false

@@ -367,8 +369,8 @@ }

closeWebSocketConnection(this.ws, code, reason, reason.length)
failWebsocketConnection(this.ws, reason)
closeWebSocketConnection(this.#handler, code, reason, reason.length)
failWebsocketConnection(this.#handler, reason)
return false
}
if (this.ws[kSentClose] !== sentCloseFrameState.SENT) {
if (this.#handler.closeState !== sentCloseFrameState.SENT) {
// If an endpoint receives a Close frame and did not previously send a

@@ -385,7 +387,7 @@ // Close frame, the endpoint MUST send a Close frame in response. (When

this.ws[kResponse].socket.write(
this.#handler.socket.write(
closeFrame.createFrame(opcodes.CLOSE),
(err) => {
if (!err) {
this.ws[kSentClose] = sentCloseFrameState.SENT
this.#handler.closeState = sentCloseFrameState.SENT
}

@@ -399,4 +401,4 @@ }

// WebSocket connection is in the CLOSING state.
this.ws[kReadyState] = states.CLOSING
this.ws[kReceivedClose] = true
this.#handler.readyState = states.CLOSING
this.#handler.receivedClose = true

@@ -410,6 +412,6 @@ return false

if (!this.ws[kReceivedClose]) {
if (!this.#handler.receivedClose) {
const frame = new WebsocketFrameSend(body)
this.ws[kResponse].socket.write(frame.createFrame(opcodes.PONG))
this.#handler.socket.write(frame.createFrame(opcodes.PONG))

@@ -416,0 +418,0 @@ if (channels.ping.hasSubscribers) {

@@ -7,5 +7,2 @@ 'use strict'

/** @type {typeof Uint8Array} */
const FastBuffer = Buffer[Symbol.species]
/**

@@ -38,6 +35,15 @@ * @typedef {object} SendQueueNode

if (hint !== sendHints.blob) {
const frame = createFrame(item, hint)
if (!this.#running) {
// fast-path
this.#socket.write(frame, cb)
// 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 {

@@ -48,3 +54,3 @@ /** @type {SendQueueNode} */

callback: cb,
frame
frame: createFrame(item, hint)
}

@@ -92,3 +98,3 @@ this.#queue.push(node)

function createFrame (data, hint) {
return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY)
return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.text ? opcodes.TEXT : opcodes.BINARY)
}

@@ -98,9 +104,8 @@

switch (hint) {
case sendHints.string:
return Buffer.from(data)
case sendHints.text:
case sendHints.typedArray:
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
case sendHints.arrayBuffer:
case sendHints.blob:
return new FastBuffer(data)
case sendHints.typedArray:
return new FastBuffer(data.buffer, data.byteOffset, data.byteLength)
return new Uint8Array(data)
}

@@ -107,0 +112,0 @@ }

'use strict'
const { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = require('./symbols')
const { states, opcodes } = require('./constants')
const { ErrorEvent, createFastMessageEvent } = require('./events')
const { isUtf8 } = require('node:buffer')
const { collectASequenceOfCodePointsFast, removeHTTPWhitespace } = require('../fetch/data-url')
/* globals Blob */
/**
* @param {import('./websocket').WebSocket} ws
* @param {number} readyState
* @returns {boolean}
*/
function isConnecting (ws) {
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 ws[kReadyState] === states.CONNECTING
return readyState === states.CONNECTING
}
/**
* @param {import('./websocket').WebSocket} ws
* @param {number} readyState
* @returns {boolean}
*/
function isEstablished (ws) {
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 ws[kReadyState] === states.OPEN
return readyState === states.OPEN
}
/**
* @param {import('./websocket').WebSocket} ws
* @param {number} readyState
* @returns {boolean}
*/
function isClosing (ws) {
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 ws[kReadyState] === states.CLOSING
return readyState === states.CLOSING
}
/**
* @param {import('./websocket').WebSocket} ws
* @param {number} readyState
* @returns {boolean}
*/
function isClosed (ws) {
return ws[kReadyState] === states.CLOSED
function isClosed (readyState) {
return readyState === states.CLOSED
}

@@ -76,45 +72,8 @@

* @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
* @param {import('./websocket').WebSocket} ws
* @param {import('./websocket').Handler} handler
* @param {number} type Opcode
* @param {Buffer} data application data
*/
function websocketMessageReceived (ws, type, data) {
// 1. If ready state is not OPEN (1), then return.
if (ws[kReadyState] !== 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(ws, 'Received invalid UTF-8 in text frame.')
return
}
} else if (type === opcodes.BINARY) {
if (ws[kBinaryType] === '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', ws, createFastMessageEvent, {
origin: ws[kWebSocketURL].origin,
data: dataForEvent
})
function websocketMessageReceived (handler, type, data) {
handler.onMessage(type, data)
}

@@ -194,21 +153,7 @@

/**
* @param {import('./websocket').WebSocket} ws
* @param {import('./websocket').Handler} handler
* @param {string|undefined} reason
*/
function failWebsocketConnection (ws, reason) {
const { [kController]: controller, [kResponse]: response } = ws
controller.abort()
if (response?.socket && !response.socket.destroyed) {
response.socket.destroy()
}
if (reason) {
// TODO: process.nextTick
fireEvent('error', ws, (type, init) => new ErrorEvent(type, init), {
error: new Error(reason),
message: reason
})
}
function failWebsocketConnection (handler, reason) {
handler.onFail(reason)
}

@@ -315,3 +260,4 @@

parseExtensions,
isValidClientWindowBits
isValidClientWindowBits,
toArrayBuffer
}

@@ -6,13 +6,4 @@ 'use strict'

const { environmentSettingsObject } = require('../fetch/util')
const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints } = require('./constants')
const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints, opcodes, emptyBuffer } = require('./constants')
const {
kWebSocketURL,
kReadyState,
kController,
kBinaryType,
kResponse,
kSentClose,
kByteParser
} = require('./symbols')
const {
isConnecting,

@@ -22,12 +13,36 @@ isEstablished,

isValidSubprotocol,
fireEvent
fireEvent,
failWebsocketConnection,
utf8Decode,
toArrayBuffer,
isClosed
} = require('./util')
const { establishWebSocketConnection, closeWebSocketConnection } = require('./connection')
const { ByteParser } = require('./receiver')
const { kEnumerableProperty, isBlobLike } = require('../../core/util')
const { kEnumerableProperty } = require('../../core/util')
const { getGlobalDispatcher } = require('../../global')
const { types } = require('node:util')
const { ErrorEvent, CloseEvent } = require('./events')
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 {(reason: any) => void} onFail
* @property {(opcode: number, data: Buffer) => void} onMessage
* @property {(code: number, reason: any, reasonByteLength: number) => void} onClose
* @property {(error: Error) => void} onParserError
* @property {() => void} onParserDrain
* @property {(chunk: Buffer) => void} onSocketData
* @property {(err: Error) => void} onSocketError
* @property {() => void} onSocketClose
*
* @property {number} readyState
* @property {import('stream').Duplex} socket
* @property {number} closeState
* @property {boolean} receivedClose
*/
// https://websockets.spec.whatwg.org/#interface-definition

@@ -49,2 +64,38 @@ class WebSocket extends EventTarget {

/** @type {Handler} */
#handler = {
onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions),
onFail: (reason) => this.#onFail(reason),
onMessage: (opcode, data) => this.#onMessage(opcode, data),
onClose: (code, reason, reasonByteLength) => this.#onClose(code, reason, reasonByteLength),
onParserError: (err) => this.#onParserError(err),
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(),
readyState: states.CONNECTING,
socket: null,
closeState: sentCloseFrameState.NOT_SENT,
receivedClose: false
}
#url
#controller
#binaryType
/** @type {import('./receiver').ByteParser} */
#parser
/**

@@ -62,3 +113,3 @@ * @param {string} url

url = webidl.converters.USVString(url, prefix, 'url')
url = webidl.converters.USVString(url)
protocols = options.protocols

@@ -120,3 +171,3 @@

// 10. Set this's url to urlRecord.
this[kWebSocketURL] = new URL(urlRecord.href)
this.#url = new URL(urlRecord.href)

@@ -130,8 +181,7 @@ // 11. Let client be this's relevant settings object.

// and client.
this[kController] = establishWebSocketConnection(
this.#controller = establishWebSocketConnection(
urlRecord,
protocols,
client,
this,
(response, extensions) => this.#onConnectionEstablished(response, extensions),
this.#handler,
options

@@ -143,5 +193,5 @@ )

// be CONNECTING (0).
this[kReadyState] = WebSocket.CONNECTING
this.#handler.readyState = WebSocket.CONNECTING
this[kSentClose] = sentCloseFrameState.NOT_SENT
this.#handler.closeState = sentCloseFrameState.NOT_SENT

@@ -154,3 +204,3 @@ // The extensions attribute must initially return the empty string.

// BinaryType. Initially it must be "blob".
this[kBinaryType] = 'blob'
this.#binaryType = 'blob'
}

@@ -173,3 +223,3 @@

if (reason !== undefined) {
reason = webidl.converters.USVString(reason, prefix, 'reason')
reason = webidl.converters.USVString(reason)
}

@@ -204,3 +254,3 @@

// 3. Run the first matching steps from the following list:
closeWebSocketConnection(this, code, reason, reasonByteLength)
closeWebSocketConnection(this.#handler, code, reason, reasonByteLength)
}

@@ -222,3 +272,3 @@

// "InvalidStateError" DOMException.
if (isConnecting(this)) {
if (isConnecting(this.#handler.readyState)) {
throw new DOMException('Sent before connected.', 'InvalidStateError')

@@ -231,3 +281,3 @@ }

if (!isEstablished(this) || isClosing(this)) {
if (!isEstablished(this.#handler.readyState) || isClosing(this.#handler.readyState)) {
return

@@ -249,8 +299,8 @@ }

const length = Buffer.byteLength(data)
const buffer = Buffer.from(data)
this.#bufferedAmount += length
this.#sendQueue.add(data, () => {
this.#bufferedAmount -= length
}, sendHints.string)
this.#bufferedAmount += buffer.byteLength
this.#sendQueue.add(buffer, () => {
this.#bufferedAmount -= buffer.byteLength
}, sendHints.text)
} else if (types.isArrayBuffer(data)) {

@@ -290,3 +340,3 @@ // If the WebSocket connection is established, and the WebSocket

}, sendHints.typedArray)
} else if (isBlobLike(data)) {
} else if (data instanceof Blob) {
// If the WebSocket connection is established, and the WebSocket

@@ -314,3 +364,3 @@ // closing handshake has not yet started, then the user agent must

// The readyState getter steps are to return this's ready state.
return this[kReadyState]
return this.#handler.readyState
}

@@ -328,3 +378,3 @@

// The url getter steps are to return this's url, serialized.
return URLSerializer(this[kWebSocketURL])
return URLSerializer(this.#url)
}

@@ -431,3 +481,3 @@

return this[kBinaryType]
return this.#binaryType
}

@@ -439,5 +489,5 @@

if (type !== 'blob' && type !== 'arraybuffer') {
this[kBinaryType] = 'blob'
this.#binaryType = 'blob'
} else {
this[kBinaryType] = type
this.#binaryType = type
}

@@ -452,15 +502,13 @@ }

// once this happens, the connection is open
this[kResponse] = response
this.#handler.socket = response.socket
const parser = new ByteParser(this, parsedExtensions)
parser.on('drain', onParserDrain)
parser.on('error', onParserError.bind(this))
const parser = new ByteParser(this.#handler, parsedExtensions)
parser.on('drain', () => this.#handler.onParserDrain())
parser.on('error', (err) => this.#handler.onParserError(err))
response.socket.ws = this
this[kByteParser] = parser
this.#parser = parser
this.#sendQueue = new SendQueue(response.socket)
// 1. Change the ready state to OPEN (1).
this[kReadyState] = states.OPEN
this.#handler.readyState = states.OPEN

@@ -488,2 +536,198 @@ // 2. Change the extensions attribute’s value to the extensions in use, if

}
#onFail (reason) {
this.#controller.abort()
if (this.#handler.socket && !this.#handler.socket.destroyed) {
this.#handler.socket.destroy()
}
this.#handler.readyState = states.CLOSED
if (reason) {
// TODO: process.nextTick
fireEvent('error', this, (type, init) => new ErrorEvent(type, init), {
error: new Error(reason),
message: reason
})
}
}
#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, '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
})
}
#onClose (code, reason, reasonByteLength) {
if (isClosing(this.#handler.readyState) || isClosed(this.#handler.readyState)) {
// If this's ready state is CLOSING (2) or CLOSED (3)
// Do nothing.
} else if (!isEstablished(this.#handler.readyState)) {
// If the WebSocket connection is not yet established
// Fail the WebSocket connection and set this's ready state
// to CLOSING (2).
failWebsocketConnection(this.#handler, 'Connection was closed before it was established.')
this.#handler.readyState = states.CLOSING
} else if (this.#handler.closeState === sentCloseFrameState.NOT_SENT) {
// If the WebSocket closing handshake has not yet been started
// Start the WebSocket closing handshake and set this's ready
// state to CLOSING (2).
// - 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 reason is also present, then reasonBytes must be
// provided in the Close message after the status code.
this.#handler.closeState = sentCloseFrameState.PROCESSING
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 !== undefined && reason === undefined) {
frame.frameData = Buffer.allocUnsafe(2)
frame.frameData.writeUInt16BE(code, 0)
} else if (code !== undefined && reason !== undefined) {
// If reason is also present, then reasonBytes must be
// provided in the Close message after the status code.
frame.frameData = Buffer.allocUnsafe(2 + reasonByteLength)
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
}
this.#handler.socket.write(frame.createFrame(opcodes.CLOSE))
this.#handler.closeState = 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
} else {
// Otherwise
// Set this's ready state to CLOSING (2).
this.#handler.readyState = states.CLOSING
}
}
#onParserError (err) {
let message
let code
if (err instanceof CloseEvent) {
message = err.reason
code = err.code
} else {
message = err.message
}
fireEvent('error', this, () => new ErrorEvent('error', { error: err, message }))
closeWebSocketConnection(this.#handler, code)
}
#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 === sentCloseFrameState.SENT && this.#handler.receivedClose
let code = 1005
let reason = ''
const result = this.#parser.closingInfo
if (result && !result.error) {
code = result.code ?? 1005
reason = result.reason
} else if (!this.#handler.receivedClose) {
// 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
}
// 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.
// TODO
// 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
})
}
}
}

@@ -537,3 +781,3 @@

webidl.converters['DOMString or sequence<DOMString>'] = function (V, prefix, argument) {
if (webidl.util.Type(V) === 'Object' && Symbol.iterator in V) {
if (webidl.util.Type(V) === webidl.util.Types.OBJECT && Symbol.iterator in V) {
return webidl.converters['sequence<DOMString>'](V)

@@ -564,3 +808,3 @@ }

webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'] = function (V) {
if (webidl.util.Type(V) === 'Object' && !(Symbol.iterator in V)) {
if (webidl.util.Type(V) === webidl.util.Types.OBJECT && !(Symbol.iterator in V)) {
return webidl.converters.WebSocketInit(V)

@@ -573,9 +817,9 @@ }

webidl.converters.WebSocketSendData = function (V) {
if (webidl.util.Type(V) === 'Object') {
if (isBlobLike(V)) {
return webidl.converters.Blob(V, { strict: false })
if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {
if (V instanceof Blob) {
return V
}
if (ArrayBuffer.isView(V) || types.isArrayBuffer(V)) {
return webidl.converters.BufferSource(V)
return V
}

@@ -587,24 +831,4 @@ }

function onParserDrain () {
this.ws[kResponse].socket.resume()
}
function onParserError (err) {
let message
let code
if (err instanceof CloseEvent) {
message = err.reason
code = err.code
} else {
message = err.message
}
fireEvent('error', this, () => new ErrorEvent('error', { error: err, message }))
closeWebSocketConnection(this, code)
}
module.exports = {
WebSocket
}
{
"name": "undici",
"version": "6.19.8",
"version": "7.0.0-alpha.1",
"description": "An HTTP/1.1 client, written from scratch for Node.js",

@@ -68,15 +68,15 @@ "homepage": "https://undici.nodejs.org",

"build:wasm": "node build/wasm.js --docker",
"lint": "standard | snazzy",
"lint:fix": "standard --fix | snazzy",
"generate-pem": "node scripts/generate-pem.js",
"lint": "eslint --cache",
"lint:fix": "eslint --fix --cache",
"test": "npm run test:javascript && cross-env NODE_V8_COVERAGE= npm run test:typescript",
"test:javascript": "node scripts/generate-pem && npm run test:unit && npm run test:node-fetch && npm run test:cache && npm run test:interceptors && npm run test:fetch && npm run test:cookies && npm run test:eventsource && npm run test:wpt && npm run test:websocket && npm run test:node-test && npm run test:jest",
"test:javascript:withoutintl": "node scripts/generate-pem && npm run test:unit && npm run test:node-fetch && npm run test:fetch:nobuild && npm run test:cache && npm run test:interceptors && npm run test:cookies && npm run test:eventsource:nobuild && npm run test:wpt:withoutintl && npm run test:node-test",
"test:javascript": "npm run test:javascript:no-jest && npm run test:jest",
"test:javascript:no-jest": "npm run generate-pem && npm run test:unit && npm run test:node-fetch && npm run test:cache && npm run test:interceptors && npm run test:fetch && npm run test:cookies && npm run test:eventsource && npm run test:wpt && npm run test:websocket && npm run test:node-test",
"test:javascript:without-intl": "npm run test:javascript:no-jest",
"test:busboy": "borp -p \"test/busboy/*.js\"",
"test:cache": "borp -p \"test/cache/*.js\"",
"test:cookies": "borp -p \"test/cookie/*.js\"",
"test:eventsource": "npm run build:node && npm run test:eventsource:nobuild",
"test:eventsource:nobuild": "borp --expose-gc -p \"test/eventsource/*.js\"",
"test:eventsource": "npm run build:node && borp --expose-gc -p \"test/eventsource/*.js\"",
"test:fuzzing": "node test/fuzzing/fuzzing.test.js",
"test:fetch": "npm run build:node && npm run test:fetch:nobuild",
"test:fetch:nobuild": "borp --timeout 180000 --expose-gc --concurrency 1 -p \"test/fetch/*.js\" && npm run test:webidl && npm run test:busboy",
"test:fetch": "npm run build:node && borp --timeout 180000 --expose-gc --concurrency 1 -p \"test/fetch/*.js\" && npm run test:webidl && npm run test:busboy",
"test:interceptors": "borp -p \"test/interceptors/*.js\"",

@@ -89,3 +89,3 @@ "test:jest": "cross-env NODE_V8_COVERAGE= jest",

"test:tdd:node-test": "borp -p \"test/node-test/**/*.js\" -w",
"test:typescript": "tsd && tsc test/imports/undici-import.ts --typeRoots ./types && tsc ./types/*.d.ts --noEmit --typeRoots ./types",
"test:typescript": "tsd && tsc test/imports/undici-import.ts --typeRoots ./types --noEmit && tsc ./types/*.d.ts --noEmit --typeRoots ./types",
"test:webidl": "borp -p \"test/webidl/*.js\"",

@@ -95,3 +95,3 @@ "test:websocket": "borp -p \"test/websocket/*.js\"",

"test:websocket:autobahn:report": "node test/autobahn/report.js",
"test:wpt": "node test/wpt/start-fetch.mjs && node test/wpt/start-FileAPI.mjs && node test/wpt/start-mimesniff.mjs && node test/wpt/start-xhr.mjs && node test/wpt/start-websockets.mjs && node test/wpt/start-cacheStorage.mjs && node test/wpt/start-eventsource.mjs",
"test:wpt": "node test/wpt/start-fetch.mjs && node test/wpt/start-mimesniff.mjs && node test/wpt/start-xhr.mjs && node test/wpt/start-websockets.mjs && node test/wpt/start-cacheStorage.mjs && node test/wpt/start-eventsource.mjs",
"test:wpt:withoutintl": "node test/wpt/start-fetch.mjs && node test/wpt/start-mimesniff.mjs && node test/wpt/start-xhr.mjs && node test/wpt/start-cacheStorage.mjs && node test/wpt/start-eventsource.mjs",

@@ -108,23 +108,20 @@ "coverage": "npm run coverage:clean && cross-env NODE_V8_COVERAGE=./coverage/tmp npm run test:javascript && npm run coverage:report",

"devDependencies": {
"@fastify/busboy": "2.1.1",
"@fastify/busboy": "3.0.0",
"@matteo.collina/tspl": "^0.1.1",
"@sinonjs/fake-timers": "^11.1.0",
"@types/node": "^18.0.3",
"@types/node": "~18.17.19",
"abort-controller": "^3.0.0",
"borp": "^0.15.0",
"borp": "^0.17.0",
"c8": "^10.0.0",
"cross-env": "^7.0.3",
"dns-packet": "^5.4.0",
"eslint": "^9.9.0",
"fast-check": "^3.17.1",
"form-data": "^4.0.0",
"formdata-node": "^6.0.3",
"https-pem": "^3.0.0",
"husky": "^9.0.7",
"jest": "^29.0.2",
"jsdom": "^24.0.0",
"neostandard": "^0.11.2",
"node-forge": "^1.3.1",
"pre-commit": "^1.2.2",
"proxy": "^2.1.1",
"snazzy": "^9.0.0",
"standard": "^17.0.0",
"tsd": "^0.31.0",

@@ -137,12 +134,2 @@ "typescript": "^5.0.2",

},
"standard": {
"env": [
"jest"
],
"ignore": [
"lib/llhttp/constants.js",
"lib/llhttp/utils.js",
"test/fixtures/wpt"
]
},
"tsd": {

@@ -149,0 +136,0 @@ "directory": "test/types",

# undici
[![Node CI](https://github.com/nodejs/undici/actions/workflows/nodejs.yml/badge.svg)](https://github.com/nodejs/undici/actions/workflows/nodejs.yml) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) [![npm version](https://badge.fury.io/js/undici.svg)](https://badge.fury.io/js/undici) [![codecov](https://codecov.io/gh/nodejs/undici/branch/main/graph/badge.svg?token=yZL6LtXkOA)](https://codecov.io/gh/nodejs/undici)
[![Node CI](https://github.com/nodejs/undici/actions/workflows/nodejs.yml/badge.svg)](https://github.com/nodejs/undici/actions/workflows/nodejs.yml) [![neostandard javascript style](https://img.shields.io/badge/neo-standard-7fffff?style=flat\&labelColor=ff80ff)](https://github.com/neostandard/neostandard) [![npm version](https://badge.fury.io/js/undici.svg)](https://badge.fury.io/js/undici) [![codecov](https://codecov.io/gh/nodejs/undici/branch/main/graph/badge.svg?token=yZL6LtXkOA)](https://codecov.io/gh/nodejs/undici)

@@ -87,2 +87,3 @@ An HTTP/1.1 client, written from scratch for Node.js.

- [`.blob()`](https://fetch.spec.whatwg.org/#dom-body-blob)
- [`.bytes()`](https://fetch.spec.whatwg.org/#dom-body-bytes)
- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)

@@ -130,3 +131,2 @@ - [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)

* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
* **maxRedirections** `Integer` - Default: `0`

@@ -147,3 +147,2 @@ Returns a promise with the result of the `Dispatcher.request` method.

* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
* **maxRedirections** `Integer` - Default: `0`
* **factory** `Dispatcher.stream.factory`

@@ -165,3 +164,2 @@

* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
* **maxRedirections** `Integer` - Default: `0`
* **handler** `Dispatcher.pipeline.handler`

@@ -184,3 +182,2 @@

* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
* **maxRedirections** `Integer` - Default: `0`
* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)

@@ -270,9 +267,9 @@

- half
- `'half'`
In this implementation of fetch, `request.duplex` must be set if `request.body` is `ReadableStream` or `Async Iterables`, however, fetch requests are currently always full duplex. For more detail refer to the [Fetch Standard.](https://fetch.spec.whatwg.org/#dom-requestinit-duplex).
In this implementation of fetch, `request.duplex` must be set if `request.body` is `ReadableStream` or `Async Iterables`, however, even though the value must be set to `'half'`, it is actually a _full_ duplex. For more detail refer to the [Fetch Standard.](https://fetch.spec.whatwg.org/#dom-requestinit-duplex).
#### `response.body`
Nodejs has two kinds of streams: [web streams](https://nodejs.org/dist/latest-v16.x/docs/api/webstreams.html), which follow the API of the WHATWG web standard found in browsers, and an older Node-specific [streams API](https://nodejs.org/api/stream.html). `response.body` returns a readable web stream. If you would prefer to work with a Node stream you can convert a web stream using `.fromWeb()`.
Nodejs has two kinds of streams: [web streams](https://nodejs.org/api/webstreams.html), which follow the API of the WHATWG web standard found in browsers, and an older Node-specific [streams API](https://nodejs.org/api/stream.html). `response.body` returns a readable web stream. If you would prefer to work with a Node stream you can convert a web stream using `.fromWeb()`.

@@ -346,3 +343,2 @@ ```js

* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
* **maxRedirections** `Integer` - Default: `0`
* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)

@@ -349,0 +345,0 @@

@@ -6,4 +6,6 @@ 'use strict'

const buffer = transcode(readFileSync('./undici-fetch.js'), 'utf8', 'latin1')
const buffer = transcode
? transcode(readFileSync('./undici-fetch.js'), 'utf8', 'latin1')
: readFileSync('./undici-fetch.js')
writeFileSync('./undici-fetch.js', buffer.toString('latin1'))
import { URL } from 'url'
import Pool from './pool'
import Dispatcher from "./dispatcher";
import Dispatcher from './dispatcher'
export default Agent
declare class Agent extends Dispatcher{
constructor(opts?: Agent.Options)
declare class Agent extends Dispatcher {
constructor (opts?: Agent.Options)
/** `true` after `dispatcher.close()` has been called. */
closed: boolean;
closed: boolean
/** `true` after `dispatcher.destroyed()` has been called or `dispatcher.close()` has been called and the dispatcher shutdown has completed. */
destroyed: boolean;
destroyed: boolean
/** Dispatches a request. */
dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean;
dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean
}

@@ -24,3 +24,3 @@

interceptors?: { Agent?: readonly Dispatcher.DispatchInterceptor[] } & Pool.Options["interceptors"]
interceptors?: { Agent?: readonly Dispatcher.DispatchInterceptor[] } & Pool.Options['interceptors']
}

@@ -27,0 +27,0 @@

@@ -5,40 +5,40 @@ import { URL, UrlObject } from 'url'

export {
request,
stream,
pipeline,
connect,
upgrade,
}
/** Performs an HTTP request. */
declare function request(
declare function request<TOpaque = null> (
url: string | URL | UrlObject,
options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions, 'origin' | 'path' | 'method'> & Partial<Pick<Dispatcher.RequestOptions, 'method'>>,
): Promise<Dispatcher.ResponseData>;
options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions<TOpaque>, 'origin' | 'path' | 'method'> & Partial<Pick<Dispatcher.RequestOptions, 'method'>>,
): Promise<Dispatcher.ResponseData<TOpaque>>
/** A faster version of `request`. */
declare function stream(
declare function stream<TOpaque = null> (
url: string | URL | UrlObject,
options: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions, 'origin' | 'path'>,
factory: Dispatcher.StreamFactory
): Promise<Dispatcher.StreamData>;
options: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions<TOpaque>, 'origin' | 'path'>,
factory: Dispatcher.StreamFactory<TOpaque>
): Promise<Dispatcher.StreamData<TOpaque>>
/** For easy use with `stream.pipeline`. */
declare function pipeline(
declare function pipeline<TOpaque = null> (
url: string | URL | UrlObject,
options: { dispatcher?: Dispatcher } & Omit<Dispatcher.PipelineOptions, 'origin' | 'path'>,
handler: Dispatcher.PipelineHandler
): Duplex;
options: { dispatcher?: Dispatcher } & Omit<Dispatcher.PipelineOptions<TOpaque>, 'origin' | 'path'>,
handler: Dispatcher.PipelineHandler<TOpaque>
): Duplex
/** Starts two-way communications with the requested resource. */
declare function connect(
declare function connect<TOpaque = null> (
url: string | URL | UrlObject,
options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.ConnectOptions, 'origin' | 'path'>
): Promise<Dispatcher.ConnectData>;
options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.ConnectOptions<TOpaque>, 'origin' | 'path'>
): Promise<Dispatcher.ConnectData<TOpaque>>
/** Upgrade to a different protocol. */
declare function upgrade(
declare function upgrade (
url: string | URL | UrlObject,
options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.UpgradeOptions, 'origin' | 'path'>
): Promise<Dispatcher.UpgradeData>;
): Promise<Dispatcher.UpgradeData>
export {
request,
stream,
pipeline,
connect,
upgrade
}

@@ -7,24 +7,24 @@ import Pool from './pool'

type BalancedPoolConnectOptions = Omit<Dispatcher.ConnectOptions, "origin">;
type BalancedPoolConnectOptions = Omit<Dispatcher.ConnectOptions, 'origin'>
declare class BalancedPool extends Dispatcher {
constructor(url: string | string[] | URL | URL[], options?: Pool.Options);
constructor (url: string | string[] | URL | URL[], options?: Pool.Options)
addUpstream(upstream: string | URL): BalancedPool;
removeUpstream(upstream: string | URL): BalancedPool;
upstreams: Array<string>;
addUpstream (upstream: string | URL): BalancedPool
removeUpstream (upstream: string | URL): BalancedPool
upstreams: Array<string>
/** `true` after `pool.close()` has been called. */
closed: boolean;
closed: boolean
/** `true` after `pool.destroyed()` has been called or `pool.close()` has been called and the pool shutdown has completed. */
destroyed: boolean;
destroyed: boolean
// Override dispatcher APIs.
override connect(
override connect (
options: BalancedPoolConnectOptions
): Promise<Dispatcher.ConnectData>;
override connect(
): Promise<Dispatcher.ConnectData>
override connect (
options: BalancedPoolConnectOptions,
callback: (err: Error | null, data: Dispatcher.ConnectData) => void
): void;
): void
}
import { URL } from 'url'
import { TlsOptions } from 'tls'
import Dispatcher from './dispatcher'
import buildConnector from "./connector";
import buildConnector from './connector'
type ClientConnectOptions = Omit<Dispatcher.ConnectOptions, "origin">;
type ClientConnectOptions = Omit<Dispatcher.ConnectOptions, 'origin'>

@@ -12,18 +11,18 @@ /**

export class Client extends Dispatcher {
constructor(url: string | URL, options?: Client.Options);
constructor (url: string | URL, options?: Client.Options)
/** Property to get and set the pipelining factor. */
pipelining: number;
pipelining: number
/** `true` after `client.close()` has been called. */
closed: boolean;
closed: boolean
/** `true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed. */
destroyed: boolean;
destroyed: boolean
// Override dispatcher APIs.
override connect(
override connect (
options: ClientConnectOptions
): Promise<Dispatcher.ConnectData>;
override connect(
): Promise<Dispatcher.ConnectData>
override connect (
options: ClientConnectOptions,
callback: (err: Error | null, data: Dispatcher.ConnectData) => void
): void;
): void
}

@@ -109,2 +108,2 @@

export default Client;
export default Client

@@ -1,5 +0,5 @@

import { Socket } from "net";
import { URL } from "url";
import Connector from "./connector";
import Dispatcher from "./dispatcher";
import { Socket } from 'net'
import { URL } from 'url'
import buildConnector from './connector'
import Dispatcher from './dispatcher'

@@ -19,11 +19,11 @@ declare namespace DiagnosticsChannel {

}
type Error = unknown;
type Error = unknown
interface ConnectParams {
host: URL["host"];
hostname: URL["hostname"];
protocol: URL["protocol"];
port: URL["port"];
host: URL['host'];
hostname: URL['hostname'];
protocol: URL['protocol'];
port: URL['port'];
servername: string | null;
}
type Connector = Connector.connector;
type Connector = buildConnector.connector
export interface RequestCreateMessage {

@@ -30,0 +30,0 @@ request: Request;

@@ -9,4 +9,5 @@ import { URL } from 'url'

import Errors from './errors'
import { Autocomplete } from './utility'
type AbortSignal = unknown;
type AbortSignal = unknown

@@ -18,81 +19,78 @@ export default Dispatcher

/** Dispatches a request. This API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs. It is primarily intended for library developers who implement higher level APIs on top of this. */
dispatch(options: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean;
dispatch (options: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean
/** Starts two-way communications with the requested resource. */
connect(options: Dispatcher.ConnectOptions): Promise<Dispatcher.ConnectData>;
connect(options: Dispatcher.ConnectOptions, callback: (err: Error | null, data: Dispatcher.ConnectData) => void): void;
connect<TOpaque = null>(options: Dispatcher.ConnectOptions<TOpaque>): Promise<Dispatcher.ConnectData<TOpaque>>
connect<TOpaque = null>(options: Dispatcher.ConnectOptions<TOpaque>, callback: (err: Error | null, data: Dispatcher.ConnectData<TOpaque>) => void): void
/** Compose a chain of dispatchers */
compose(dispatchers: Dispatcher.DispatcherComposeInterceptor[]): Dispatcher.ComposedDispatcher;
compose(...dispatchers: Dispatcher.DispatcherComposeInterceptor[]): Dispatcher.ComposedDispatcher;
compose (dispatchers: Dispatcher.DispatcherComposeInterceptor[]): Dispatcher.ComposedDispatcher
compose (...dispatchers: Dispatcher.DispatcherComposeInterceptor[]): Dispatcher.ComposedDispatcher
/** Performs an HTTP request. */
request(options: Dispatcher.RequestOptions): Promise<Dispatcher.ResponseData>;
request(options: Dispatcher.RequestOptions, callback: (err: Error | null, data: Dispatcher.ResponseData) => void): void;
request<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>): Promise<Dispatcher.ResponseData<TOpaque>>
request<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>, callback: (err: Error | null, data: Dispatcher.ResponseData<TOpaque>) => void): void
/** For easy use with `stream.pipeline`. */
pipeline(options: Dispatcher.PipelineOptions, handler: Dispatcher.PipelineHandler): Duplex;
pipeline<TOpaque = null>(options: Dispatcher.PipelineOptions<TOpaque>, handler: Dispatcher.PipelineHandler<TOpaque>): Duplex
/** A faster version of `Dispatcher.request`. */
stream(options: Dispatcher.RequestOptions, factory: Dispatcher.StreamFactory): Promise<Dispatcher.StreamData>;
stream(options: Dispatcher.RequestOptions, factory: Dispatcher.StreamFactory, callback: (err: Error | null, data: Dispatcher.StreamData) => void): void;
stream<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>, factory: Dispatcher.StreamFactory<TOpaque>): Promise<Dispatcher.StreamData<TOpaque>>
stream<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>, factory: Dispatcher.StreamFactory<TOpaque>, callback: (err: Error | null, data: Dispatcher.StreamData<TOpaque>) => void): void
/** Upgrade to a different protocol. */
upgrade(options: Dispatcher.UpgradeOptions): Promise<Dispatcher.UpgradeData>;
upgrade(options: Dispatcher.UpgradeOptions, callback: (err: Error | null, data: Dispatcher.UpgradeData) => void): void;
upgrade (options: Dispatcher.UpgradeOptions): Promise<Dispatcher.UpgradeData>
upgrade (options: Dispatcher.UpgradeOptions, callback: (err: Error | null, data: Dispatcher.UpgradeData) => void): void
/** Closes the client and gracefully waits for enqueued requests to complete before invoking the callback (or returning a promise if no callback is provided). */
close(): Promise<void>;
close(callback: () => void): void;
close (): Promise<void>
close (callback: () => void): void
/** Destroy the client abruptly with the given err. All the pending and running requests will be asynchronously aborted and error. Waits until socket is closed before invoking the callback (or returning a promise if no callback is provided). Since this operation is asynchronously dispatched there might still be some progress on dispatched requests. */
destroy(): Promise<void>;
destroy(err: Error | null): Promise<void>;
destroy(callback: () => void): void;
destroy(err: Error | null, callback: () => void): void;
destroy (): Promise<void>
destroy (err: Error | null): Promise<void>
destroy (callback: () => void): void
destroy (err: Error | null, callback: () => void): void
on(eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this;
on(eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
on(eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
on(eventName: 'drain', callback: (origin: URL) => void): this;
on (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this
on (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
on (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
on (eventName: 'drain', callback: (origin: URL) => void): this
once (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this
once (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
once (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
once (eventName: 'drain', callback: (origin: URL) => void): this
once(eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this;
once(eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
once(eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
once(eventName: 'drain', callback: (origin: URL) => void): this;
off (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this
off (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
off (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
off (eventName: 'drain', callback: (origin: URL) => void): this
addListener (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this
addListener (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
addListener (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
addListener (eventName: 'drain', callback: (origin: URL) => void): this
off(eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this;
off(eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
off(eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
off(eventName: 'drain', callback: (origin: URL) => void): this;
removeListener (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this
removeListener (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
removeListener (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
removeListener (eventName: 'drain', callback: (origin: URL) => void): this
prependListener (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this
prependListener (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
prependListener (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
prependListener (eventName: 'drain', callback: (origin: URL) => void): this
addListener(eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this;
addListener(eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
addListener(eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
addListener(eventName: 'drain', callback: (origin: URL) => void): this;
prependOnceListener (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this
prependOnceListener (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
prependOnceListener (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this
prependOnceListener (eventName: 'drain', callback: (origin: URL) => void): this
removeListener(eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this;
removeListener(eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
removeListener(eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
removeListener(eventName: 'drain', callback: (origin: URL) => void): this;
listeners (eventName: 'connect'): ((origin: URL, targets: readonly Dispatcher[]) => void)[]
listeners (eventName: 'disconnect'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[]
listeners (eventName: 'connectionError'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[]
listeners (eventName: 'drain'): ((origin: URL) => void)[]
prependListener(eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this;
prependListener(eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
prependListener(eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
prependListener(eventName: 'drain', callback: (origin: URL) => void): this;
rawListeners (eventName: 'connect'): ((origin: URL, targets: readonly Dispatcher[]) => void)[]
rawListeners (eventName: 'disconnect'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[]
rawListeners (eventName: 'connectionError'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[]
rawListeners (eventName: 'drain'): ((origin: URL) => void)[]
prependOnceListener(eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this;
prependOnceListener(eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
prependOnceListener(eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this;
prependOnceListener(eventName: 'drain', callback: (origin: URL) => void): this;
listeners(eventName: 'connect'): ((origin: URL, targets: readonly Dispatcher[]) => void)[]
listeners(eventName: 'disconnect'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[];
listeners(eventName: 'connectionError'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[];
listeners(eventName: 'drain'): ((origin: URL) => void)[];
rawListeners(eventName: 'connect'): ((origin: URL, targets: readonly Dispatcher[]) => void)[]
rawListeners(eventName: 'disconnect'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[];
rawListeners(eventName: 'connectionError'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[];
rawListeners(eventName: 'drain'): ((origin: URL) => void)[];
emit(eventName: 'connect', origin: URL, targets: readonly Dispatcher[]): boolean;
emit(eventName: 'disconnect', origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError): boolean;
emit(eventName: 'connectionError', origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError): boolean;
emit(eventName: 'drain', origin: URL): boolean;
emit (eventName: 'connect', origin: URL, targets: readonly Dispatcher[]): boolean
emit (eventName: 'disconnect', origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError): boolean
emit (eventName: 'connectionError', origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError): boolean
emit (eventName: 'drain', origin: URL): boolean
}

@@ -102,3 +100,3 @@

export interface ComposedDispatcher extends Dispatcher {}
export type DispatcherComposeInterceptor = (dispatch: Dispatcher['dispatch']) => Dispatcher['dispatch'];
export type DispatcherComposeInterceptor = (dispatch: Dispatcher['dispatch']) => Dispatcher['dispatch']
export interface DispatchOptions {

@@ -128,6 +126,6 @@ origin?: string | URL;

throwOnError?: boolean;
/** For H2, it appends the expect: 100-continue header, and halts the request body until a 100-continue is received from the remote server*/
/** For H2, it appends the expect: 100-continue header, and halts the request body until a 100-continue is received from the remote server */
expectContinue?: boolean;
}
export interface ConnectOptions {
export interface ConnectOptions<TOpaque = null> {
origin: string | URL;

@@ -140,3 +138,3 @@ path: string;

/** This argument parameter is passed through to `ConnectData` */
opaque?: unknown;
opaque?: TOpaque;
/** Default: 0 */

@@ -147,7 +145,7 @@ maxRedirections?: number;

/** Default: `null` */
responseHeader?: 'raw' | null;
responseHeaders?: 'raw' | null;
}
export interface RequestOptions extends DispatchOptions {
export interface RequestOptions<TOpaque = null> extends DispatchOptions {
/** Default: `null` */
opaque?: unknown;
opaque?: TOpaque;
/** Default: `null` */

@@ -162,7 +160,7 @@ signal?: AbortSignal | EventEmitter | null;

/** Default: `null` */
responseHeader?: 'raw' | null;
responseHeaders?: 'raw' | null;
/** Default: `64 KiB` */
highWaterMark?: number;
}
export interface PipelineOptions extends RequestOptions {
export interface PipelineOptions<TOpaque = null> extends RequestOptions<TOpaque> {
/** `true` if the `handler` will return an object stream. Default: `false` */

@@ -186,11 +184,11 @@ objectMode?: boolean;

/** Default: `null` */
responseHeader?: 'raw' | null;
responseHeaders?: 'raw' | null;
}
export interface ConnectData {
export interface ConnectData<TOpaque = null> {
statusCode: number;
headers: IncomingHttpHeaders;
socket: Duplex;
opaque: unknown;
opaque: TOpaque;
}
export interface ResponseData {
export interface ResponseData<TOpaque = null> {
statusCode: number;

@@ -200,28 +198,28 @@ headers: IncomingHttpHeaders;

trailers: Record<string, string>;
opaque: unknown;
opaque: TOpaque;
context: object;
}
export interface PipelineHandlerData {
export interface PipelineHandlerData<TOpaque = null> {
statusCode: number;
headers: IncomingHttpHeaders;
opaque: unknown;
opaque: TOpaque;
body: BodyReadable;
context: object;
}
export interface StreamData {
opaque: unknown;
export interface StreamData<TOpaque = null> {
opaque: TOpaque;
trailers: Record<string, string>;
}
export interface UpgradeData {
export interface UpgradeData<TOpaque = null> {
headers: IncomingHttpHeaders;
socket: Duplex;
opaque: unknown;
opaque: TOpaque;
}
export interface StreamFactoryData {
export interface StreamFactoryData<TOpaque = null> {
statusCode: number;
headers: IncomingHttpHeaders;
opaque: unknown;
opaque: TOpaque;
context: object;
}
export type StreamFactory = (data: StreamFactoryData) => Writable;
export type StreamFactory<TOpaque = null> = (data: StreamFactoryData<TOpaque>) => Writable
export interface DispatchHandlers {

@@ -245,4 +243,4 @@ /** Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails. */

}
export type PipelineHandler = (data: PipelineHandlerData) => Readable;
export type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH';
export type PipelineHandler<TOpaque = null> = (data: PipelineHandlerData<TOpaque>) => Readable
export type HttpMethod = Autocomplete<'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH'>

@@ -257,2 +255,3 @@ /**

blob(): Promise<Blob>;
bytes(): Promise<Uint8Array>;
formData(): Promise<never>;

@@ -259,0 +258,0 @@ json(): Promise<unknown>;

@@ -7,5 +7,5 @@ import Agent from './agent'

declare class EnvHttpProxyAgent extends Dispatcher {
constructor(opts?: EnvHttpProxyAgent.Options)
constructor (opts?: EnvHttpProxyAgent.Options)
dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean;
dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean
}

@@ -12,0 +12,0 @@

@@ -1,2 +0,2 @@

import { IncomingHttpHeaders } from "./header";
import { IncomingHttpHeaders } from './header'
import Client from './client'

@@ -8,4 +8,4 @@

export class UndiciError extends Error {
name: string;
code: string;
name: string
code: string
}

@@ -15,4 +15,4 @@

export class ConnectTimeoutError extends UndiciError {
name: 'ConnectTimeoutError';
code: 'UND_ERR_CONNECT_TIMEOUT';
name: 'ConnectTimeoutError'
code: 'UND_ERR_CONNECT_TIMEOUT'
}

@@ -22,4 +22,4 @@

export class HeadersTimeoutError extends UndiciError {
name: 'HeadersTimeoutError';
code: 'UND_ERR_HEADERS_TIMEOUT';
name: 'HeadersTimeoutError'
code: 'UND_ERR_HEADERS_TIMEOUT'
}

@@ -35,4 +35,4 @@

export class BodyTimeoutError extends UndiciError {
name: 'BodyTimeoutError';
code: 'UND_ERR_BODY_TIMEOUT';
name: 'BodyTimeoutError'
code: 'UND_ERR_BODY_TIMEOUT'
}

@@ -46,9 +46,9 @@

body?: null | Record<string, any> | string
);
name: 'ResponseStatusCodeError';
code: 'UND_ERR_RESPONSE_STATUS_CODE';
)
name: 'ResponseStatusCodeError'
code: 'UND_ERR_RESPONSE_STATUS_CODE'
body: null | Record<string, any> | string
status: number
statusCode: number
headers: IncomingHttpHeaders | string[] | null;
headers: IncomingHttpHeaders | string[] | null
}

@@ -58,4 +58,4 @@

export class InvalidArgumentError extends UndiciError {
name: 'InvalidArgumentError';
code: 'UND_ERR_INVALID_ARG';
name: 'InvalidArgumentError'
code: 'UND_ERR_INVALID_ARG'
}

@@ -65,4 +65,4 @@

export class InvalidReturnValueError extends UndiciError {
name: 'InvalidReturnValueError';
code: 'UND_ERR_INVALID_RETURN_VALUE';
name: 'InvalidReturnValueError'
code: 'UND_ERR_INVALID_RETURN_VALUE'
}

@@ -72,4 +72,4 @@

export class RequestAbortedError extends UndiciError {
name: 'AbortError';
code: 'UND_ERR_ABORTED';
name: 'AbortError'
code: 'UND_ERR_ABORTED'
}

@@ -79,4 +79,4 @@

export class InformationalError extends UndiciError {
name: 'InformationalError';
code: 'UND_ERR_INFO';
name: 'InformationalError'
code: 'UND_ERR_INFO'
}

@@ -86,4 +86,4 @@

export class RequestContentLengthMismatchError extends UndiciError {
name: 'RequestContentLengthMismatchError';
code: 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH';
name: 'RequestContentLengthMismatchError'
code: 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
}

@@ -93,4 +93,4 @@

export class ResponseContentLengthMismatchError extends UndiciError {
name: 'ResponseContentLengthMismatchError';
code: 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH';
name: 'ResponseContentLengthMismatchError'
code: 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH'
}

@@ -100,4 +100,4 @@

export class ClientDestroyedError extends UndiciError {
name: 'ClientDestroyedError';
code: 'UND_ERR_DESTROYED';
name: 'ClientDestroyedError'
code: 'UND_ERR_DESTROYED'
}

@@ -107,4 +107,4 @@

export class ClientClosedError extends UndiciError {
name: 'ClientClosedError';
code: 'UND_ERR_CLOSED';
name: 'ClientClosedError'
code: 'UND_ERR_CLOSED'
}

@@ -114,4 +114,4 @@

export class SocketError extends UndiciError {
name: 'SocketError';
code: 'UND_ERR_SOCKET';
name: 'SocketError'
code: 'UND_ERR_SOCKET'
socket: Client.SocketInfo | null

@@ -122,4 +122,4 @@ }

export class NotSupportedError extends UndiciError {
name: 'NotSupportedError';
code: 'UND_ERR_NOT_SUPPORTED';
name: 'NotSupportedError'
code: 'UND_ERR_NOT_SUPPORTED'
}

@@ -129,9 +129,9 @@

export class BalancedPoolMissingUpstreamError extends UndiciError {
name: 'MissingUpstreamError';
code: 'UND_ERR_BPL_MISSING_UPSTREAM';
name: 'MissingUpstreamError'
code: 'UND_ERR_BPL_MISSING_UPSTREAM'
}
export class HTTPParserError extends UndiciError {
name: 'HTTPParserError';
code: string;
name: 'HTTPParserError'
code: string
}

@@ -141,4 +141,4 @@

export class ResponseExceededMaxSizeError extends UndiciError {
name: 'ResponseExceededMaxSizeError';
code: 'UND_ERR_RES_EXCEEDED_MAX_SIZE';
name: 'ResponseExceededMaxSizeError'
code: 'UND_ERR_RES_EXCEEDED_MAX_SIZE'
}

@@ -152,16 +152,22 @@

body?: null | Record<string, any> | string
);
name: 'RequestRetryError';
code: 'UND_ERR_REQ_RETRY';
statusCode: number;
)
name: 'RequestRetryError'
code: 'UND_ERR_REQ_RETRY'
statusCode: number
data: {
count: number;
};
headers: Record<string, string | string[]>;
}
headers: Record<string, string | string[]>
}
export class SecureProxyConnectionError extends UndiciError {
name: 'SecureProxyConnectionError';
code: 'UND_ERR_PRX_TLS';
constructor (
cause?: Error,
message?: string,
options?: Record<any, any>
)
name: 'SecureProxyConnectionError'
code: 'UND_ERR_PRX_TLS'
}
}

@@ -5,4 +5,2 @@ import { MessageEvent, ErrorEvent } from './websocket'

import {
EventTarget,
Event,
EventListenerOptions,

@@ -9,0 +7,0 @@ AddEventListenerOptions,

@@ -9,3 +9,3 @@ // based on https://github.com/Ethan-Arrowood/undici-fetch/blob/249269714db874351589d2d364a0645d5160ae71/index.d.ts (MIT license)

import { FormData } from './formdata'
import { HeaderRecord } from './header'
import Dispatcher from './dispatcher'

@@ -40,3 +40,3 @@

* It is recommended to use a library such as [@fastify/busboy](https://www.npmjs.com/package/@fastify/busboy) as follows:
*
*
* @example

@@ -46,8 +46,8 @@ * ```js

* import { Readable } from 'node:stream'
*
*
* const response = await fetch('...')
* const busboy = new Busboy({ headers: { 'content-type': response.headers.get('content-type') } })
*
*
* // handle events emitted from `busboy`
*
*
* Readable.fromWeb(response.body).pipe(busboy)

@@ -73,3 +73,3 @@ * ```

export type HeadersInit = string[][] | Record<string, string | ReadonlyArray<string>> | Headers
export type HeadersInit = [string, string][] | HeaderRecord | Headers

@@ -151,3 +151,3 @@ export declare class Headers implements SpecIterable<[string, string]> {

| 'strict-origin-when-cross-origin'
| 'unsafe-url';
| 'unsafe-url'

@@ -212,4 +212,4 @@ export type RequestMode = 'cors' | 'navigate' | 'no-cors' | 'same-origin'

static error (): Response
static json(data: any, init?: ResponseInit): Response
static json (data: any, init?: ResponseInit): Response
static redirect (url: string | URL, status: ResponseRedirectStatus): Response
}
// Based on https://github.com/octet-stream/form-data/blob/2d0f0dc371517444ce1f22cdde13f51995d0953a/lib/FormData.ts (MIT)
/// <reference types="node" />
import { File } from './file'
import { File } from 'buffer'
import { SpecIterableIterator } from './fetch'

@@ -27,3 +27,3 @@

*/
append(name: string, value: unknown, fileName?: string): void
append (name: string, value: unknown, fileName?: string): void

@@ -40,3 +40,3 @@ /**

*/
set(name: string, value: unknown, fileName?: string): void
set (name: string, value: unknown, fileName?: string): void

@@ -51,3 +51,3 @@ /**

*/
get(name: string): FormDataEntryValue | null
get (name: string): FormDataEntryValue | null

@@ -61,3 +61,3 @@ /**

*/
getAll(name: string): FormDataEntryValue[]
getAll (name: string): FormDataEntryValue[]

@@ -71,3 +71,3 @@ /**

*/
has(name: string): boolean
has (name: string): boolean

@@ -79,3 +79,3 @@ /**

*/
delete(name: string): void
delete (name: string): void

@@ -82,0 +82,0 @@ /**

@@ -1,3 +0,6 @@

import Dispatcher from "./dispatcher";
import Dispatcher from './dispatcher'
declare function setGlobalDispatcher<DispatcherImplementation extends Dispatcher> (dispatcher: DispatcherImplementation): void
declare function getGlobalDispatcher (): Dispatcher
export {

@@ -7,4 +10,1 @@ getGlobalDispatcher,

}
declare function setGlobalDispatcher<DispatcherImplementation extends Dispatcher>(dispatcher: DispatcherImplementation): void;
declare function getGlobalDispatcher(): Dispatcher;

@@ -0,7 +1,7 @@

declare function setGlobalOrigin (origin: string | URL | undefined): void
declare function getGlobalOrigin (): URL | undefined
export {
setGlobalOrigin,
getGlobalOrigin
setGlobalOrigin,
getGlobalOrigin
}
declare function setGlobalOrigin(origin: string | URL | undefined): void;
declare function getGlobalOrigin(): URL | undefined;

@@ -1,5 +0,5 @@

import Dispatcher from "./dispatcher";
import Dispatcher from './dispatcher'
export declare class RedirectHandler implements Dispatcher.DispatchHandlers {
constructor(
constructor (
dispatch: Dispatcher,

@@ -10,7 +10,7 @@ maxRedirections: number,

redirectionLimitReached: boolean
);
)
}
export declare class DecoratorHandler implements Dispatcher.DispatchHandlers {
constructor(handler: Dispatcher.DispatchHandlers);
constructor (handler: Dispatcher.DispatchHandlers)
}

@@ -0,4 +1,160 @@

import { Autocomplete } from './utility'
/**
* The header type declaration of `undici`.
*/
export type IncomingHttpHeaders = Record<string, string | string[] | undefined>;
export type IncomingHttpHeaders = Record<string, string | string[] | undefined>
type HeaderNames = Autocomplete<
| 'Accept'
| 'Accept-CH'
| 'Accept-Charset'
| 'Accept-Encoding'
| 'Accept-Language'
| 'Accept-Patch'
| 'Accept-Post'
| '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'
| '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'
| 'Priority'
| 'Proxy-Authenticate'
| 'Proxy-Authorization'
| 'Range'
| 'Referer'
| 'Referrer-Policy'
| 'Retry-After'
| 'Sec-Fetch-Dest'
| 'Sec-Fetch-Mode'
| 'Sec-Fetch-Site'
| 'Sec-Fetch-User'
| 'Sec-Purpose'
| 'Sec-WebSocket-Accept'
| 'Server'
| 'Server-Timing'
| 'Service-Worker-Navigation-Preload'
| 'Set-Cookie'
| 'SourceMap'
| 'Strict-Transport-Security'
| 'TE'
| 'Timing-Allow-Origin'
| 'Trailer'
| 'Transfer-Encoding'
| 'Upgrade'
| 'Upgrade-Insecure-Requests'
| 'User-Agent'
| 'Vary'
| 'Via'
| 'WWW-Authenticate'
| 'X-Content-Type-Options'
| 'X-Frame-Options'
>
type IANARegisteredMimeType = Autocomplete<
| 'audio/aac'
| 'video/x-msvideo'
| 'image/avif'
| 'video/av1'
| 'application/octet-stream'
| 'image/bmp'
| 'text/css'
| 'text/csv'
| 'application/vnd.ms-fontobject'
| 'application/epub+zip'
| 'image/gif'
| 'application/gzip'
| 'text/html'
| 'image/x-icon'
| 'text/calendar'
| 'image/jpeg'
| 'text/javascript'
| 'application/json'
| 'application/ld+json'
| 'audio/x-midi'
| 'audio/mpeg'
| 'video/mp4'
| 'video/mpeg'
| 'audio/ogg'
| 'video/ogg'
| 'application/ogg'
| 'audio/opus'
| 'font/otf'
| 'application/pdf'
| 'image/png'
| 'application/rtf'
| 'image/svg+xml'
| 'image/tiff'
| 'video/mp2t'
| 'font/ttf'
| 'text/plain'
| 'application/wasm'
| 'video/webm'
| 'audio/webm'
| 'image/webp'
| 'font/woff'
| 'font/woff2'
| 'application/xhtml+xml'
| 'application/xml'
| 'application/zip'
| 'video/3gpp'
| 'video/3gpp2'
| 'model/gltf+json'
| 'model/gltf-binary'
>
type KnownHeaderValues = {
'content-type': IANARegisteredMimeType
}
export type HeaderRecord = {
[K in HeaderNames | Lowercase<HeaderNames>]?: Lowercase<K> extends keyof KnownHeaderValues
? KnownHeaderValues[Lowercase<K>]
: string
}

@@ -1,20 +0,20 @@

import Dispatcher from'./dispatcher'
import Dispatcher from './dispatcher'
import { setGlobalDispatcher, getGlobalDispatcher } from './global-dispatcher'
import { setGlobalOrigin, getGlobalOrigin } from './global-origin'
import Pool from'./pool'
import Pool from './pool'
import { RedirectHandler, DecoratorHandler } from './handlers'
import BalancedPool from './balanced-pool'
import Client from'./client'
import buildConnector from'./connector'
import errors from'./errors'
import Agent from'./agent'
import MockClient from'./mock-client'
import MockPool from'./mock-pool'
import MockAgent from'./mock-agent'
import mockErrors from'./mock-errors'
import ProxyAgent from'./proxy-agent'
import Client from './client'
import buildConnector from './connector'
import errors from './errors'
import Agent from './agent'
import MockClient from './mock-client'
import MockPool from './mock-pool'
import MockAgent from './mock-agent'
import mockErrors from './mock-errors'
import ProxyAgent from './proxy-agent'
import EnvHttpProxyAgent from './env-http-proxy-agent'
import RetryHandler from'./retry-handler'
import RetryAgent from'./retry-agent'
import RetryHandler from './retry-handler'
import RetryAgent from './retry-agent'
import { request, pipeline, stream, connect, upgrade } from './api'

@@ -27,4 +27,2 @@ import interceptors from './interceptors'

export * from './fetch'
export * from './file'
export * from './filereader'
export * from './formdata'

@@ -41,33 +39,31 @@ export * from './diagnostics-channel'

declare namespace Undici {
var Dispatcher: typeof import('./dispatcher').default
var Pool: typeof import('./pool').default;
var RedirectHandler: typeof import ('./handlers').RedirectHandler
var DecoratorHandler: typeof import ('./handlers').DecoratorHandler
var RetryHandler: typeof import ('./retry-handler').default
var createRedirectInterceptor: typeof import ('./interceptors').default.createRedirectInterceptor
var BalancedPool: typeof import('./balanced-pool').default;
var Client: typeof import('./client').default;
var buildConnector: typeof import('./connector').default;
var errors: typeof import('./errors').default;
var Agent: typeof import('./agent').default;
var setGlobalDispatcher: typeof import('./global-dispatcher').setGlobalDispatcher;
var getGlobalDispatcher: typeof import('./global-dispatcher').getGlobalDispatcher;
var request: typeof import('./api').request;
var stream: typeof import('./api').stream;
var pipeline: typeof import('./api').pipeline;
var connect: typeof import('./api').connect;
var upgrade: typeof import('./api').upgrade;
var MockClient: typeof import('./mock-client').default;
var MockPool: typeof import('./mock-pool').default;
var MockAgent: typeof import('./mock-agent').default;
var mockErrors: typeof import('./mock-errors').default;
var fetch: typeof import('./fetch').fetch;
var Headers: typeof import('./fetch').Headers;
var Response: typeof import('./fetch').Response;
var Request: typeof import('./fetch').Request;
var FormData: typeof import('./formdata').FormData;
var File: typeof import('./file').File;
var FileReader: typeof import('./filereader').FileReader;
var caches: typeof import('./cache').caches;
var interceptors: typeof import('./interceptors').default;
const Dispatcher: typeof import('./dispatcher').default
const Pool: typeof import('./pool').default
const RedirectHandler: typeof import ('./handlers').RedirectHandler
const DecoratorHandler: typeof import ('./handlers').DecoratorHandler
const RetryHandler: typeof import ('./retry-handler').default
const createRedirectInterceptor: typeof import ('./interceptors').default.createRedirectInterceptor
const BalancedPool: typeof import('./balanced-pool').default
const Client: typeof import('./client').default
const buildConnector: typeof import('./connector').default
const errors: typeof import('./errors').default
const Agent: typeof import('./agent').default
const setGlobalDispatcher: typeof import('./global-dispatcher').setGlobalDispatcher
const getGlobalDispatcher: typeof import('./global-dispatcher').getGlobalDispatcher
const request: typeof import('./api').request
const stream: typeof import('./api').stream
const pipeline: typeof import('./api').pipeline
const connect: typeof import('./api').connect
const upgrade: typeof import('./api').upgrade
const MockClient: typeof import('./mock-client').default
const MockPool: typeof import('./mock-pool').default
const MockAgent: typeof import('./mock-agent').default
const mockErrors: typeof import('./mock-errors').default
const fetch: typeof import('./fetch').fetch
const Headers: typeof import('./fetch').Headers
const Response: typeof import('./fetch').Response
const Request: typeof import('./fetch').Request
const FormData: typeof import('./formdata').FormData
const caches: typeof import('./cache').caches
const interceptors: typeof import('./interceptors').default
}

@@ -1,5 +0,5 @@

import Dispatcher from "./dispatcher";
import RetryHandler from "./retry-handler";
import Dispatcher from './dispatcher'
import RetryHandler from './retry-handler'
export default Interceptors;
export default Interceptors

@@ -10,7 +10,9 @@ declare namespace Interceptors {

export type RedirectInterceptorOpts = { maxRedirections?: number }
export function createRedirectInterceptor(opts: RedirectInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
export function dump(opts?: DumpInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
export function retry(opts?: RetryInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
export function redirect(opts?: RedirectInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
export type ResponseErrorInterceptorOpts = { throwOnError: boolean }
export function createRedirectInterceptor (opts: RedirectInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
export function dump (opts?: DumpInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
export function retry (opts?: RetryInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
export function redirect (opts?: RedirectInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
export function responseError (opts?: ResponseErrorInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
}
import Agent from './agent'
import Dispatcher from './dispatcher'
import { Interceptable, MockInterceptor } from './mock-interceptor'
import MockDispatch = MockInterceptor.MockDispatch;
import MockDispatch = MockInterceptor.MockDispatch

@@ -14,26 +14,26 @@ export default MockAgent

declare class MockAgent<TMockAgentOptions extends MockAgent.Options = MockAgent.Options> extends Dispatcher {
constructor(options?: MockAgent.Options)
constructor (options?: TMockAgentOptions)
/** Creates and retrieves mock Dispatcher instances which can then be used to intercept HTTP requests. If the number of connections on the mock agent is set to 1, a MockClient instance is returned. Otherwise a MockPool instance is returned. */
get<TInterceptable extends Interceptable>(origin: string): TInterceptable;
get<TInterceptable extends Interceptable>(origin: RegExp): TInterceptable;
get<TInterceptable extends Interceptable>(origin: ((origin: string) => boolean)): TInterceptable;
get<TInterceptable extends Interceptable>(origin: string): TInterceptable
get<TInterceptable extends Interceptable>(origin: RegExp): TInterceptable
get<TInterceptable extends Interceptable>(origin: ((origin: string) => boolean)): TInterceptable
/** Dispatches a mocked request. */
dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean;
dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean
/** Closes the mock agent and waits for registered mock pools and clients to also close before resolving. */
close(): Promise<void>;
close (): Promise<void>
/** Disables mocking in MockAgent. */
deactivate(): void;
deactivate (): void
/** Enables mocking in a MockAgent instance. When instantiated, a MockAgent is automatically activated. Therefore, this method is only effective after `MockAgent.deactivate` has been called. */
activate(): void;
activate (): void
/** Define host matchers so only matching requests that aren't intercepted by the mock dispatchers will be attempted. */
enableNetConnect(): void;
enableNetConnect(host: string): void;
enableNetConnect(host: RegExp): void;
enableNetConnect(host: ((host: string) => boolean)): void;
enableNetConnect (): void
enableNetConnect (host: string): void
enableNetConnect (host: RegExp): void
enableNetConnect (host: ((host: string) => boolean)): void
/** Causes all requests to throw when requests are not matched in a MockAgent intercept. */
disableNetConnect(): void;
pendingInterceptors(): PendingInterceptor[];
assertNoPendingInterceptors(options?: {
disableNetConnect (): void
pendingInterceptors (): PendingInterceptor[]
assertNoPendingInterceptors (options?: {
pendingInterceptorsFormatter?: PendingInterceptorsFormatter;
}): void;
}): void
}

@@ -49,4 +49,4 @@

/** A custom agent to be encapsulated by the MockAgent. */
agent?: Agent;
agent?: Dispatcher;
}
}

@@ -10,9 +10,9 @@ import Client from './client'

declare class MockClient extends Client implements Interceptable {
constructor(origin: string, options: MockClient.Options);
constructor (origin: string, options: MockClient.Options)
/** Intercepts any matching requests that use the same origin as this mock client. */
intercept(options: MockInterceptor.Options): MockInterceptor;
intercept (options: MockInterceptor.Options): MockInterceptor
/** Dispatches a mocked request. */
dispatch(options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandlers): boolean;
dispatch (options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandlers): boolean
/** Closes the mock client and gracefully waits for enqueued requests to complete. */
close(): Promise<void>;
close (): Promise<void>
}

@@ -19,0 +19,0 @@

@@ -8,6 +8,6 @@ import Errors from './errors'

export class MockNotMatchedError extends Errors.UndiciError {
constructor(message?: string);
name: 'MockNotMatchedError';
code: 'UND_MOCK_ERR_MOCK_NOT_MATCHED';
constructor (message?: string)
name: 'MockNotMatchedError'
code: 'UND_MOCK_ERR_MOCK_NOT_MATCHED'
}
}
import { IncomingHttpHeaders } from './header'
import Dispatcher from './dispatcher';
import Dispatcher from './dispatcher'
import { BodyInit, Headers } from './fetch'
export {
Interceptable,
MockInterceptor,
MockScope
}
/** The scope associated with a mock dispatch. */
declare class MockScope<TData extends object = object> {
constructor(mockDispatch: MockInterceptor.MockDispatch<TData>);
constructor (mockDispatch: MockInterceptor.MockDispatch<TData>)
/** Delay a reply by a set amount of time in ms. */
delay(waitInMs: number): MockScope<TData>;
delay (waitInMs: number): MockScope<TData>
/** Persist the defined mock data for the associated reply. It will return the defined mock data indefinitely. */
persist(): MockScope<TData>;
persist (): MockScope<TData>
/** Define a reply for a set amount of matching requests. */
times(repeatTimes: number): MockScope<TData>;
times (repeatTimes: number): MockScope<TData>
}

@@ -24,5 +18,5 @@

declare class MockInterceptor {
constructor(options: MockInterceptor.Options, mockDispatches: MockInterceptor.MockDispatch[]);
constructor (options: MockInterceptor.Options, mockDispatches: MockInterceptor.MockDispatch[])
/** Mock an undici request with the defined reply. */
reply<TData extends object = object>(replyOptionsCallback: MockInterceptor.MockReplyOptionsCallback<TData>): MockScope<TData>;
reply<TData extends object = object>(replyOptionsCallback: MockInterceptor.MockReplyOptionsCallback<TData>): MockScope<TData>
reply<TData extends object = object>(

@@ -32,11 +26,11 @@ statusCode: number,

responseOptions?: MockInterceptor.MockResponseOptions
): MockScope<TData>;
): MockScope<TData>
/** Mock an undici request by throwing the defined reply error. */
replyWithError<TError extends Error = Error>(error: TError): MockScope;
replyWithError<TError extends Error = Error>(error: TError): MockScope
/** Set default reply headers on the interceptor for subsequent mocked replies. */
defaultReplyHeaders(headers: IncomingHttpHeaders): MockInterceptor;
defaultReplyHeaders (headers: IncomingHttpHeaders): MockInterceptor
/** Set default reply trailers on the interceptor for subsequent mocked replies. */
defaultReplyTrailers(trailers: Record<string, string>): MockInterceptor;
defaultReplyTrailers (trailers: Record<string, string>): MockInterceptor
/** Set automatically calculated content-length header on subsequent mocked replies. */
replyContentLength(): MockInterceptor;
replyContentLength (): MockInterceptor
}

@@ -85,3 +79,3 @@

opts: MockResponseCallbackOptions
) => TData | Buffer | string;
) => TData | Buffer | string

@@ -97,1 +91,7 @@ export type MockReplyOptionsCallback<TData extends object = object> = (

}
export {
Interceptable,
MockInterceptor,
MockScope
}

@@ -10,9 +10,9 @@ import Pool from './pool'

declare class MockPool extends Pool implements Interceptable {
constructor(origin: string, options: MockPool.Options);
constructor (origin: string, options: MockPool.Options)
/** Intercepts any matching requests that use the same origin as this mock pool. */
intercept(options: MockInterceptor.Options): MockInterceptor;
intercept (options: MockInterceptor.Options): MockInterceptor
/** Dispatches a mocked request. */
dispatch(options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandlers): boolean;
dispatch (options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandlers): boolean
/** Closes the mock pool and gracefully waits for enqueued requests to complete. */
close(): Promise<void>;
close (): Promise<void>
}

@@ -19,0 +19,0 @@

@@ -5,44 +5,2 @@ /// <reference types="node" />

export type DOMException = typeof globalThis extends { DOMException: infer T }
? T
: any
export type EventTarget = typeof globalThis extends { EventTarget: infer T }
? T
: {
addEventListener(
type: string,
listener: any,
options?: any,
): void
dispatchEvent(event: Event): boolean
removeEventListener(
type: string,
listener: any,
options?: any | boolean,
): void
}
export type Event = typeof globalThis extends { Event: infer T }
? T
: {
readonly bubbles: boolean
cancelBubble: () => void
readonly cancelable: boolean
readonly composed: boolean
composedPath(): [EventTarget?]
readonly currentTarget: EventTarget | null
readonly defaultPrevented: boolean
readonly eventPhase: 0 | 2
readonly isTrusted: boolean
preventDefault(): void
returnValue: boolean
readonly srcElement: EventTarget | null
stopImmediatePropagation(): void
stopPropagation(): void
readonly target: EventTarget | null
readonly timeStamp: number
readonly type: string
}
export interface EventInit {

@@ -49,0 +7,0 @@ bubbles?: boolean

@@ -1,2 +0,2 @@

import Pool from "./pool"
import Pool from './pool'

@@ -6,15 +6,15 @@ export default PoolStats

declare class PoolStats {
constructor(pool: Pool);
constructor (pool: Pool)
/** Number of open socket connections in this pool. */
connected: number;
connected: number
/** Number of open socket connections in this pool that do not have an active request. */
free: number;
free: number
/** Number of pending requests across all clients in this pool. */
pending: number;
pending: number
/** Number of queued requests across all clients in this pool. */
queued: number;
queued: number
/** Number of currently active requests across all clients in this pool. */
running: number;
running: number
/** Number of active, pending, or queued requests across all clients in this pool. */
size: number;
size: number
}
import Client from './client'
import TPoolStats from './pool-stats'
import { URL } from 'url'
import Dispatcher from "./dispatcher";
import Dispatcher from './dispatcher'
export default Pool
type PoolConnectOptions = Omit<Dispatcher.ConnectOptions, "origin">;
type PoolConnectOptions = Omit<Dispatcher.ConnectOptions, 'origin'>
declare class Pool extends Dispatcher {
constructor(url: string | URL, options?: Pool.Options)
constructor (url: string | URL, options?: Pool.Options)
/** `true` after `pool.close()` has been called. */
closed: boolean;
closed: boolean
/** `true` after `pool.destroyed()` has been called or `pool.close()` has been called and the pool shutdown has completed. */
destroyed: boolean;
destroyed: boolean
/** Aggregate stats for a Pool. */
readonly stats: TPoolStats;
readonly stats: TPoolStats
// Override dispatcher APIs.
override connect(
override connect (
options: PoolConnectOptions
): Promise<Dispatcher.ConnectData>;
override connect(
): Promise<Dispatcher.ConnectData>
override connect (
options: PoolConnectOptions,
callback: (err: Error | null, data: Dispatcher.ConnectData) => void
): void;
): void
}
declare namespace Pool {
export type PoolStats = TPoolStats;
export type PoolStats = TPoolStats
export interface Options extends Client.Options {

@@ -37,4 +37,4 @@ /** Default: `(origin, opts) => new Client(origin, opts)`. */

interceptors?: { Pool?: readonly Dispatcher.DispatchInterceptor[] } & Client.Options["interceptors"]
interceptors?: { Pool?: readonly Dispatcher.DispatchInterceptor[] } & Client.Options['interceptors']
}
}
import Agent from './agent'
import buildConnector from './connector';
import buildConnector from './connector'
import Dispatcher from './dispatcher'

@@ -9,6 +9,6 @@ import { IncomingHttpHeaders } from './header'

declare class ProxyAgent extends Dispatcher {
constructor(options: ProxyAgent.Options | string)
constructor (options: ProxyAgent.Options | string)
dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean;
close(): Promise<void>;
dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean
close (): Promise<void>
}

@@ -15,0 +15,0 @@

@@ -1,2 +0,2 @@

import { Readable } from "stream";
import { Readable } from 'stream'
import { Blob } from 'buffer'

@@ -7,3 +7,3 @@

declare class BodyReadable extends Readable {
constructor(
constructor (
resume?: (this: Readable, size: number) => void | null,

@@ -17,3 +17,3 @@ abort?: () => void | null,

*/
text(): Promise<string>
text (): Promise<string>

@@ -23,3 +23,3 @@ /** Consumes and returns the body as a JavaScript Object

*/
json(): Promise<unknown>
json (): Promise<unknown>

@@ -29,8 +29,13 @@ /** Consumes and returns the body as a Blob

*/
blob(): Promise<Blob>
blob (): Promise<Blob>
/** Consumes and returns the body as an Uint8Array
* https://fetch.spec.whatwg.org/#dom-body-bytes
*/
bytes (): Promise<Uint8Array>
/** Consumes and returns the body as an ArrayBuffer
* https://fetch.spec.whatwg.org/#dom-body-arraybuffer
*/
arrayBuffer(): Promise<ArrayBuffer>
arrayBuffer (): Promise<ArrayBuffer>

@@ -41,3 +46,3 @@ /** Not implemented

*/
formData(): Promise<never>
formData (): Promise<never>

@@ -52,3 +57,3 @@ /** Returns true if the body is not null and the body has been consumed

/**
/**
* If body is null, it should return null as the body

@@ -65,3 +70,3 @@ *

*/
dump(opts?: { limit: number }): Promise<void>
dump (opts?: { limit: number }): Promise<void>
}

@@ -7,3 +7,3 @@ import Dispatcher from './dispatcher'

declare class RetryAgent extends Dispatcher {
constructor(dispatcher: Dispatcher, options?: RetryHandler.RetryOptions)
constructor (dispatcher: Dispatcher, options?: RetryHandler.RetryOptions)
}

@@ -1,7 +0,7 @@

import Dispatcher from "./dispatcher";
import Dispatcher from './dispatcher'
export default RetryHandler;
export default RetryHandler
declare class RetryHandler implements Dispatcher.DispatchHandlers {
constructor(
constructor (
options: Dispatcher.DispatchOptions & {

@@ -11,7 +11,7 @@ retryOptions?: RetryHandler.RetryOptions;

retryHandlers: RetryHandler.RetryHandlers
);
)
}
declare namespace RetryHandler {
export type RetryState = { counter: number; };
export type RetryState = { counter: number; }

@@ -25,3 +25,3 @@ export type RetryContext = {

export type OnRetryCallback = (result?: Error | null) => void;
export type OnRetryCallback = (result?: Error | null) => void

@@ -37,3 +37,3 @@ export type RetryCallback = (

callback: OnRetryCallback
) => number | null;
) => number | null

@@ -116,5 +116,5 @@ export interface RetryOptions {

export interface RetryHandlers {
dispatch: Dispatcher["dispatch"];
dispatch: Dispatcher['dispatch'];
handler: Dispatcher.DispatchHandlers;
}
}

@@ -6,3 +6,3 @@ export namespace util {

*/
export function headerNameToString(value: string | Buffer): string;
export function headerNameToString (value: string | Buffer): string

@@ -15,6 +15,6 @@ /**

*/
export function parseHeaders(
export function parseHeaders (
headers: (Buffer | string | (Buffer | string)[])[],
obj?: Record<string, string | string[]>
): Record<string, string | string[]>;
): Record<string, string | string[]>
}

@@ -37,2 +37,13 @@ // These types are not exported, and are only used internally

interface WebIDLTypes {
UNDEFINED: 1,
BOOLEAN: 2,
STRING: 3,
SYMBOL: 4,
NUMBER: 5,
BIGINT: 6,
NULL: 7
OBJECT: 8
}
interface WebidlUtil {

@@ -42,3 +53,5 @@ /**

*/
Type (object: unknown):
Type (object: unknown): WebIDLTypes
TypeValueToString (o: unknown):
| 'Undefined'

@@ -53,2 +66,4 @@ | 'Boolean'

Types: WebIDLTypes
/**

@@ -156,3 +171,3 @@ * @see https://webidl.spec.whatwg.org/#abstract-opdef-converttoint

['sequence<ByteString>']: SequenceConverter<string>
['sequence<sequence<ByteString>>']: SequenceConverter<string[]>

@@ -174,4 +189,6 @@

*/
brandCheck <Interface>(V: unknown, cls: Interface, opts?: { strict?: boolean }): asserts V is Interface
brandCheck <Interface extends new () => unknown>(V: unknown, cls: Interface): asserts V is Interface
brandCheckMultiple <Interfaces extends (new () => unknown)[]> (V: unknown, list: Interfaces): asserts V is Interfaces[number]
/**

@@ -200,3 +217,4 @@ * @see https://webidl.spec.whatwg.org/#es-sequence

V: unknown,
opts?: { strict: boolean }
prefix: string,
argument: string
) => asserts V is typeof cls

@@ -203,0 +221,0 @@

@@ -6,4 +6,2 @@ /// <reference types="node" />

import {
EventTarget,
Event,
EventInit,

@@ -28,3 +26,3 @@ EventListenerOptions,

binaryType: BinaryType
readonly bufferedAmount: number

@@ -31,0 +29,0 @@ readonly extensions: string

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc