Comparing version 6.19.8 to 7.0.0-alpha.1
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
1145511
21
169
23696
3
463