Created a first draft of this action. (#14)
All checks were successful
Auto Maintenance Cycle / pre-commit Autoupdate (push) Successful in 40s

Reviewed-on: #14
Co-authored-by: Philipp Horstenkamp <philipp@horstenkamp.de>
Co-committed-by: Philipp Horstenkamp <philipp@horstenkamp.de>
This commit is contained in:
2023-11-19 23:21:45 +01:00
committed by Philipp Horstenkamp
parent ac99073c40
commit 440cf79854
296 changed files with 37461 additions and 0 deletions

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

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

443
node_modules/undici/README.md generated vendored Normal file
View File

@ -0,0 +1,443 @@
# 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)
An HTTP/1.1 client, written from scratch for Node.js.
> Undici means eleven in Italian. 1.1 -> 11 -> Eleven -> Undici.
It is also a Stranger Things reference.
Have a question about using Undici? Open a [Q&A Discussion](https://github.com/nodejs/undici/discussions/new) or join our official OpenJS [Slack](https://openjs-foundation.slack.com/archives/C01QF9Q31QD) channel.
## Install
```
npm i undici
```
## Benchmarks
The benchmark is a simple `hello world` [example](benchmarks/benchmark.js) using a
number of unix sockets (connections) with a pipelining depth of 10 running on Node 20.6.0.
### Connections 1
| Tests | Samples | Result | Tolerance | Difference with slowest |
|---------------------|---------|---------------|-----------|-------------------------|
| http - no keepalive | 15 | 5.32 req/sec | ± 2.61 % | - |
| http - keepalive | 10 | 5.35 req/sec | ± 2.47 % | + 0.44 % |
| undici - fetch | 15 | 41.85 req/sec | ± 2.49 % | + 686.04 % |
| undici - pipeline | 40 | 50.36 req/sec | ± 2.77 % | + 845.92 % |
| undici - stream | 15 | 60.58 req/sec | ± 2.75 % | + 1037.72 % |
| undici - request | 10 | 61.19 req/sec | ± 2.60 % | + 1049.24 % |
| undici - dispatch | 20 | 64.84 req/sec | ± 2.81 % | + 1117.81 % |
### Connections 50
| Tests | Samples | Result | Tolerance | Difference with slowest |
|---------------------|---------|------------------|-----------|-------------------------|
| undici - fetch | 30 | 2107.19 req/sec | ± 2.69 % | - |
| http - no keepalive | 10 | 2698.90 req/sec | ± 2.68 % | + 28.08 % |
| http - keepalive | 10 | 4639.49 req/sec | ± 2.55 % | + 120.17 % |
| undici - pipeline | 40 | 6123.33 req/sec | ± 2.97 % | + 190.59 % |
| undici - stream | 50 | 9426.51 req/sec | ± 2.92 % | + 347.35 % |
| undici - request | 10 | 10162.88 req/sec | ± 2.13 % | + 382.29 % |
| undici - dispatch | 50 | 11191.11 req/sec | ± 2.98 % | + 431.09 % |
## Quick Start
```js
import { request } from 'undici'
const {
statusCode,
headers,
trailers,
body
} = await request('http://localhost:3000/foo')
console.log('response received', statusCode)
console.log('headers', headers)
for await (const data of body) {
console.log('data', data)
}
console.log('trailers', trailers)
```
## Body Mixins
The `body` mixins are the most common way to format the request/response body. Mixins include:
- [`.formData()`](https://fetch.spec.whatwg.org/#dom-body-formdata)
- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)
- [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)
Example usage:
```js
import { request } from 'undici'
const {
statusCode,
headers,
trailers,
body
} = await request('http://localhost:3000/foo')
console.log('response received', statusCode)
console.log('headers', headers)
console.log('data', await body.json())
console.log('trailers', trailers)
```
_Note: Once a mixin has been called then the body cannot be reused, thus calling additional mixins on `.body`, e.g. `.body.json(); .body.text()` will result in an error `TypeError: unusable` being thrown and returned through the `Promise` rejection._
Should you need to access the `body` in plain-text after using a mixin, the best practice is to use the `.text()` mixin first and then manually parse the text to the desired format.
For more information about their behavior, please reference the body mixin from the [Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).
## Common API Methods
This section documents our most commonly used API methods. Additional APIs are documented in their own files within the [docs](./docs/) folder and are accessible via the navigation list on the left side of the docs site.
### `undici.request([url, options]): Promise`
Arguments:
* **url** `string | URL | UrlObject`
* **options** [`RequestOptions`](./docs/api/Dispatcher.md#parameter-requestoptions)
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
* **maxRedirections** `Integer` - Default: `0`
Returns a promise with the result of the `Dispatcher.request` method.
Calls `options.dispatcher.request(options)`.
See [Dispatcher.request](./docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details.
### `undici.stream([url, options, ]factory): Promise`
Arguments:
* **url** `string | URL | UrlObject`
* **options** [`StreamOptions`](./docs/api/Dispatcher.md#parameter-streamoptions)
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
* **maxRedirections** `Integer` - Default: `0`
* **factory** `Dispatcher.stream.factory`
Returns a promise with the result of the `Dispatcher.stream` method.
Calls `options.dispatcher.stream(options, factory)`.
See [Dispatcher.stream](docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details.
### `undici.pipeline([url, options, ]handler): Duplex`
Arguments:
* **url** `string | URL | UrlObject`
* **options** [`PipelineOptions`](docs/api/Dispatcher.md#parameter-pipelineoptions)
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
* **maxRedirections** `Integer` - Default: `0`
* **handler** `Dispatcher.pipeline.handler`
Returns: `stream.Duplex`
Calls `options.dispatch.pipeline(options, handler)`.
See [Dispatcher.pipeline](docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details.
### `undici.connect([url, options]): Promise`
Starts two-way communications with the requested resource using [HTTP CONNECT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT).
Arguments:
* **url** `string | URL | UrlObject`
* **options** [`ConnectOptions`](docs/api/Dispatcher.md#parameter-connectoptions)
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
* **maxRedirections** `Integer` - Default: `0`
* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)
Returns a promise with the result of the `Dispatcher.connect` method.
Calls `options.dispatch.connect(options)`.
See [Dispatcher.connect](docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details.
### `undici.fetch(input[, init]): Promise`
Implements [fetch](https://fetch.spec.whatwg.org/#fetch-method).
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
* https://fetch.spec.whatwg.org/#fetch-method
Only supported on Node 16.8+.
Basic usage example:
```js
import { fetch } from 'undici'
const res = await fetch('https://example.com')
const json = await res.json()
console.log(json)
```
You can pass an optional dispatcher to `fetch` as:
```js
import { fetch, Agent } from 'undici'
const res = await fetch('https://example.com', {
// Mocks are also supported
dispatcher: new Agent({
keepAliveTimeout: 10,
keepAliveMaxTimeout: 10
})
})
const json = await res.json()
console.log(json)
```
#### `request.body`
A body can be of the following types:
- ArrayBuffer
- ArrayBufferView
- AsyncIterables
- Blob
- Iterables
- String
- URLSearchParams
- FormData
In this implementation of fetch, ```request.body``` now accepts ```Async Iterables```. It is not present in the [Fetch Standard.](https://fetch.spec.whatwg.org)
```js
import { fetch } from 'undici'
const data = {
async *[Symbol.asyncIterator]() {
yield 'hello'
yield 'world'
},
}
await fetch('https://example.com', { body: data, method: 'POST', duplex: 'half' })
```
#### `request.duplex`
- half
In this implementation of fetch, `request.duplex` must be set if `request.body` is `ReadableStream` or `Async Iterables`. And fetch requests are currently always be full duplex. More detail refer to [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()`.
```js
import { fetch } from 'undici'
import { Readable } from 'node:stream'
const response = await fetch('https://example.com')
const readableWebStream = response.body
const readableNodeStream = Readable.fromWeb(readableWebStream)
```
#### Specification Compliance
This section documents parts of the [Fetch Standard](https://fetch.spec.whatwg.org) that Undici does
not support or does not fully implement.
##### Garbage Collection
* https://fetch.spec.whatwg.org/#garbage-collection
The [Fetch Standard](https://fetch.spec.whatwg.org) allows users to skip consuming the response body by relying on
[garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#garbage_collection) to release connection resources. Undici does not do the same. Therefore, it is important to always either consume or cancel the response body.
Garbage collection in Node is less aggressive and deterministic
(due to the lack of clear idle periods that browsers have through the rendering refresh rate)
which means that leaving the release of connection resources to the garbage collector can lead
to excessive connection usage, reduced performance (due to less connection re-use), and even
stalls or deadlocks when running out of connections.
```js
// Do
const headers = await fetch(url)
.then(async res => {
for await (const chunk of res.body) {
// force consumption of body
}
return res.headers
})
// Do not
const headers = await fetch(url)
.then(res => res.headers)
```
However, if you want to get only headers, it might be better to use `HEAD` request method. Usage of this method will obviate the need for consumption or cancelling of the response body. See [MDN - HTTP - HTTP request methods - HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) for more details.
```js
const headers = await fetch(url, { method: 'HEAD' })
.then(res => res.headers)
```
##### Forbidden and Safelisted Header Names
* https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name
* https://fetch.spec.whatwg.org/#forbidden-header-name
* https://fetch.spec.whatwg.org/#forbidden-response-header-name
* https://github.com/wintercg/fetch/issues/6
The [Fetch Standard](https://fetch.spec.whatwg.org) requires implementations to exclude certain headers from requests and responses. In browser environments, some headers are forbidden so the user agent remains in full control over them. In Undici, these constraints are removed to give more control to the user.
### `undici.upgrade([url, options]): Promise`
Upgrade to a different protocol. See [MDN - HTTP - Protocol upgrade mechanism](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) for more details.
Arguments:
* **url** `string | URL | UrlObject`
* **options** [`UpgradeOptions`](docs/api/Dispatcher.md#parameter-upgradeoptions)
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
* **maxRedirections** `Integer` - Default: `0`
* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)
Returns a promise with the result of the `Dispatcher.upgrade` method.
Calls `options.dispatcher.upgrade(options)`.
See [Dispatcher.upgrade](docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details.
### `undici.setGlobalDispatcher(dispatcher)`
* dispatcher `Dispatcher`
Sets the global dispatcher used by Common API Methods.
### `undici.getGlobalDispatcher()`
Gets the global dispatcher used by Common API Methods.
Returns: `Dispatcher`
### `undici.setGlobalOrigin(origin)`
* origin `string | URL | undefined`
Sets the global origin used in `fetch`.
If `undefined` is passed, the global origin will be reset. This will cause `Response.redirect`, `new Request()`, and `fetch` to throw an error when a relative path is passed.
```js
setGlobalOrigin('http://localhost:3000')
const response = await fetch('/api/ping')
console.log(response.url) // http://localhost:3000/api/ping
```
### `undici.getGlobalOrigin()`
Gets the global origin used in `fetch`.
Returns: `URL`
### `UrlObject`
* **port** `string | number` (optional)
* **path** `string` (optional)
* **pathname** `string` (optional)
* **hostname** `string` (optional)
* **origin** `string` (optional)
* **protocol** `string` (optional)
* **search** `string` (optional)
## Specification Compliance
This section documents parts of the HTTP/1.1 specification that Undici does
not support or does not fully implement.
### Expect
Undici does not support the `Expect` request header field. The request
body is always immediately sent and the `100 Continue` response will be
ignored.
Refs: https://tools.ietf.org/html/rfc7231#section-5.1.1
### Pipelining
Undici will only use pipelining if configured with a `pipelining` factor
greater than `1`.
Undici always assumes that connections are persistent and will immediately
pipeline requests, without checking whether the connection is persistent.
Hence, automatic fallback to HTTP/1.0 or HTTP/1.1 without pipelining is
not supported.
Undici will immediately pipeline when retrying requests after a failed
connection. However, Undici will not retry the first remaining requests in
the prior pipeline and instead error the corresponding callback/promise/stream.
Undici will abort all running requests in the pipeline when any of them are
aborted.
* Refs: https://tools.ietf.org/html/rfc2616#section-8.1.2.2
* Refs: https://tools.ietf.org/html/rfc7230#section-6.3.2
### Manual Redirect
Since it is not possible to manually follow an HTTP redirect on the server-side,
Undici returns the actual response instead of an `opaqueredirect` filtered one
when invoked with a `manual` redirect. This aligns `fetch()` with the other
implementations in Deno and Cloudflare Workers.
Refs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
## Workarounds
### Network address family autoselection.
If you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record)
first, there are chances that your local router or ISP might have problem connecting to IPv6 networks. In that case
undici will throw an error with code `UND_ERR_CONNECT_TIMEOUT`.
If the target server resolves to both a IPv6 and IPv4 (A records) address and you are using a compatible Node version
(18.3.0 and above), you can fix the problem by providing the `autoSelectFamily` option (support by both `undici.request`
and `undici.Agent`) which will enable the family autoselection algorithm when establishing the connection.
## Collaborators
* [__Daniele Belardi__](https://github.com/dnlup), <https://www.npmjs.com/~dnlup>
* [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood>
* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina>
* [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf>
* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag>
* [__Szymon Marczak__](https://github.com/szmarczak), <https://www.npmjs.com/~szmarczak>
* [__Tomas Della Vedova__](https://github.com/delvedor), <https://www.npmjs.com/~delvedor>
### Releasers
* [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood>
* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina>
* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag>
* [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf>
## License
MIT

80
node_modules/undici/docs/api/Agent.md generated vendored Normal file
View File

@ -0,0 +1,80 @@
# Agent
Extends: `undici.Dispatcher`
Agent allow dispatching requests against multiple different origins.
Requests are not guaranteed to be dispatched in order of invocation.
## `new undici.Agent([options])`
Arguments:
* **options** `AgentOptions` (optional)
Returns: `Agent`
### Parameter: `AgentOptions`
Extends: [`PoolOptions`](Pool.md#parameter-pooloptions)
* **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.
## Instance Properties
### `Agent.closed`
Implements [Client.closed](Client.md#clientclosed)
### `Agent.destroyed`
Implements [Client.destroyed](Client.md#clientdestroyed)
## Instance Methods
### `Agent.close([callback])`
Implements [`Dispatcher.close([callback])`](Dispatcher.md#dispatcherclosecallback-promise).
### `Agent.destroy([error, callback])`
Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
### `Agent.dispatch(options, handler: AgentDispatchOptions)`
Implements [`Dispatcher.dispatch(options, handler)`](Dispatcher.md#dispatcherdispatchoptions-handler).
#### Parameter: `AgentDispatchOptions`
Extends: [`DispatchOptions`](Dispatcher.md#parameter-dispatchoptions)
* **origin** `string | URL`
* **maxRedirections** `Integer`.
Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
### `Agent.connect(options[, callback])`
See [`Dispatcher.connect(options[, callback])`](Dispatcher.md#dispatcherconnectoptions-callback).
### `Agent.dispatch(options, handler)`
Implements [`Dispatcher.dispatch(options, handler)`](Dispatcher.md#dispatcherdispatchoptions-handler).
### `Agent.pipeline(options, handler)`
See [`Dispatcher.pipeline(options, handler)`](Dispatcher.md#dispatcherpipelineoptions-handler).
### `Agent.request(options[, callback])`
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
### `Agent.stream(options, factory[, callback])`
See [`Dispatcher.stream(options, factory[, callback])`](Dispatcher.md#dispatcherstreamoptions-factory-callback).
### `Agent.upgrade(options[, callback])`
See [`Dispatcher.upgrade(options[, callback])`](Dispatcher.md#dispatcherupgradeoptions-callback).

99
node_modules/undici/docs/api/BalancedPool.md generated vendored Normal file
View File

@ -0,0 +1,99 @@
# Class: BalancedPool
Extends: `undici.Dispatcher`
A pool of [Pool](Pool.md) instances connected to multiple upstreams.
Requests are not guaranteed to be dispatched in order of invocation.
## `new BalancedPool(upstreams [, options])`
Arguments:
* **upstreams** `URL | string | string[]` - It should only include the **protocol, hostname, and port**.
* **options** `BalancedPoolOptions` (optional)
### Parameter: `BalancedPoolOptions`
Extends: [`PoolOptions`](Pool.md#parameter-pooloptions)
* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`
The `PoolOptions` are passed to each of the `Pool` instances being created.
## Instance Properties
### `BalancedPool.upstreams`
Returns an array of upstreams that were previously added.
### `BalancedPool.closed`
Implements [Client.closed](Client.md#clientclosed)
### `BalancedPool.destroyed`
Implements [Client.destroyed](Client.md#clientdestroyed)
### `Pool.stats`
Returns [`PoolStats`](PoolStats.md) instance for this pool.
## Instance Methods
### `BalancedPool.addUpstream(upstream)`
Add an upstream.
Arguments:
* **upstream** `string` - It should only include the **protocol, hostname, and port**.
### `BalancedPool.removeUpstream(upstream)`
Removes an upstream that was previously addded.
### `BalancedPool.close([callback])`
Implements [`Dispatcher.close([callback])`](Dispatcher.md#dispatcherclosecallback-promise).
### `BalancedPool.destroy([error, callback])`
Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
### `BalancedPool.connect(options[, callback])`
See [`Dispatcher.connect(options[, callback])`](Dispatcher.md#dispatcherconnectoptions-callback).
### `BalancedPool.dispatch(options, handlers)`
Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
### `BalancedPool.pipeline(options, handler)`
See [`Dispatcher.pipeline(options, handler)`](Dispatcher.md#dispatcherpipelineoptions-handler).
### `BalancedPool.request(options[, callback])`
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
### `BalancedPool.stream(options, factory[, callback])`
See [`Dispatcher.stream(options, factory[, callback])`](Dispatcher.md#dispatcherstreamoptions-factory-callback).
### `BalancedPool.upgrade(options[, callback])`
See [`Dispatcher.upgrade(options[, callback])`](Dispatcher.md#dispatcherupgradeoptions-callback).
## Instance Events
### Event: `'connect'`
See [Dispatcher Event: `'connect'`](Dispatcher.md#event-connect).
### Event: `'disconnect'`
See [Dispatcher Event: `'disconnect'`](Dispatcher.md#event-disconnect).
### Event: `'drain'`
See [Dispatcher Event: `'drain'`](Dispatcher.md#event-drain).

30
node_modules/undici/docs/api/CacheStorage.md generated vendored Normal file
View File

@ -0,0 +1,30 @@
# CacheStorage
Undici exposes a W3C spec-compliant implementation of [CacheStorage](https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage) and [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache).
## Opening a Cache
Undici exports a top-level CacheStorage instance. You can open a new Cache, or duplicate a Cache with an existing name, by using `CacheStorage.prototype.open`. If you open a Cache with the same name as an already-existing Cache, its list of cached Responses will be shared between both instances.
```mjs
import { caches } from 'undici'
const cache_1 = await caches.open('v1')
const cache_2 = await caches.open('v1')
// Although .open() creates a new instance,
assert(cache_1 !== cache_2)
// The same Response is matched in both.
assert.deepStrictEqual(await cache_1.match('/req'), await cache_2.match('/req'))
```
## Deleting a Cache
If a Cache is deleted, the cached Responses/Requests can still be used.
```mjs
const response = await cache_1.match('/req')
await caches.delete('v1')
await response.text() // the Response's body
```

273
node_modules/undici/docs/api/Client.md generated vendored Normal file
View File

@ -0,0 +1,273 @@
# Class: Client
Extends: `undici.Dispatcher`
A basic HTTP/1.1 client, mapped on top a single TCP/TLS connection. Pipelining is disabled by default.
Requests are not guaranteed to be dispatched in order of invocation.
## `new Client(url[, options])`
Arguments:
* **url** `URL | string` - Should only include the **protocol, hostname, and port**.
* **options** `ClientOptions` (optional)
Returns: `Client`
### Parameter: `ClientOptions`
> ⚠️ Warning: The `H2` support is experimental.
* **bodyTimeout** `number | null` (optional) - Default: `300e3` - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 300 seconds.
* **headersTimeout** `number | null` (optional) - Default: `300e3` - 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.
* **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout`, in milliseconds, when overridden by *keep-alive* hints from the server. Defaults to 10 minutes.
* **keepAliveTimeout** `number | null` (optional) - Default: `4e3` - The timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server. See [MDN: HTTP - Headers - Keep-Alive directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive#directives) for more details. Defaults to 4 seconds.
* **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `1e3` - A number of milliseconds subtracted from server *keep-alive* hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 1 second.
* **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB.
* **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable.
* **pipelining** `number | null` (optional) - Default: `1` - The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections.
* **connect** `ConnectOptions | Function | null` (optional) - Default: `null`.
* **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.
* **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.
* **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.
* **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.
* **allowH2**: `boolean` - Default: `false`. Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.
* **maxConcurrentStreams**: `number` - Default: `100`. Dictates the maximum number of concurrent streams for a single H2 session. It can be overriden by a SETTINGS remote frame.
#### Parameter: `ConnectOptions`
Every Tls option, see [here](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback).
Furthermore, the following options can be passed:
* **socketPath** `string | null` (optional) - Default: `null` - An IPC endpoint, either Unix domain socket or Windows named pipe.
* **maxCachedSessions** `number | null` (optional) - Default: `100` - Maximum number of TLS cached sessions. Use 0 to disable TLS session caching. Default: 100.
* **timeout** `number | null` (optional) - In milliseconds, Default `10e3`.
* **servername** `string | null` (optional)
* **keepAlive** `boolean | null` (optional) - Default: `true` - TCP keep-alive enabled
* **keepAliveInitialDelay** `number | null` (optional) - Default: `60000` - TCP keep-alive interval for the socket in milliseconds
### Example - Basic Client instantiation
This will instantiate the undici Client, but it will not connect to the origin until something is queued. Consider using `client.connect` to prematurely connect to the origin, or just call `client.request`.
```js
'use strict'
import { Client } from 'undici'
const client = new Client('http://localhost:3000')
```
### Example - Custom connector
This will allow you to perform some additional check on the socket that will be used for the next request.
```js
'use strict'
import { Client, buildConnector } from 'undici'
const connector = buildConnector({ rejectUnauthorized: false })
const client = new Client('https://localhost:3000', {
connect (opts, cb) {
connector(opts, (err, socket) => {
if (err) {
cb(err)
} else if (/* assertion */) {
socket.destroy()
cb(new Error('kaboom'))
} else {
cb(null, socket)
}
})
}
})
```
## Instance Methods
### `Client.close([callback])`
Implements [`Dispatcher.close([callback])`](Dispatcher.md#dispatcherclosecallback-promise).
### `Client.destroy([error, callback])`
Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
Waits until socket is closed before invoking the callback (or returning a promise if no callback is provided).
### `Client.connect(options[, callback])`
See [`Dispatcher.connect(options[, callback])`](Dispatcher.md#dispatcherconnectoptions-callback).
### `Client.dispatch(options, handlers)`
Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
### `Client.pipeline(options, handler)`
See [`Dispatcher.pipeline(options, handler)`](Dispatcher.md#dispatcherpipelineoptions-handler).
### `Client.request(options[, callback])`
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
### `Client.stream(options, factory[, callback])`
See [`Dispatcher.stream(options, factory[, callback])`](Dispatcher.md#dispatcherstreamoptions-factory-callback).
### `Client.upgrade(options[, callback])`
See [`Dispatcher.upgrade(options[, callback])`](Dispatcher.md#dispatcherupgradeoptions-callback).
## Instance Properties
### `Client.closed`
* `boolean`
`true` after `client.close()` has been called.
### `Client.destroyed`
* `boolean`
`true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed.
### `Client.pipelining`
* `number`
Property to get and set the pipelining factor.
## Instance Events
### Event: `'connect'`
See [Dispatcher Event: `'connect'`](Dispatcher.md#event-connect).
Parameters:
* **origin** `URL`
* **targets** `Array<Dispatcher>`
Emitted when a socket has been created and connected. The client will connect once `client.size > 0`.
#### Example - Client connect event
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.end('Hello, World!')
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
client.on('connect', (origin) => {
console.log(`Connected to ${origin}`) // should print before the request body statement
})
try {
const { body } = await client.request({
path: '/',
method: 'GET'
})
body.setEncoding('utf-8')
body.on('data', console.log)
client.close()
server.close()
} catch (error) {
console.error(error)
client.close()
server.close()
}
```
### Event: `'disconnect'`
See [Dispatcher Event: `'disconnect'`](Dispatcher.md#event-disconnect).
Parameters:
* **origin** `URL`
* **targets** `Array<Dispatcher>`
* **error** `Error`
Emitted when socket has disconnected. The error argument of the event is the error which caused the socket to disconnect. The client will reconnect if or once `client.size > 0`.
#### Example - Client disconnect event
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.destroy()
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
client.on('disconnect', (origin) => {
console.log(`Disconnected from ${origin}`)
})
try {
await client.request({
path: '/',
method: 'GET'
})
} catch (error) {
console.error(error.message)
client.close()
server.close()
}
```
### Event: `'drain'`
Emitted when pipeline is no longer busy.
See [Dispatcher Event: `'drain'`](Dispatcher.md#event-drain).
#### Example - Client drain event
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.end('Hello, World!')
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
client.on('drain', () => {
console.log('drain event')
client.close()
server.close()
})
const requests = [
client.request({ path: '/', method: 'GET' }),
client.request({ path: '/', method: 'GET' }),
client.request({ path: '/', method: 'GET' })
]
await Promise.all(requests)
console.log('requests completed')
```
### Event: `'error'`
Invoked for users errors such as throwing in the `onError` handler.

115
node_modules/undici/docs/api/Connector.md generated vendored Normal file
View File

@ -0,0 +1,115 @@
# Connector
Undici creates the underlying socket via the connector builder.
Normally, this happens automatically and you don't need to care about this,
but if you need to perform some additional check over the currently used socket,
this is the right place.
If you want to create a custom connector, you must import the `buildConnector` utility.
#### Parameter: `buildConnector.BuildOptions`
Every Tls option, see [here](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback).
Furthermore, the following options can be passed:
* **socketPath** `string | null` (optional) - Default: `null` - An IPC endpoint, either Unix domain socket or Windows named pipe.
* **maxCachedSessions** `number | null` (optional) - Default: `100` - Maximum number of TLS cached sessions. Use 0 to disable TLS session caching. Default: `100`.
* **timeout** `number | null` (optional) - In milliseconds. Default `10e3`.
* **servername** `string | null` (optional)
Once you call `buildConnector`, it will return a connector function, which takes the following parameters.
#### Parameter: `connector.Options`
* **hostname** `string` (required)
* **host** `string` (optional)
* **protocol** `string` (required)
* **port** `string` (required)
* **servername** `string` (optional)
* **localAddress** `string | null` (optional) Local address the socket should connect from.
* **httpSocket** `Socket` (optional) Establish secure connection on a given socket rather than creating a new socket. It can only be sent on TLS update.
### Basic example
```js
'use strict'
import { Client, buildConnector } from 'undici'
const connector = buildConnector({ rejectUnauthorized: false })
const client = new Client('https://localhost:3000', {
connect (opts, cb) {
connector(opts, (err, socket) => {
if (err) {
cb(err)
} else if (/* assertion */) {
socket.destroy()
cb(new Error('kaboom'))
} else {
cb(null, socket)
}
})
}
})
```
### Example: validate the CA fingerprint
```js
'use strict'
import { Client, buildConnector } from 'undici'
const caFingerprint = 'FO:OB:AR'
const connector = buildConnector({ rejectUnauthorized: false })
const client = new Client('https://localhost:3000', {
connect (opts, cb) {
connector(opts, (err, socket) => {
if (err) {
cb(err)
} else if (getIssuerCertificate(socket).fingerprint256 !== caFingerprint) {
socket.destroy()
cb(new Error('Fingerprint does not match or malformed certificate'))
} else {
cb(null, socket)
}
})
}
})
client.request({
path: '/',
method: 'GET'
}, (err, data) => {
if (err) throw err
const bufs = []
data.body.on('data', (buf) => {
bufs.push(buf)
})
data.body.on('end', () => {
console.log(Buffer.concat(bufs).toString('utf8'))
client.close()
})
})
function getIssuerCertificate (socket) {
let certificate = socket.getPeerCertificate(true)
while (certificate && Object.keys(certificate).length > 0) {
// invalid certificate
if (certificate.issuerCertificate == null) {
return null
}
// We have reached the root certificate.
// In case of self-signed certificates, `issuerCertificate` may be a circular reference.
if (certificate.fingerprint256 === certificate.issuerCertificate.fingerprint256) {
break
}
// continue the loop
certificate = certificate.issuerCertificate
}
return certificate
}
```

57
node_modules/undici/docs/api/ContentType.md generated vendored Normal file
View File

@ -0,0 +1,57 @@
# MIME Type Parsing
## `MIMEType` interface
* **type** `string`
* **subtype** `string`
* **parameters** `Map<string, string>`
* **essence** `string`
## `parseMIMEType(input)`
Implements [parse a MIME type](https://mimesniff.spec.whatwg.org/#parse-a-mime-type).
Parses a MIME type, returning its type, subtype, and any associated parameters. If the parser can't parse an input it returns the string literal `'failure'`.
```js
import { parseMIMEType } from 'undici'
parseMIMEType('text/html; charset=gbk')
// {
// type: 'text',
// subtype: 'html',
// parameters: Map(1) { 'charset' => 'gbk' },
// essence: 'text/html'
// }
```
Arguments:
* **input** `string`
Returns: `MIMEType|'failure'`
## `serializeAMimeType(input)`
Implements [serialize a MIME type](https://mimesniff.spec.whatwg.org/#serialize-a-mime-type).
Serializes a MIMEType object.
```js
import { serializeAMimeType } from 'undici'
serializeAMimeType({
type: 'text',
subtype: 'html',
parameters: new Map([['charset', 'gbk']]),
essence: 'text/html'
})
// text/html;charset=gbk
```
Arguments:
* **mimeType** `MIMEType`
Returns: `string`

101
node_modules/undici/docs/api/Cookies.md generated vendored Normal file
View File

@ -0,0 +1,101 @@
# Cookie Handling
## `Cookie` interface
* **name** `string`
* **value** `string`
* **expires** `Date|number` (optional)
* **maxAge** `number` (optional)
* **domain** `string` (optional)
* **path** `string` (optional)
* **secure** `boolean` (optional)
* **httpOnly** `boolean` (optional)
* **sameSite** `'String'|'Lax'|'None'` (optional)
* **unparsed** `string[]` (optional) Left over attributes that weren't parsed.
## `deleteCookie(headers, name[, attributes])`
Sets the expiry time of the cookie to the unix epoch, causing browsers to delete it when received.
```js
import { deleteCookie, Headers } from 'undici'
const headers = new Headers()
deleteCookie(headers, 'name')
console.log(headers.get('set-cookie')) // name=; Expires=Thu, 01 Jan 1970 00:00:00 GMT
```
Arguments:
* **headers** `Headers`
* **name** `string`
* **attributes** `{ path?: string, domain?: string }` (optional)
Returns: `void`
## `getCookies(headers)`
Parses the `Cookie` header and returns a list of attributes and values.
```js
import { getCookies, Headers } from 'undici'
const headers = new Headers({
cookie: 'get=cookies; and=attributes'
})
console.log(getCookies(headers)) // { get: 'cookies', and: 'attributes' }
```
Arguments:
* **headers** `Headers`
Returns: `Record<string, string>`
## `getSetCookies(headers)`
Parses all `Set-Cookie` headers.
```js
import { getSetCookies, Headers } from 'undici'
const headers = new Headers({ 'set-cookie': 'undici=getSetCookies; Secure' })
console.log(getSetCookies(headers))
// [
// {
// name: 'undici',
// value: 'getSetCookies',
// secure: true
// }
// ]
```
Arguments:
* **headers** `Headers`
Returns: `Cookie[]`
## `setCookie(headers, cookie)`
Appends a cookie to the `Set-Cookie` header.
```js
import { setCookie, Headers } from 'undici'
const headers = new Headers()
setCookie(headers, { name: 'undici', value: 'setCookie' })
console.log(headers.get('Set-Cookie')) // undici=setCookie
```
Arguments:
* **headers** `Headers`
* **cookie** `Cookie`
Returns: `void`

204
node_modules/undici/docs/api/DiagnosticsChannel.md generated vendored Normal file
View File

@ -0,0 +1,204 @@
# Diagnostics Channel Support
Stability: Experimental.
Undici supports the [`diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html) (currently available only on Node.js v16+).
It is the preferred way to instrument Undici and retrieve internal information.
The channels available are the following.
## `undici:request:create`
This message is published when a new outgoing request is created.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {
console.log('origin', request.origin)
console.log('completed', request.completed)
console.log('method', request.method)
console.log('path', request.path)
console.log('headers') // raw text, e.g: 'bar: bar\r\n'
request.addHeader('hello', 'world')
console.log('headers', request.headers) // e.g. 'bar: bar\r\nhello: world\r\n'
})
```
Note: a request is only loosely completed to a given socket.
## `undici:request:bodySent`
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:request:bodySent').subscribe(({ request }) => {
// request is the same object undici:request:create
})
```
## `undici:request:headers`
This message is published after the response headers have been received, i.e. the response has been completed.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:request:headers').subscribe(({ request, response }) => {
// request is the same object undici:request:create
console.log('statusCode', response.statusCode)
console.log(response.statusText)
// response.headers are buffers.
console.log(response.headers.map((x) => x.toString()))
})
```
## `undici:request:trailers`
This message is published after the response body and trailers have been received, i.e. the response has been completed.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:request:trailers').subscribe(({ request, trailers }) => {
// request is the same object undici:request:create
console.log('completed', request.completed)
// trailers are buffers.
console.log(trailers.map((x) => x.toString()))
})
```
## `undici:request:error`
This message is published if the request is going to error, but it has not errored yet.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:request:error').subscribe(({ request, error }) => {
// request is the same object undici:request:create
})
```
## `undici:client:sendHeaders`
This message is published right before the first byte of the request is written to the socket.
*Note*: It will publish the exact headers that will be sent to the server in raw format.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:client:sendHeaders').subscribe(({ request, headers, socket }) => {
// request is the same object undici:request:create
console.log(`Full headers list ${headers.split('\r\n')}`);
})
```
## `undici:client:beforeConnect`
This message is published before creating a new connection for **any** request.
You can not assume that this event is related to any specific request.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:client:beforeConnect').subscribe(({ connectParams, connector }) => {
// const { host, hostname, protocol, port, servername } = connectParams
// connector is a function that creates the socket
})
```
## `undici:client:connected`
This message is published after a connection is established.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:client:connected').subscribe(({ socket, connectParams, connector }) => {
// const { host, hostname, protocol, port, servername } = connectParams
// connector is a function that creates the socket
})
```
## `undici:client:connectError`
This message is published if it did not succeed to create new connection
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:client:connectError').subscribe(({ error, socket, connectParams, connector }) => {
// const { host, hostname, protocol, port, servername } = connectParams
// connector is a function that creates the socket
console.log(`Connect failed with ${error.message}`)
})
```
## `undici:websocket:open`
This message is published after the client has successfully connected to a server.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:websocket:open').subscribe(({ address, protocol, extensions }) => {
console.log(address) // address, family, and port
console.log(protocol) // negotiated subprotocols
console.log(extensions) // negotiated extensions
})
```
## `undici:websocket:close`
This message is published after the connection has closed.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:websocket:close').subscribe(({ websocket, code, reason }) => {
console.log(websocket) // the WebSocket object
console.log(code) // the closing status code
console.log(reason) // the closing reason
})
```
## `undici:websocket:socket_error`
This message is published if the socket experiences an error.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:websocket:socket_error').subscribe((error) => {
console.log(error)
})
```
## `undici:websocket:ping`
This message is published after the client receives a ping frame, if the connection is not closing.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:websocket:ping').subscribe(({ payload }) => {
// a Buffer or undefined, containing the optional application data of the frame
console.log(payload)
})
```
## `undici:websocket:pong`
This message is published after the client receives a pong frame.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('undici:websocket:pong').subscribe(({ payload }) => {
// a Buffer or undefined, containing the optional application data of the frame
console.log(payload)
})
```

60
node_modules/undici/docs/api/DispatchInterceptor.md generated vendored Normal file
View File

@ -0,0 +1,60 @@
# Interface: DispatchInterceptor
Extends: `Function`
A function that can be applied to the `Dispatcher.Dispatch` function before it is invoked with a dispatch request.
This allows one to write logic to intercept both the outgoing request, and the incoming response.
### Parameter: `Dispatcher.Dispatch`
The base dispatch function you are decorating.
### ReturnType: `Dispatcher.Dispatch`
A dispatch function that has been altered to provide additional logic
### Basic Example
Here is an example of an interceptor being used to provide a JWT bearer token
```js
'use strict'
const insertHeaderInterceptor = dispatch => {
return function InterceptedDispatch(opts, handler){
opts.headers.push('Authorization', 'Bearer [Some token]')
return dispatch(opts, handler)
}
}
const client = new Client('https://localhost:3000', {
interceptors: { Client: [insertHeaderInterceptor] }
})
```
### Basic Example 2
Here is a contrived example of an interceptor stripping the headers from a response.
```js
'use strict'
const clearHeadersInterceptor = dispatch => {
const { DecoratorHandler } = require('undici')
class ResultInterceptor extends DecoratorHandler {
onHeaders (statusCode, headers, resume) {
return super.onHeaders(statusCode, [], resume)
}
}
return function InterceptedDispatch(opts, handler){
return dispatch(opts, new ResultInterceptor(handler))
}
}
const client = new Client('https://localhost:3000', {
interceptors: { Client: [clearHeadersInterceptor] }
})
```

887
node_modules/undici/docs/api/Dispatcher.md generated vendored Normal file
View File

@ -0,0 +1,887 @@
# Dispatcher
Extends: `events.EventEmitter`
Dispatcher is the core API used to dispatch requests.
Requests are not guaranteed to be dispatched in order of invocation.
## Instance Methods
### `Dispatcher.close([callback]): Promise`
Closes the dispatcher and gracefully waits for enqueued requests to complete before resolving.
Arguments:
* **callback** `(error: Error | null, data: null) => void` (optional)
Returns: `void | Promise<null>` - Only returns a `Promise` if no `callback` argument was passed
```js
dispatcher.close() // -> Promise
dispatcher.close(() => {}) // -> void
```
#### Example - Request resolves before Client closes
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.end('undici')
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
try {
const { body } = await client.request({
path: '/',
method: 'GET'
})
body.setEncoding('utf8')
body.on('data', console.log)
} catch (error) {}
await client.close()
console.log('Client closed')
server.close()
```
### `Dispatcher.connect(options[, callback])`
Starts two-way communications with the requested resource using [HTTP CONNECT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT).
Arguments:
* **options** `ConnectOptions`
* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)
Returns: `void | Promise<ConnectData>` - Only returns a `Promise` if no `callback` argument was passed
#### Parameter: `ConnectOptions`
* **path** `string`
* **headers** `UndiciHeaders` (optional) - Default: `null`
* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`
* **opaque** `unknown` (optional) - This argument parameter is passed through to `ConnectData`
#### Parameter: `ConnectData`
* **statusCode** `number`
* **headers** `Record<string, string | string[] | undefined>`
* **socket** `stream.Duplex`
* **opaque** `unknown`
#### Example - Connect request with echo
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
throw Error('should never get here')
}).listen()
server.on('connect', (req, socket, head) => {
socket.write('HTTP/1.1 200 Connection established\r\n\r\n')
let data = head.toString()
socket.on('data', (buf) => {
data += buf.toString()
})
socket.on('end', () => {
socket.end(data)
})
})
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
try {
const { socket } = await client.connect({
path: '/'
})
const wanted = 'Body'
let data = ''
socket.on('data', d => { data += d })
socket.on('end', () => {
console.log(`Data received: ${data.toString()} | Data wanted: ${wanted}`)
client.close()
server.close()
})
socket.write(wanted)
socket.end()
} catch (error) { }
```
### `Dispatcher.destroy([error, callback]): Promise`
Destroy the dispatcher abruptly with the given error. All the pending and running requests will be asynchronously aborted and error. Since this operation is asynchronously dispatched there might still be some progress on dispatched requests.
Both arguments are optional; the method can be called in four different ways:
Arguments:
* **error** `Error | null` (optional)
* **callback** `(error: Error | null, data: null) => void` (optional)
Returns: `void | Promise<void>` - Only returns a `Promise` if no `callback` argument was passed
```js
dispatcher.destroy() // -> Promise
dispatcher.destroy(new Error()) // -> Promise
dispatcher.destroy(() => {}) // -> void
dispatcher.destroy(new Error(), () => {}) // -> void
```
#### Example - Request is aborted when Client is destroyed
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.end()
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
try {
const request = client.request({
path: '/',
method: 'GET'
})
client.destroy()
.then(() => {
console.log('Client destroyed')
server.close()
})
await request
} catch (error) {
console.error(error)
}
```
### `Dispatcher.dispatch(options, handler)`
This is the low level API which all the preceding APIs are implemented on top of.
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.
Arguments:
* **options** `DispatchOptions`
* **handler** `DispatchHandler`
Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls won't make any progress until the `'drain'` event has been emitted.
#### Parameter: `DispatchOptions`
* **origin** `string | URL`
* **path** `string`
* **method** `string`
* **reset** `boolean` (optional) - Default: `false` - If `false`, the request will attempt to create a long-living connection by sending the `connection: keep-alive` header,otherwise will attempt to close it immediately after response by sending `connection: close` within the request and closing the socket afterwards.
* **body** `string | Buffer | Uint8Array | stream.Readable | Iterable | AsyncIterable | null` (optional) - Default: `null`
* **headers** `UndiciHeaders | string[]` (optional) - Default: `null`.
* **query** `Record<string, any> | null` (optional) - Default: `null` - Query string params to be embedded in the request URL. Note that both keys and values of query are encoded using `encodeURIComponent`. If for some reason you need to send them unencoded, embed query params into path directly instead.
* **idempotent** `boolean` (optional) - Default: `true` if `method` is `'HEAD'` or `'GET'` - Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceding requests in the pipeline has completed.
* **blocking** `boolean` (optional) - Default: `false` - Whether the response is expected to take a long time and would end up blocking the pipeline. When this is set to `true` further pipelining will be avoided on the same connection until headers have been received.
* **upgrade** `string | null` (optional) - Default: `null` - Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`.
* **bodyTimeout** `number | null` (optional) - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 300 seconds.
* **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
#### Parameter: `DispatchHandler`
* **onConnect** `(abort: () => void, context: object) => void` - 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.
* **onError** `(error: Error) => void` - Invoked when an error has occurred. May not throw.
* **onUpgrade** `(statusCode: number, headers: Buffer[], socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`.
* **onHeaders** `(statusCode: number, headers: Buffer[], resume: () => void, statusText: string) => boolean` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests.
* **onData** `(chunk: Buffer) => boolean` - Invoked when response payload data is received. Not required for `upgrade` requests.
* **onComplete** `(trailers: Buffer[]) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
* **onBodySent** `(chunk: string | Buffer | Uint8Array) => void` - Invoked when a body chunk is sent to the server. Not required. For a stream or iterable body this will be invoked for every chunk. For other body types, it will be invoked once after the body is sent.
#### Example 1 - Dispatch GET request
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.end('Hello, World!')
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
const data = []
client.dispatch({
path: '/',
method: 'GET',
headers: {
'x-foo': 'bar'
}
}, {
onConnect: () => {
console.log('Connected!')
},
onError: (error) => {
console.error(error)
},
onHeaders: (statusCode, headers) => {
console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)
},
onData: (chunk) => {
console.log('onData: chunk received')
data.push(chunk)
},
onComplete: (trailers) => {
console.log(`onComplete | trailers: ${trailers}`)
const res = Buffer.concat(data).toString('utf8')
console.log(`Data: ${res}`)
client.close()
server.close()
}
})
```
#### Example 2 - Dispatch Upgrade Request
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.end()
}).listen()
await once(server, 'listening')
server.on('upgrade', (request, socket, head) => {
console.log('Node.js Server - upgrade event')
socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n')
socket.write('Upgrade: WebSocket\r\n')
socket.write('Connection: Upgrade\r\n')
socket.write('\r\n')
socket.end()
})
const client = new Client(`http://localhost:${server.address().port}`)
client.dispatch({
path: '/',
method: 'GET',
upgrade: 'websocket'
}, {
onConnect: () => {
console.log('Undici Client - onConnect')
},
onError: (error) => {
console.log('onError') // shouldn't print
},
onUpgrade: (statusCode, headers, socket) => {
console.log('Undici Client - onUpgrade')
console.log(`onUpgrade Headers: ${headers}`)
socket.on('data', buffer => {
console.log(buffer.toString('utf8'))
})
socket.on('end', () => {
client.close()
server.close()
})
socket.end()
}
})
```
#### Example 3 - Dispatch POST request
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
request.on('data', (data) => {
console.log(`Request Data: ${data.toString('utf8')}`)
const body = JSON.parse(data)
body.message = 'World'
response.end(JSON.stringify(body))
})
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
const data = []
client.dispatch({
path: '/',
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({ message: 'Hello' })
}, {
onConnect: () => {
console.log('Connected!')
},
onError: (error) => {
console.error(error)
},
onHeaders: (statusCode, headers) => {
console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)
},
onData: (chunk) => {
console.log('onData: chunk received')
data.push(chunk)
},
onComplete: (trailers) => {
console.log(`onComplete | trailers: ${trailers}`)
const res = Buffer.concat(data).toString('utf8')
console.log(`Response Data: ${res}`)
client.close()
server.close()
}
})
```
### `Dispatcher.pipeline(options, handler)`
For easy use with [stream.pipeline](https://nodejs.org/api/stream.html#stream_stream_pipeline_source_transforms_destination_callback). The `handler` argument should return a `Readable` from which the result will be read. Usually it should just return the `body` argument unless some kind of transformation needs to be performed based on e.g. `headers` or `statusCode`. The `handler` should validate the response and save any required state. If there is an error, it should be thrown. The function returns a `Duplex` which writes to the request and reads from the response.
Arguments:
* **options** `PipelineOptions`
* **handler** `(data: PipelineHandlerData) => stream.Readable`
Returns: `stream.Duplex`
#### Parameter: PipelineOptions
Extends: [`RequestOptions`](#parameter-requestoptions)
* **objectMode** `boolean` (optional) - Default: `false` - Set to `true` if the `handler` will return an object stream.
#### Parameter: PipelineHandlerData
* **statusCode** `number`
* **headers** `Record<string, string | string[] | undefined>`
* **opaque** `unknown`
* **body** `stream.Readable`
* **context** `object`
* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
#### Example 1 - Pipeline Echo
```js
import { Readable, Writable, PassThrough, pipeline } from 'stream'
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
request.pipe(response)
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
let res = ''
pipeline(
new Readable({
read () {
this.push(Buffer.from('undici'))
this.push(null)
}
}),
client.pipeline({
path: '/',
method: 'GET'
}, ({ statusCode, headers, body }) => {
console.log(`response received ${statusCode}`)
console.log('headers', headers)
return pipeline(body, new PassThrough(), () => {})
}),
new Writable({
write (chunk, _, callback) {
res += chunk.toString()
callback()
},
final (callback) {
console.log(`Response pipelined to writable: ${res}`)
callback()
}
}),
error => {
if (error) {
console.error(error)
}
client.close()
server.close()
}
)
```
### `Dispatcher.request(options[, callback])`
Performs a HTTP request.
Non-idempotent requests will not be pipelined in order
to avoid indirect failures.
Idempotent requests will be automatically retried if
they fail due to indirect failure from the request
at the head of the pipeline. This does not apply to
idempotent requests with a stream request body.
All response bodies must always be fully consumed or destroyed.
Arguments:
* **options** `RequestOptions`
* **callback** `(error: Error | null, data: ResponseData) => void` (optional)
Returns: `void | Promise<ResponseData>` - Only returns a `Promise` if no `callback` argument was passed.
#### Parameter: `RequestOptions`
Extends: [`DispatchOptions`](#parameter-dispatchoptions)
* **opaque** `unknown` (optional) - Default: `null` - Used for passing through context to `ResponseData`.
* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`.
* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
The `RequestOptions.method` property should not be value `'CONNECT'`.
#### Parameter: `ResponseData`
* **statusCode** `number`
* **headers** `Record<string, string | string[]>` - Note that all header keys are lower-cased, e. g. `content-type`.
* **body** `stream.Readable` which also implements [the body mixin from the Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).
* **trailers** `Record<string, string>` - This object starts out
as empty and will be mutated to contain trailers after `body` has emitted `'end'`.
* **opaque** `unknown`
* **context** `object`
`body` contains the following additional [body mixin](https://fetch.spec.whatwg.org/#body-mixin) methods and properties:
- `text()`
- `json()`
- `arrayBuffer()`
- `body`
- `bodyUsed`
`body` can not be consumed twice. For example, calling `text()` after `json()` throws `TypeError`.
`body` contains the following additional extensions:
- `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 262144.
Note that body will still be a `Readable` even if it is empty, but attempting to deserialize it with `json()` will result in an exception. Recommended way to ensure there is a body to deserialize is to check if status code is not 204, and `content-type` header starts with `application/json`.
#### Example 1 - Basic GET Request
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.end('Hello, World!')
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
try {
const { body, headers, statusCode, trailers } = await client.request({
path: '/',
method: 'GET'
})
console.log(`response received ${statusCode}`)
console.log('headers', headers)
body.setEncoding('utf8')
body.on('data', console.log)
body.on('end', () => {
console.log('trailers', trailers)
})
client.close()
server.close()
} catch (error) {
console.error(error)
}
```
#### Example 2 - Aborting a request
> Node.js v15+ is required to run this example
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.end('Hello, World!')
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
const abortController = new AbortController()
try {
client.request({
path: '/',
method: 'GET',
signal: abortController.signal
})
} catch (error) {
console.error(error) // should print an RequestAbortedError
client.close()
server.close()
}
abortController.abort()
```
Alternatively, any `EventEmitter` that emits an `'abort'` event may be used as an abort controller:
```js
import { createServer } from 'http'
import { Client } from 'undici'
import EventEmitter, { once } from 'events'
const server = createServer((request, response) => {
response.end('Hello, World!')
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
const ee = new EventEmitter()
try {
client.request({
path: '/',
method: 'GET',
signal: ee
})
} catch (error) {
console.error(error) // should print an RequestAbortedError
client.close()
server.close()
}
ee.emit('abort')
```
Destroying the request or response body will have the same effect.
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.end('Hello, World!')
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
try {
const { body } = await client.request({
path: '/',
method: 'GET'
})
body.destroy()
} catch (error) {
console.error(error) // should print an RequestAbortedError
client.close()
server.close()
}
```
### `Dispatcher.stream(options, factory[, callback])`
A faster version of `Dispatcher.request`. This method expects the second argument `factory` to return a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream which the response will be written to. This improves performance by avoiding creating an intermediate [`stream.Readable`](https://nodejs.org/api/stream.html#stream_readable_streams) stream when the user expects to directly pipe the response body to a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream.
As demonstrated in [Example 1 - Basic GET stream request](#example-1---basic-get-stream-request), it is recommended to use the `option.opaque` property to avoid creating a closure for the `factory` method. This pattern works well with Node.js Web Frameworks such as [Fastify](https://fastify.io). See [Example 2 - Stream to Fastify Response](#example-2---stream-to-fastify-response) for more details.
Arguments:
* **options** `RequestOptions`
* **factory** `(data: StreamFactoryData) => stream.Writable`
* **callback** `(error: Error | null, data: StreamData) => void` (optional)
Returns: `void | Promise<StreamData>` - Only returns a `Promise` if no `callback` argument was passed
#### Parameter: `StreamFactoryData`
* **statusCode** `number`
* **headers** `Record<string, string | string[] | undefined>`
* **opaque** `unknown`
* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
#### Parameter: `StreamData`
* **opaque** `unknown`
* **trailers** `Record<string, string>`
* **context** `object`
#### Example 1 - Basic GET stream request
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
import { Writable } from 'stream'
const server = createServer((request, response) => {
response.end('Hello, World!')
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
const bufs = []
try {
await client.stream({
path: '/',
method: 'GET',
opaque: { bufs }
}, ({ statusCode, headers, opaque: { bufs } }) => {
console.log(`response received ${statusCode}`)
console.log('headers', headers)
return new Writable({
write (chunk, encoding, callback) {
bufs.push(chunk)
callback()
}
})
})
console.log(Buffer.concat(bufs).toString('utf-8'))
client.close()
server.close()
} catch (error) {
console.error(error)
}
```
#### Example 2 - Stream to Fastify Response
In this example, a (fake) request is made to the fastify server using `fastify.inject()`. This request then executes the fastify route handler which makes a subsequent request to the raw Node.js http server using `undici.dispatcher.stream()`. The fastify response is passed to the `opaque` option so that undici can tap into the underlying writable stream using `response.raw`. This methodology demonstrates how one could use undici and fastify together to create fast-as-possible requests from one backend server to another.
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
import fastify from 'fastify'
const nodeServer = createServer((request, response) => {
response.end('Hello, World! From Node.js HTTP Server')
}).listen()
await once(nodeServer, 'listening')
console.log('Node Server listening')
const nodeServerUndiciClient = new Client(`http://localhost:${nodeServer.address().port}`)
const fastifyServer = fastify()
fastifyServer.route({
url: '/',
method: 'GET',
handler: (request, response) => {
nodeServerUndiciClient.stream({
path: '/',
method: 'GET',
opaque: response
}, ({ opaque }) => opaque.raw)
}
})
await fastifyServer.listen()
console.log('Fastify Server listening')
const fastifyServerUndiciClient = new Client(`http://localhost:${fastifyServer.server.address().port}`)
try {
const { statusCode, body } = await fastifyServerUndiciClient.request({
path: '/',
method: 'GET'
})
console.log(`response received ${statusCode}`)
body.setEncoding('utf8')
body.on('data', console.log)
nodeServerUndiciClient.close()
fastifyServerUndiciClient.close()
fastifyServer.close()
nodeServer.close()
} catch (error) { }
```
### `Dispatcher.upgrade(options[, callback])`
Upgrade to a different protocol. Visit [MDN - HTTP - Protocol upgrade mechanism](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) for more details.
Arguments:
* **options** `UpgradeOptions`
* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)
Returns: `void | Promise<UpgradeData>` - Only returns a `Promise` if no `callback` argument was passed
#### Parameter: `UpgradeOptions`
* **path** `string`
* **method** `string` (optional) - Default: `'GET'`
* **headers** `UndiciHeaders` (optional) - Default: `null`
* **protocol** `string` (optional) - Default: `'Websocket'` - A string of comma separated protocols, in descending preference order.
* **signal** `AbortSignal | EventEmitter | null` (optional) - Default: `null`
#### Parameter: `UpgradeData`
* **headers** `http.IncomingHeaders`
* **socket** `stream.Duplex`
* **opaque** `unknown`
#### Example 1 - Basic Upgrade Request
```js
import { createServer } from 'http'
import { Client } from 'undici'
import { once } from 'events'
const server = createServer((request, response) => {
response.statusCode = 101
response.setHeader('connection', 'upgrade')
response.setHeader('upgrade', request.headers.upgrade)
response.end()
}).listen()
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
try {
const { headers, socket } = await client.upgrade({
path: '/',
})
socket.on('end', () => {
console.log(`upgrade: ${headers.upgrade}`) // upgrade: Websocket
client.close()
server.close()
})
socket.end()
} catch (error) {
console.error(error)
client.close()
server.close()
}
```
## Instance Events
### Event: `'connect'`
Parameters:
* **origin** `URL`
* **targets** `Array<Dispatcher>`
### Event: `'disconnect'`
Parameters:
* **origin** `URL`
* **targets** `Array<Dispatcher>`
* **error** `Error`
### Event: `'connectionError'`
Parameters:
* **origin** `URL`
* **targets** `Array<Dispatcher>`
* **error** `Error`
Emitted when dispatcher fails to connect to
origin.
### Event: `'drain'`
Parameters:
* **origin** `URL`
Emitted when dispatcher is no longer busy.
## Parameter: `UndiciHeaders`
* `Record<string, string | string[] | undefined> | string[] | null`
Header arguments such as `options.headers` in [`Client.dispatch`](Client.md#clientdispatchoptions-handlers) can be specified in two forms; either as an object specified by the `Record<string, string | string[] | undefined>` (`IncomingHttpHeaders`) type, or an array of strings. An array representation of a header list must have an even length or an `InvalidArgumentError` will be thrown.
Keys are lowercase and values are not modified.
Response headers will derive a `host` from the `url` of the [Client](Client.md#class-client) instance if no `host` header was previously specified.
### Example 1 - Object
```js
{
'content-length': '123',
'content-type': 'text/plain',
connection: 'keep-alive',
host: 'mysite.com',
accept: '*/*'
}
```
### Example 2 - Array
```js
[
'content-length', '123',
'content-type', 'text/plain',
'connection', 'keep-alive',
'host', 'mysite.com',
'accept', '*/*'
]
```

47
node_modules/undici/docs/api/Errors.md generated vendored Normal file
View File

@ -0,0 +1,47 @@
# Errors
Undici exposes a variety of error objects that you can use to enhance your error handling.
You can find all the error objects inside the `errors` key.
```js
import { errors } from 'undici'
```
| Error | Error Codes | Description |
| ------------------------------------ | ------------------------------------- | ------------------------------------------------------------------------- |
| `UndiciError` | `UND_ERR` | all errors below are extended from `UndiciError`. |
| `ConnectTimeoutError` | `UND_ERR_CONNECT_TIMEOUT` | socket is destroyed due to connect timeout. |
| `HeadersTimeoutError` | `UND_ERR_HEADERS_TIMEOUT` | socket is destroyed due to headers timeout. |
| `HeadersOverflowError` | `UND_ERR_HEADERS_OVERFLOW` | socket is destroyed due to headers' max size being exceeded. |
| `BodyTimeoutError` | `UND_ERR_BODY_TIMEOUT` | socket is destroyed due to body timeout. |
| `ResponseStatusCodeError` | `UND_ERR_RESPONSE_STATUS_CODE` | an error is thrown when `throwOnError` is `true` for status codes >= 400. |
| `InvalidArgumentError` | `UND_ERR_INVALID_ARG` | passed an invalid argument. |
| `InvalidReturnValueError` | `UND_ERR_INVALID_RETURN_VALUE` | returned an invalid value. |
| `RequestAbortedError` | `UND_ERR_ABORTED` | the request has been aborted by the user |
| `ClientDestroyedError` | `UND_ERR_DESTROYED` | trying to use a destroyed client. |
| `ClientClosedError` | `UND_ERR_CLOSED` | trying to use a closed client. |
| `SocketError` | `UND_ERR_SOCKET` | there is an error with the socket. |
| `NotSupportedError` | `UND_ERR_NOT_SUPPORTED` | encountered unsupported functionality. |
| `RequestContentLengthMismatchError` | `UND_ERR_REQ_CONTENT_LENGTH_MISMATCH` | request body does not match content-length header |
| `ResponseContentLengthMismatchError` | `UND_ERR_RES_CONTENT_LENGTH_MISMATCH` | response body does not match content-length header |
| `InformationalError` | `UND_ERR_INFO` | expected error with reason |
| `ResponseExceededMaxSizeError` | `UND_ERR_RES_EXCEEDED_MAX_SIZE` | response body exceed the max size allowed |
### `SocketError`
The `SocketError` has a `.socket` property which holds socket metadata:
```ts
interface SocketInfo {
localAddress?: string
localPort?: number
remoteAddress?: string
remotePort?: number
remoteFamily?: string
timeout?: number
bytesWritten?: number
bytesRead?: number
}
```
Be aware that in some cases the `.socket` property can be `null`.

27
node_modules/undici/docs/api/Fetch.md generated vendored Normal file
View File

@ -0,0 +1,27 @@
# Fetch
Undici exposes a fetch() method starts the process of fetching a resource from the network.
Documentation and examples can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/fetch).
## File
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/File)
In Node versions v18.13.0 and above and v19.2.0 and above, undici will default to using Node's [File](https://nodejs.org/api/buffer.html#class-file) class. In versions where it's not available, it will default to the undici one.
## FormData
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
## Response
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Response)
## Request
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Request)
## Header
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Headers)

540
node_modules/undici/docs/api/MockAgent.md generated vendored Normal file
View File

@ -0,0 +1,540 @@
# Class: MockAgent
Extends: `undici.Dispatcher`
A mocked Agent class that implements the Agent API. It allows one to intercept HTTP requests made through undici and return mocked responses instead.
## `new MockAgent([options])`
Arguments:
* **options** `MockAgentOptions` (optional) - It extends the `Agent` options.
Returns: `MockAgent`
### Parameter: `MockAgentOptions`
Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)
* **agent** `Agent` (optional) - Default: `new Agent([options])` - a custom agent encapsulated by the MockAgent.
### Example - Basic MockAgent instantiation
This will instantiate the MockAgent. It will not do anything until registered as the agent to use with requests and mock interceptions are added.
```js
import { MockAgent } from 'undici'
const mockAgent = new MockAgent()
```
### Example - Basic MockAgent instantiation with custom agent
```js
import { Agent, MockAgent } from 'undici'
const agent = new Agent()
const mockAgent = new MockAgent({ agent })
```
## Instance Methods
### `MockAgent.get(origin)`
This method creates and retrieves MockPool or MockClient 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.
For subsequent `MockAgent.get` calls on the same origin, the same mock instance will be returned.
Arguments:
* **origin** `string | RegExp | (value) => boolean` - a matcher for the pool origin to be retrieved from the MockAgent.
| Matcher type | Condition to pass |
|:------------:| -------------------------- |
| `string` | Exact match against string |
| `RegExp` | Regex must pass |
| `Function` | Function must return true |
Returns: `MockClient | MockPool`.
| `MockAgentOptions` | Mock instance returned |
| -------------------- | ---------------------- |
| `connections === 1` | `MockClient` |
| `connections` > `1` | `MockPool` |
#### Example - Basic Mocked Request
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
const { statusCode, body } = await request('http://localhost:3000/foo')
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
#### Example - Basic Mocked Request with local mock agent dispatcher
```js
import { MockAgent, request } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
const {
statusCode,
body
} = await request('http://localhost:3000/foo', { dispatcher: mockAgent })
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
#### Example - Basic Mocked Request with local mock pool dispatcher
```js
import { MockAgent, request } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
const {
statusCode,
body
} = await request('http://localhost:3000/foo', { dispatcher: mockPool })
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
#### Example - Basic Mocked Request with local mock client dispatcher
```js
import { MockAgent, request } from 'undici'
const mockAgent = new MockAgent({ connections: 1 })
const mockClient = mockAgent.get('http://localhost:3000')
mockClient.intercept({ path: '/foo' }).reply(200, 'foo')
const {
statusCode,
body
} = await request('http://localhost:3000/foo', { dispatcher: mockClient })
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
#### Example - Basic Mocked requests with multiple intercepts
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
mockPool.intercept({ path: '/hello'}).reply(200, 'hello')
const result1 = await request('http://localhost:3000/foo')
console.log('response received', result1.statusCode) // response received 200
for await (const data of result1.body) {
console.log('data', data.toString('utf8')) // data foo
}
const result2 = await request('http://localhost:3000/hello')
console.log('response received', result2.statusCode) // response received 200
for await (const data of result2.body) {
console.log('data', data.toString('utf8')) // data hello
}
```
#### Example - Mock different requests within the same file
```js
const { MockAgent, setGlobalDispatcher } = require('undici');
const agent = new MockAgent();
agent.disableNetConnect();
setGlobalDispatcher(agent);
describe('Test', () => {
it('200', async () => {
const mockAgent = agent.get('http://test.com');
// your test
});
it('200', async () => {
const mockAgent = agent.get('http://testing.com');
// your test
});
});
```
#### Example - Mocked request with query body, headers and trailers
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo?hello=there&see=ya',
method: 'POST',
body: 'form1=data1&form2=data2'
}).reply(200, { foo: 'bar' }, {
headers: { 'content-type': 'application/json' },
trailers: { 'Content-MD5': 'test' }
})
const {
statusCode,
headers,
trailers,
body
} = await request('http://localhost:3000/foo?hello=there&see=ya', {
method: 'POST',
body: 'form1=data1&form2=data2'
})
console.log('response received', statusCode) // response received 200
console.log('headers', headers) // { 'content-type': 'application/json' }
for await (const data of body) {
console.log('data', data.toString('utf8')) // '{"foo":"bar"}'
}
console.log('trailers', trailers) // { 'content-md5': 'test' }
```
#### Example - Mocked request with origin regex
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get(new RegExp('http://localhost:3000'))
mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
const {
statusCode,
body
} = await request('http://localhost:3000/foo')
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
#### Example - Mocked request with origin function
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get((origin) => origin === 'http://localhost:3000')
mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
const {
statusCode,
body
} = await request('http://localhost:3000/foo')
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
### `MockAgent.close()`
Closes the mock agent and waits for registered mock pools and clients to also close before resolving.
Returns: `Promise<void>`
#### Example - clean up after tests are complete
```js
import { MockAgent, setGlobalDispatcher } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
await mockAgent.close()
```
### `MockAgent.dispatch(options, handlers)`
Implements [`Agent.dispatch(options, handlers)`](Agent.md#parameter-agentdispatchoptions).
### `MockAgent.request(options[, callback])`
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
#### Example - MockAgent request
```js
import { MockAgent } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
const {
statusCode,
body
} = await mockAgent.request({
origin: 'http://localhost:3000',
path: '/foo',
method: 'GET'
})
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
### `MockAgent.deactivate()`
This method disables mocking in MockAgent.
Returns: `void`
#### Example - Deactivate Mocking
```js
import { MockAgent, setGlobalDispatcher } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
mockAgent.deactivate()
```
### `MockAgent.activate()`
This method 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.
Returns: `void`
#### Example - Activate Mocking
```js
import { MockAgent, setGlobalDispatcher } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
mockAgent.deactivate()
// No mocking will occur
// Later
mockAgent.activate()
```
### `MockAgent.enableNetConnect([host])`
When requests are not matched in a MockAgent intercept, a real HTTP request is attempted. We can control this further through the use of `enableNetConnect`. This is achieved by defining host matchers so only matching requests will be attempted.
When using a string, it should only include the **hostname and optionally, the port**. In addition, calling this method multiple times with a string will allow all HTTP requests that match these values.
Arguments:
* **host** `string | RegExp | (value) => boolean` - (optional)
Returns: `void`
#### Example - Allow all non-matching urls to be dispatched in a real HTTP request
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
mockAgent.enableNetConnect()
await request('http://example.com')
// A real request is made
```
#### Example - Allow requests matching a host string to make real requests
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
mockAgent.enableNetConnect('example-1.com')
mockAgent.enableNetConnect('example-2.com:8080')
await request('http://example-1.com')
// A real request is made
await request('http://example-2.com:8080')
// A real request is made
await request('http://example-3.com')
// Will throw
```
#### Example - Allow requests matching a host regex to make real requests
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
mockAgent.enableNetConnect(new RegExp('example.com'))
await request('http://example.com')
// A real request is made
```
#### Example - Allow requests matching a host function to make real requests
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
mockAgent.enableNetConnect((value) => value === 'example.com')
await request('http://example.com')
// A real request is made
```
### `MockAgent.disableNetConnect()`
This method causes all requests to throw when requests are not matched in a MockAgent intercept.
Returns: `void`
#### Example - Disable all non-matching requests by throwing an error for each
```js
import { MockAgent, request } from 'undici'
const mockAgent = new MockAgent()
mockAgent.disableNetConnect()
await request('http://example.com')
// Will throw
```
### `MockAgent.pendingInterceptors()`
This method returns any pending interceptors registered on a mock agent. A pending interceptor meets one of the following criteria:
- Is registered with neither `.times(<number>)` nor `.persist()`, and has not been invoked;
- Is persistent (i.e., registered with `.persist()`) and has not been invoked;
- Is registered with `.times(<number>)` and has not been invoked `<number>` of times.
Returns: `PendingInterceptor[]` (where `PendingInterceptor` is a `MockDispatch` with an additional `origin: string`)
#### Example - List all pending inteceptors
```js
const agent = new MockAgent()
agent.disableNetConnect()
agent
.get('https://example.com')
.intercept({ method: 'GET', path: '/' })
.reply(200)
const pendingInterceptors = agent.pendingInterceptors()
// Returns [
// {
// timesInvoked: 0,
// times: 1,
// persist: false,
// consumed: false,
// pending: true,
// path: '/',
// method: 'GET',
// body: undefined,
// headers: undefined,
// data: {
// error: null,
// statusCode: 200,
// data: '',
// headers: {},
// trailers: {}
// },
// origin: 'https://example.com'
// }
// ]
```
### `MockAgent.assertNoPendingInterceptors([options])`
This method throws if the mock agent has any pending interceptors. A pending interceptor meets one of the following criteria:
- Is registered with neither `.times(<number>)` nor `.persist()`, and has not been invoked;
- Is persistent (i.e., registered with `.persist()`) and has not been invoked;
- Is registered with `.times(<number>)` and has not been invoked `<number>` of times.
#### Example - Check that there are no pending interceptors
```js
const agent = new MockAgent()
agent.disableNetConnect()
agent
.get('https://example.com')
.intercept({ method: 'GET', path: '/' })
.reply(200)
agent.assertNoPendingInterceptors()
// Throws an UndiciError with the following message:
//
// 1 interceptor is pending:
//
// ┌─────────┬────────┬───────────────────────┬──────┬─────────────┬────────────┬─────────────┬───────────┐
// │ (index) │ Method │ Origin │ Path │ Status code │ Persistent │ Invocations │ Remaining │
// ├─────────┼────────┼───────────────────────┼──────┼─────────────┼────────────┼─────────────┼───────────┤
// │ 0 │ 'GET' │ 'https://example.com' │ '/' │ 200 │ '❌' │ 0 │ 1 │
// └─────────┴────────┴───────────────────────┴──────┴─────────────┴────────────┴─────────────┴───────────┘
```

77
node_modules/undici/docs/api/MockClient.md generated vendored Normal file
View File

@ -0,0 +1,77 @@
# Class: MockClient
Extends: `undici.Client`
A mock client class that implements the same api as [MockPool](MockPool.md).
## `new MockClient(origin, [options])`
Arguments:
* **origin** `string` - It should only include the **protocol, hostname, and port**.
* **options** `MockClientOptions` - It extends the `Client` options.
Returns: `MockClient`
### Parameter: `MockClientOptions`
Extends: `ClientOptions`
* **agent** `Agent` - the agent to associate this MockClient with.
### Example - Basic MockClient instantiation
We can use MockAgent to instantiate a MockClient ready to be used to intercept specified requests. It will not do anything until registered as the agent to use and any mock request are registered.
```js
import { MockAgent } from 'undici'
// Connections must be set to 1 to return a MockClient instance
const mockAgent = new MockAgent({ connections: 1 })
const mockClient = mockAgent.get('http://localhost:3000')
```
## Instance Methods
### `MockClient.intercept(options)`
Implements: [`MockPool.intercept(options)`](MockPool.md#mockpoolinterceptoptions)
### `MockClient.close()`
Implements: [`MockPool.close()`](MockPool.md#mockpoolclose)
### `MockClient.dispatch(options, handlers)`
Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
### `MockClient.request(options[, callback])`
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
#### Example - MockClient request
```js
import { MockAgent } from 'undici'
const mockAgent = new MockAgent({ connections: 1 })
const mockClient = mockAgent.get('http://localhost:3000')
mockClient.intercept({ path: '/foo' }).reply(200, 'foo')
const {
statusCode,
body
} = await mockClient.request({
origin: 'http://localhost:3000',
path: '/foo',
method: 'GET'
})
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```

12
node_modules/undici/docs/api/MockErrors.md generated vendored Normal file
View File

@ -0,0 +1,12 @@
# MockErrors
Undici exposes a variety of mock error objects that you can use to enhance your mock error handling.
You can find all the mock error objects inside the `mockErrors` key.
```js
import { mockErrors } from 'undici'
```
| Mock Error | Mock Error Codes | Description |
| --------------------- | ------------------------------- | ---------------------------------------------------------- |
| `MockNotMatchedError` | `UND_MOCK_ERR_MOCK_NOT_MATCHED` | The request does not match any registered mock dispatches. |

513
node_modules/undici/docs/api/MockPool.md generated vendored Normal file
View File

@ -0,0 +1,513 @@
# Class: MockPool
Extends: `undici.Pool`
A mock Pool class that implements the Pool API and is used by MockAgent to intercept real requests and return mocked responses.
## `new MockPool(origin, [options])`
Arguments:
* **origin** `string` - It should only include the **protocol, hostname, and port**.
* **options** `MockPoolOptions` - It extends the `Pool` options.
Returns: `MockPool`
### Parameter: `MockPoolOptions`
Extends: `PoolOptions`
* **agent** `Agent` - the agent to associate this MockPool with.
### Example - Basic MockPool instantiation
We can use MockAgent to instantiate a MockPool ready to be used to intercept specified requests. It will not do anything until registered as the agent to use and any mock request are registered.
```js
import { MockAgent } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
```
## Instance Methods
### `MockPool.intercept(options)`
This method defines the interception rules for matching against requests for a MockPool or MockPool. We can intercept multiple times on a single instance, but each intercept is only used once.
For example if you expect to make 2 requests inside a test, you need to call `intercept()` twice. Assuming you use `disableNetConnect()` you will get `MockNotMatchedError` on the second request when you only call `intercept()` once.
When defining interception rules, all the rules must pass for a request to be intercepted. If a request is not intercepted, a real request will be attempted.
| Matcher type | Condition to pass |
|:------------:| -------------------------- |
| `string` | Exact match against string |
| `RegExp` | Regex must pass |
| `Function` | Function must return true |
Arguments:
* **options** `MockPoolInterceptOptions` - Interception options.
Returns: `MockInterceptor` corresponding to the input options.
### Parameter: `MockPoolInterceptOptions`
* **path** `string | RegExp | (path: string) => boolean` - a matcher for the HTTP request path.
* **method** `string | RegExp | (method: string) => boolean` - (optional) - a matcher for the HTTP request method. Defaults to `GET`.
* **body** `string | RegExp | (body: string) => boolean` - (optional) - a matcher for the HTTP request body.
* **headers** `Record<string, string | RegExp | (body: string) => boolean`> - (optional) - a matcher for the HTTP request headers. To be intercepted, a request must match all defined headers. Extra headers not defined here may (or may not) be included in the request and do not affect the interception in any way.
* **query** `Record<string, any> | null` - (optional) - a matcher for the HTTP request query string params.
### Return: `MockInterceptor`
We can define the behaviour of an intercepted request with the following options.
* **reply** `(statusCode: number, replyData: string | Buffer | object | MockInterceptor.MockResponseDataHandler, responseOptions?: MockResponseOptions) => MockScope` - define a reply for a matching request. You can define the replyData as a callback to read incoming request data. Default for `responseOptions` is `{}`.
* **reply** `(callback: MockInterceptor.MockReplyOptionsCallback) => MockScope` - define a reply for a matching request, allowing dynamic mocking of all reply options rather than just the data.
* **replyWithError** `(error: Error) => MockScope` - define an error for a matching request to throw.
* **defaultReplyHeaders** `(headers: Record<string, string>) => MockInterceptor` - define default headers to be included in subsequent replies. These are in addition to headers on a specific reply.
* **defaultReplyTrailers** `(trailers: Record<string, string>) => MockInterceptor` - define default trailers to be included in subsequent replies. These are in addition to trailers on a specific reply.
* **replyContentLength** `() => MockInterceptor` - define automatically calculated `content-length` headers to be included in subsequent replies.
The reply data of an intercepted request may either be a string, buffer, or JavaScript object. Objects are converted to JSON while strings and buffers are sent as-is.
By default, `reply` and `replyWithError` define the behaviour for the first matching request only. Subsequent requests will not be affected (this can be changed using the returned `MockScope`).
### Parameter: `MockResponseOptions`
* **headers** `Record<string, string>` - headers to be included on the mocked reply.
* **trailers** `Record<string, string>` - trailers to be included on the mocked reply.
### Return: `MockScope`
A `MockScope` is associated with a single `MockInterceptor`. With this, we can configure the default behaviour of a intercepted reply.
* **delay** `(waitInMs: number) => MockScope` - delay the associated reply by a set amount in ms.
* **persist** `() => MockScope` - any matching request will always reply with the defined response indefinitely.
* **times** `(repeatTimes: number) => MockScope` - any matching request will reply with the defined response a fixed amount of times. This is overridden by **persist**.
#### Example - Basic Mocked Request
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
// MockPool
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
const {
statusCode,
body
} = await request('http://localhost:3000/foo')
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
#### Example - Mocked request using reply data callbacks
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/echo',
method: 'GET',
headers: {
'User-Agent': 'undici',
Host: 'example.com'
}
}).reply(200, ({ headers }) => ({ message: headers.get('message') }))
const { statusCode, body, headers } = await request('http://localhost:3000', {
headers: {
message: 'hello world!'
}
})
console.log('response received', statusCode) // response received 200
console.log('headers', headers) // { 'content-type': 'application/json' }
for await (const data of body) {
console.log('data', data.toString('utf8')) // { "message":"hello world!" }
}
```
#### Example - Mocked request using reply options callback
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/echo',
method: 'GET',
headers: {
'User-Agent': 'undici',
Host: 'example.com'
}
}).reply(({ headers }) => ({ statusCode: 200, data: { message: headers.get('message') }})))
const { statusCode, body, headers } = await request('http://localhost:3000', {
headers: {
message: 'hello world!'
}
})
console.log('response received', statusCode) // response received 200
console.log('headers', headers) // { 'content-type': 'application/json' }
for await (const data of body) {
console.log('data', data.toString('utf8')) // { "message":"hello world!" }
}
```
#### Example - Basic Mocked requests with multiple intercepts
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: 'GET'
}).reply(200, 'foo')
mockPool.intercept({
path: '/hello',
method: 'GET',
}).reply(200, 'hello')
const result1 = await request('http://localhost:3000/foo')
console.log('response received', result1.statusCode) // response received 200
for await (const data of result1.body) {
console.log('data', data.toString('utf8')) // data foo
}
const result2 = await request('http://localhost:3000/hello')
console.log('response received', result2.statusCode) // response received 200
for await (const data of result2.body) {
console.log('data', data.toString('utf8')) // data hello
}
```
#### Example - Mocked request with query body, request headers and response headers and trailers
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo?hello=there&see=ya',
method: 'POST',
body: 'form1=data1&form2=data2',
headers: {
'User-Agent': 'undici',
Host: 'example.com'
}
}).reply(200, { foo: 'bar' }, {
headers: { 'content-type': 'application/json' },
trailers: { 'Content-MD5': 'test' }
})
const {
statusCode,
headers,
trailers,
body
} = await request('http://localhost:3000/foo?hello=there&see=ya', {
method: 'POST',
body: 'form1=data1&form2=data2',
headers: {
foo: 'bar',
'User-Agent': 'undici',
Host: 'example.com'
}
})
console.log('response received', statusCode) // response received 200
console.log('headers', headers) // { 'content-type': 'application/json' }
for await (const data of body) {
console.log('data', data.toString('utf8')) // '{"foo":"bar"}'
}
console.log('trailers', trailers) // { 'content-md5': 'test' }
```
#### Example - Mocked request using different matchers
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: /^GET$/,
body: (value) => value === 'form=data',
headers: {
'User-Agent': 'undici',
Host: /^example.com$/
}
}).reply(200, 'foo')
const {
statusCode,
body
} = await request('http://localhost:3000/foo', {
method: 'GET',
body: 'form=data',
headers: {
foo: 'bar',
'User-Agent': 'undici',
Host: 'example.com'
}
})
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
#### Example - Mocked request with reply with a defined error
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: 'GET'
}).replyWithError(new Error('kaboom'))
try {
await request('http://localhost:3000/foo', {
method: 'GET'
})
} catch (error) {
console.error(error) // Error: kaboom
}
```
#### Example - Mocked request with defaultReplyHeaders
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: 'GET'
}).defaultReplyHeaders({ foo: 'bar' })
.reply(200, 'foo')
const { headers } = await request('http://localhost:3000/foo')
console.log('headers', headers) // headers { foo: 'bar' }
```
#### Example - Mocked request with defaultReplyTrailers
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: 'GET'
}).defaultReplyTrailers({ foo: 'bar' })
.reply(200, 'foo')
const { trailers } = await request('http://localhost:3000/foo')
console.log('trailers', trailers) // trailers { foo: 'bar' }
```
#### Example - Mocked request with automatic content-length calculation
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: 'GET'
}).replyContentLength().reply(200, 'foo')
const { headers } = await request('http://localhost:3000/foo')
console.log('headers', headers) // headers { 'content-length': '3' }
```
#### Example - Mocked request with automatic content-length calculation on an object
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: 'GET'
}).replyContentLength().reply(200, { foo: 'bar' })
const { headers } = await request('http://localhost:3000/foo')
console.log('headers', headers) // headers { 'content-length': '13' }
```
#### Example - Mocked request with persist enabled
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: 'GET'
}).reply(200, 'foo').persist()
const result1 = await request('http://localhost:3000/foo')
// Will match and return mocked data
const result2 = await request('http://localhost:3000/foo')
// Will match and return mocked data
// Etc
```
#### Example - Mocked request with times enabled
```js
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: 'GET'
}).reply(200, 'foo').times(2)
const result1 = await request('http://localhost:3000/foo')
// Will match and return mocked data
const result2 = await request('http://localhost:3000/foo')
// Will match and return mocked data
const result3 = await request('http://localhost:3000/foo')
// Will not match and make attempt a real request
```
### `MockPool.close()`
Closes the mock pool and de-registers from associated MockAgent.
Returns: `Promise<void>`
#### Example - clean up after tests are complete
```js
import { MockAgent } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
await mockPool.close()
```
### `MockPool.dispatch(options, handlers)`
Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
### `MockPool.request(options[, callback])`
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
#### Example - MockPool request
```js
import { MockAgent } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: 'GET',
}).reply(200, 'foo')
const {
statusCode,
body
} = await mockPool.request({
origin: 'http://localhost:3000',
path: '/foo',
method: 'GET'
})
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```

84
node_modules/undici/docs/api/Pool.md generated vendored Normal file
View File

@ -0,0 +1,84 @@
# Class: Pool
Extends: `undici.Dispatcher`
A pool of [Client](Client.md) instances connected to the same upstream target.
Requests are not guaranteed to be dispatched in order of invocation.
## `new Pool(url[, options])`
Arguments:
* **url** `URL | string` - It should only include the **protocol, hostname, and port**.
* **options** `PoolOptions` (optional)
### Parameter: `PoolOptions`
Extends: [`ClientOptions`](Client.md#parameter-clientoptions)
* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Client(origin, opts)`
* **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).
## Instance Properties
### `Pool.closed`
Implements [Client.closed](Client.md#clientclosed)
### `Pool.destroyed`
Implements [Client.destroyed](Client.md#clientdestroyed)
### `Pool.stats`
Returns [`PoolStats`](PoolStats.md) instance for this pool.
## Instance Methods
### `Pool.close([callback])`
Implements [`Dispatcher.close([callback])`](Dispatcher.md#dispatcherclosecallback-promise).
### `Pool.destroy([error, callback])`
Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
### `Pool.connect(options[, callback])`
See [`Dispatcher.connect(options[, callback])`](Dispatcher.md#dispatcherconnectoptions-callback).
### `Pool.dispatch(options, handler)`
Implements [`Dispatcher.dispatch(options, handler)`](Dispatcher.md#dispatcherdispatchoptions-handler).
### `Pool.pipeline(options, handler)`
See [`Dispatcher.pipeline(options, handler)`](Dispatcher.md#dispatcherpipelineoptions-handler).
### `Pool.request(options[, callback])`
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
### `Pool.stream(options, factory[, callback])`
See [`Dispatcher.stream(options, factory[, callback])`](Dispatcher.md#dispatcherstreamoptions-factory-callback).
### `Pool.upgrade(options[, callback])`
See [`Dispatcher.upgrade(options[, callback])`](Dispatcher.md#dispatcherupgradeoptions-callback).
## Instance Events
### Event: `'connect'`
See [Dispatcher Event: `'connect'`](Dispatcher.md#event-connect).
### Event: `'disconnect'`
See [Dispatcher Event: `'disconnect'`](Dispatcher.md#event-disconnect).
### Event: `'drain'`
See [Dispatcher Event: `'drain'`](Dispatcher.md#event-drain).

35
node_modules/undici/docs/api/PoolStats.md generated vendored Normal file
View File

@ -0,0 +1,35 @@
# Class: PoolStats
Aggregate stats for a [Pool](Pool.md) or [BalancedPool](BalancedPool.md).
## `new PoolStats(pool)`
Arguments:
* **pool** `Pool` - Pool or BalancedPool from which to return stats.
## Instance Properties
### `PoolStats.connected`
Number of open socket connections in this pool.
### `PoolStats.free`
Number of open socket connections in this pool that do not have an active request.
### `PoolStats.pending`
Number of pending requests across all clients in this pool.
### `PoolStats.queued`
Number of queued requests across all clients in this pool.
### `PoolStats.running`
Number of currently active requests across all clients in this pool.
### `PoolStats.size`
Number of active, pending, or queued requests across all clients in this pool.

126
node_modules/undici/docs/api/ProxyAgent.md generated vendored Normal file
View File

@ -0,0 +1,126 @@
# Class: ProxyAgent
Extends: `undici.Dispatcher`
A Proxy Agent class that implements the Agent API. It allows the connection through proxy in a simple way.
## `new ProxyAgent([options])`
Arguments:
* **options** `ProxyAgentOptions` (required) - It extends the `Agent` options.
Returns: `ProxyAgent`
### Parameter: `ProxyAgentOptions`
Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)
* **uri** `string` (required) - It can be passed either by a string or a object containing `uri` as string.
* **token** `string` (optional) - It can be passed by a string of token for authentication.
* **auth** `string` (**deprecated**) - Use token.
* **clientFactory** `(origin: URL, opts: Object) => Dispatcher` (optional) - Default: `(origin, opts) => new Pool(origin, opts)`
* **requestTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the request. See [TLS](https://nodejs.org/api/tls.html#tlsconnectoptions-callback).
* **proxyTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the proxy server. See [TLS](https://nodejs.org/api/tls.html#tlsconnectoptions-callback).
Examples:
```js
import { ProxyAgent } from 'undici'
const proxyAgent = new ProxyAgent('my.proxy.server')
// or
const proxyAgent = new ProxyAgent({ uri: 'my.proxy.server' })
```
#### Example - Basic ProxyAgent instantiation
This will instantiate the ProxyAgent. It will not do anything until registered as the agent to use with requests.
```js
import { ProxyAgent } from 'undici'
const proxyAgent = new ProxyAgent('my.proxy.server')
```
#### Example - Basic Proxy Request with global agent dispatcher
```js
import { setGlobalDispatcher, request, ProxyAgent } from 'undici'
const proxyAgent = new ProxyAgent('my.proxy.server')
setGlobalDispatcher(proxyAgent)
const { statusCode, body } = await request('http://localhost:3000/foo')
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
#### Example - Basic Proxy Request with local agent dispatcher
```js
import { ProxyAgent, request } from 'undici'
const proxyAgent = new ProxyAgent('my.proxy.server')
const {
statusCode,
body
} = await request('http://localhost:3000/foo', { dispatcher: proxyAgent })
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
```
#### Example - Basic Proxy Request with authentication
```js
import { setGlobalDispatcher, request, ProxyAgent } from 'undici';
const proxyAgent = new ProxyAgent({
uri: 'my.proxy.server',
// token: 'Bearer xxxx'
token: `Basic ${Buffer.from('username:password').toString('base64')}`
});
setGlobalDispatcher(proxyAgent);
const { statusCode, body } = await request('http://localhost:3000/foo');
console.log('response received', statusCode); // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')); // data foo
}
```
### `ProxyAgent.close()`
Closes the proxy agent and waits for registered pools and clients to also close before resolving.
Returns: `Promise<void>`
#### Example - clean up after tests are complete
```js
import { ProxyAgent, setGlobalDispatcher } from 'undici'
const proxyAgent = new ProxyAgent('my.proxy.server')
setGlobalDispatcher(proxyAgent)
await proxyAgent.close()
```
### `ProxyAgent.dispatch(options, handlers)`
Implements [`Agent.dispatch(options, handlers)`](Agent.md#parameter-agentdispatchoptions).
### `ProxyAgent.request(options[, callback])`
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).

43
node_modules/undici/docs/api/WebSocket.md generated vendored Normal file
View File

@ -0,0 +1,43 @@
# Class: WebSocket
> ⚠️ Warning: the WebSocket API is experimental.
Extends: [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
The WebSocket object provides a way to manage a WebSocket connection to a server, allowing bidirectional communication. The API follows the [WebSocket spec](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) and [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455).
## `new WebSocket(url[, protocol])`
Arguments:
* **url** `URL | string` - The url's protocol *must* be `ws` or `wss`.
* **protocol** `string | string[] | WebSocketInit` (optional) - Subprotocol(s) to request the server use, or a [`Dispatcher`](./Dispatcher.md).
### Example:
This example will not work in browsers or other platforms that don't allow passing an object.
```mjs
import { WebSocket, ProxyAgent } from 'undici'
const proxyAgent = new ProxyAgent('my.proxy.server')
const ws = new WebSocket('wss://echo.websocket.events', {
dispatcher: proxyAgent,
protocols: ['echo', 'chat']
})
```
If you do not need a custom Dispatcher, it's recommended to use the following pattern:
```mjs
import { WebSocket } from 'undici'
const ws = new WebSocket('wss://echo.websocket.events', ['echo', 'chat'])
```
## Read More
- [MDN - WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
- [The WebSocket Specification](https://www.rfc-editor.org/rfc/rfc6455)
- [The WHATWG WebSocket Specification](https://websockets.spec.whatwg.org/)

62
node_modules/undici/docs/api/api-lifecycle.md generated vendored Normal file
View File

@ -0,0 +1,62 @@
# Client Lifecycle
An Undici [Client](Client.md) can be best described as a state machine. The following list is a summary of the various state transitions the `Client` will go through in its lifecycle. This document also contains detailed breakdowns of each state.
> This diagram is not a perfect representation of the undici Client. Since the Client class is not actually implemented as a state-machine, actual execution may deviate slightly from what is described below. Consider this as a general resource for understanding the inner workings of the Undici client rather than some kind of formal specification.
## State Transition Overview
* A `Client` begins in the **idle** state with no socket connection and no requests in queue.
* The *connect* event transitions the `Client` to the **pending** state where requests can be queued prior to processing.
* The *close* and *destroy* events transition the `Client` to the **destroyed** state. Since there are no requests in the queue, the *close* event immediately transitions to the **destroyed** state.
* The **pending** state indicates the underlying socket connection has been successfully established and requests are queueing.
* The *process* event transitions the `Client` to the **processing** state where requests are processed.
* If requests are queued, the *close* event transitions to the **processing** state; otherwise, it transitions to the **destroyed** state.
* The *destroy* event transitions to the **destroyed** state.
* The **processing** state initializes to the **processing.running** state.
* If the current request requires draining, the *needDrain* event transitions the `Client` into the **processing.busy** state which will return to the **processing.running** state with the *drainComplete* event.
* After all queued requests are completed, the *keepalive* event transitions the `Client` back to the **pending** state. If no requests are queued during the timeout, the **close** event transitions the `Client` to the **destroyed** state.
* If the *close* event is fired while the `Client` still has queued requests, the `Client` transitions to the **process.closing** state where it will complete all existing requests before firing the *done* event.
* The *done* event gracefully transitions the `Client` to the **destroyed** state.
* At any point in time, the *destroy* event will transition the `Client` from the **processing** state to the **destroyed** state, destroying any queued requests.
* The **destroyed** state is a final state and the `Client` is no longer functional.
![A state diagram representing an Undici Client instance](../assets/lifecycle-diagram.png)
> The diagram was generated using Mermaid.js Live Editor. Modify the state diagram [here](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoic3RhdGVEaWFncmFtLXYyXG4gICAgWypdIC0tPiBpZGxlXG4gICAgaWRsZSAtLT4gcGVuZGluZyA6IGNvbm5lY3RcbiAgICBpZGxlIC0tPiBkZXN0cm95ZWQgOiBkZXN0cm95L2Nsb3NlXG4gICAgXG4gICAgcGVuZGluZyAtLT4gaWRsZSA6IHRpbWVvdXRcbiAgICBwZW5kaW5nIC0tPiBkZXN0cm95ZWQgOiBkZXN0cm95XG5cbiAgICBzdGF0ZSBjbG9zZV9mb3JrIDw8Zm9yaz4-XG4gICAgcGVuZGluZyAtLT4gY2xvc2VfZm9yayA6IGNsb3NlXG4gICAgY2xvc2VfZm9yayAtLT4gcHJvY2Vzc2luZ1xuICAgIGNsb3NlX2ZvcmsgLS0-IGRlc3Ryb3llZFxuXG4gICAgcGVuZGluZyAtLT4gcHJvY2Vzc2luZyA6IHByb2Nlc3NcblxuICAgIHByb2Nlc3NpbmcgLS0-IHBlbmRpbmcgOiBrZWVwYWxpdmVcbiAgICBwcm9jZXNzaW5nIC0tPiBkZXN0cm95ZWQgOiBkb25lXG4gICAgcHJvY2Vzc2luZyAtLT4gZGVzdHJveWVkIDogZGVzdHJveVxuXG4gICAgc3RhdGUgcHJvY2Vzc2luZyB7XG4gICAgICAgIHJ1bm5pbmcgLS0-IGJ1c3kgOiBuZWVkRHJhaW5cbiAgICAgICAgYnVzeSAtLT4gcnVubmluZyA6IGRyYWluQ29tcGxldGVcbiAgICAgICAgcnVubmluZyAtLT4gWypdIDoga2VlcGFsaXZlXG4gICAgICAgIHJ1bm5pbmcgLS0-IGNsb3NpbmcgOiBjbG9zZVxuICAgICAgICBjbG9zaW5nIC0tPiBbKl0gOiBkb25lXG4gICAgICAgIFsqXSAtLT4gcnVubmluZ1xuICAgIH1cbiAgICAiLCJtZXJtYWlkIjp7InRoZW1lIjoiYmFzZSJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ)
## State details
### idle
The **idle** state is the initial state of a `Client` instance. While an `origin` is required for instantiating a `Client` instance, the underlying socket connection will not be established until a request is queued using [`Client.dispatch()`](Client.md#clientdispatchoptions-handlers). By calling `Client.dispatch()` directly or using one of the multiple implementations ([`Client.connect()`](Client.md#clientconnectoptions-callback), [`Client.pipeline()`](Client.md#clientpipelineoptions-handler), [`Client.request()`](Client.md#clientrequestoptions-callback), [`Client.stream()`](Client.md#clientstreamoptions-factory-callback), and [`Client.upgrade()`](Client.md#clientupgradeoptions-callback)), the `Client` instance will transition from **idle** to [**pending**](#pending) and then most likely directly to [**processing**](#processing).
Calling [`Client.close()`](Client.md#clientclosecallback) or [`Client.destroy()`](Client.md#clientdestroyerror-callback) transitions directly to the [**destroyed**](#destroyed) state since the `Client` instance will have no queued requests in this state.
### pending
The **pending** state signifies a non-processing `Client`. Upon entering this state, the `Client` establishes a socket connection and emits the [`'connect'`](Client.md#event-connect) event signalling a connection was successfully established with the `origin` provided during `Client` instantiation. The internal queue is initially empty, and requests can start queueing.
Calling [`Client.close()`](Client.md#clientclosecallback) with queued requests, transitions the `Client` to the [**processing**](#processing) state. Without queued requests, it transitions to the [**destroyed**](#destroyed) state.
Calling [`Client.destroy()`](Client.md#clientdestroyerror-callback) transitions directly to the [**destroyed**](#destroyed) state regardless of existing requests.
### processing
The **processing** state is a state machine within itself. It initializes to the [**processing.running**](#running) state. The [`Client.dispatch()`](Client.md#clientdispatchoptions-handlers), [`Client.close()`](Client.md#clientclosecallback), and [`Client.destroy()`](Client.md#clientdestroyerror-callback) can be called at any time while the `Client` is in this state. `Client.dispatch()` will add more requests to the queue while existing requests continue to be processed. `Client.close()` will transition to the [**processing.closing**](#closing) state. And `Client.destroy()` will transition to [**destroyed**](#destroyed).
#### running
In the **processing.running** sub-state, queued requests are being processed in a FIFO order. If a request body requires draining, the *needDrain* event transitions to the [**processing.busy**](#busy) sub-state. The *close* event transitions the Client to the [**process.closing**](#closing) sub-state. If all queued requests are processed and neither [`Client.close()`](Client.md#clientclosecallback) nor [`Client.destroy()`](Client.md#clientdestroyerror-callback) are called, then the [**processing**](#processing) machine will trigger a *keepalive* event transitioning the `Client` back to the [**pending**](#pending) state. During this time, the `Client` is waiting for the socket connection to timeout, and once it does, it triggers the *timeout* event and transitions to the [**idle**](#idle) state.
#### busy
This sub-state is only entered when a request body is an instance of [Stream](https://nodejs.org/api/stream.html) and requires draining. The `Client` cannot process additional requests while in this state and must wait until the currently processing request body is completely drained before transitioning back to [**processing.running**](#running).
#### closing
This sub-state is only entered when a `Client` instance has queued requests and the [`Client.close()`](Client.md#clientclosecallback) method is called. In this state, the `Client` instance continues to process requests as usual, with the one exception that no additional requests can be queued. Once all of the queued requests are processed, the `Client` will trigger the *done* event gracefully entering the [**destroyed**](#destroyed) state without an error.
### destroyed
The **destroyed** state is a final state for the `Client` instance. Once in this state, a `Client` is nonfunctional. Calling any other `Client` methods will result in an `ClientDestroyedError`.

BIN
node_modules/undici/docs/assets/lifecycle-diagram.png generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -0,0 +1,64 @@
# Client certificate
Client certificate authentication can be configured with the `Client`, the required options are passed along through the `connect` option.
The client certificates must be signed by a trusted CA. The Node.js default is to trust the well-known CAs curated by Mozilla.
Setting the server option `requestCert: true` tells the server to request the client certificate.
The server option `rejectUnauthorized: false` allows us to handle any invalid certificate errors in client code. The `authorized` property on the socket of the incoming request will show if the client certificate was valid. The `authorizationError` property will give the reason if the certificate was not valid.
### Client Certificate Authentication
```js
const { readFileSync } = require('fs')
const { join } = require('path')
const { createServer } = require('https')
const { Client } = require('undici')
const serverOptions = {
ca: [
readFileSync(join(__dirname, 'client-ca-crt.pem'), 'utf8')
],
key: readFileSync(join(__dirname, 'server-key.pem'), 'utf8'),
cert: readFileSync(join(__dirname, 'server-crt.pem'), 'utf8'),
requestCert: true,
rejectUnauthorized: false
}
const server = createServer(serverOptions, (req, res) => {
// true if client cert is valid
if(req.client.authorized === true) {
console.log('valid')
} else {
console.error(req.client.authorizationError)
}
res.end()
})
server.listen(0, function () {
const tls = {
ca: [
readFileSync(join(__dirname, 'server-ca-crt.pem'), 'utf8')
],
key: readFileSync(join(__dirname, 'client-key.pem'), 'utf8'),
cert: readFileSync(join(__dirname, 'client-crt.pem'), 'utf8'),
rejectUnauthorized: false,
servername: 'agent1'
}
const client = new Client(`https://localhost:${server.address().port}`, {
connect: tls
})
client.request({
path: '/',
method: 'GET'
}, (err, { body }) => {
body.on('data', (buf) => {})
body.on('end', () => {
client.close()
server.close()
})
})
})
```

View File

@ -0,0 +1,136 @@
# Mocking Request
Undici has its own mocking [utility](../api/MockAgent.md). It allow us to intercept undici HTTP requests and return mocked values instead. It can be useful for testing purposes.
Example:
```js
// bank.mjs
import { request } from 'undici'
export async function bankTransfer(recipient, amount) {
const { body } = await request('http://localhost:3000/bank-transfer',
{
method: 'POST',
headers: {
'X-TOKEN-SECRET': 'SuperSecretToken',
},
body: JSON.stringify({
recipient,
amount
})
}
)
return await body.json()
}
```
And this is what the test file looks like:
```js
// index.test.mjs
import { strict as assert } from 'assert'
import { MockAgent, setGlobalDispatcher, } from 'undici'
import { bankTransfer } from './bank.mjs'
const mockAgent = new MockAgent();
setGlobalDispatcher(mockAgent);
// Provide the base url to the request
const mockPool = mockAgent.get('http://localhost:3000');
// intercept the request
mockPool.intercept({
path: '/bank-transfer',
method: 'POST',
headers: {
'X-TOKEN-SECRET': 'SuperSecretToken',
},
body: JSON.stringify({
recipient: '1234567890',
amount: '100'
})
}).reply(200, {
message: 'transaction processed'
})
const success = await bankTransfer('1234567890', '100')
assert.deepEqual(success, { message: 'transaction processed' })
// if you dont want to check whether the body or the headers contain the same value
// just remove it from interceptor
mockPool.intercept({
path: '/bank-transfer',
method: 'POST',
}).reply(400, {
message: 'bank account not found'
})
const badRequest = await bankTransfer('1234567890', '100')
assert.deepEqual(badRequest, { message: 'bank account not found' })
```
Explore other MockAgent functionality [here](../api/MockAgent.md)
## Debug Mock Value
When the interceptor and the request options are not the same, undici will automatically make a real HTTP request. To prevent real requests from being made, use `mockAgent.disableNetConnect()`:
```js
const mockAgent = new MockAgent();
setGlobalDispatcher(mockAgent);
mockAgent.disableNetConnect()
// Provide the base url to the request
const mockPool = mockAgent.get('http://localhost:3000');
mockPool.intercept({
path: '/bank-transfer',
method: 'POST',
}).reply(200, {
message: 'transaction processed'
})
const badRequest = await bankTransfer('1234567890', '100')
// Will throw an error
// MockNotMatchedError: Mock dispatch not matched for path '/bank-transfer':
// subsequent request to origin http://localhost:3000 was not allowed (net.connect disabled)
```
## Reply with data based on request
If the mocked response needs to be dynamically derived from the request parameters, you can provide a function instead of an object to `reply`:
```js
mockPool.intercept({
path: '/bank-transfer',
method: 'POST',
headers: {
'X-TOKEN-SECRET': 'SuperSecretToken',
},
body: JSON.stringify({
recipient: '1234567890',
amount: '100'
})
}).reply(200, (opts) => {
// do something with opts
return { message: 'transaction processed' }
})
```
in this case opts will be
```
{
method: 'POST',
headers: { 'X-TOKEN-SECRET': 'SuperSecretToken' },
body: '{"recipient":"1234567890","amount":"100"}',
origin: 'http://localhost:3000',
path: '/bank-transfer'
}
```

127
node_modules/undici/docs/best-practices/proxy.md generated vendored Normal file
View File

@ -0,0 +1,127 @@
# Connecting through a proxy
Connecting through a proxy is possible by:
- Using [AgentProxy](../api/ProxyAgent.md).
- Configuring `Client` or `Pool` constructor.
The proxy url should be passed to the `Client` or `Pool` constructor, while the upstream server url
should be added to every request call in the `path`.
For instance, if you need to send a request to the `/hello` route of your upstream server,
the `path` should be `path: 'http://upstream.server:port/hello?foo=bar'`.
If you proxy requires basic authentication, you can send it via the `proxy-authorization` header.
### Connect without authentication
```js
import { Client } from 'undici'
import { createServer } from 'http'
import proxy from 'proxy'
const server = await buildServer()
const proxyServer = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxyServer.address().port}`
server.on('request', (req, res) => {
console.log(req.url) // '/hello?foo=bar'
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ hello: 'world' }))
})
const client = new Client(proxyUrl)
const response = await client.request({
method: 'GET',
path: serverUrl + '/hello?foo=bar'
})
response.body.setEncoding('utf8')
let data = ''
for await (const chunk of response.body) {
data += chunk
}
console.log(response.statusCode) // 200
console.log(JSON.parse(data)) // { hello: 'world' }
server.close()
proxyServer.close()
client.close()
function buildServer () {
return new Promise((resolve, reject) => {
const server = createServer()
server.listen(0, () => resolve(server))
})
}
function buildProxy () {
return new Promise((resolve, reject) => {
const server = proxy(createServer())
server.listen(0, () => resolve(server))
})
}
```
### Connect with authentication
```js
import { Client } from 'undici'
import { createServer } from 'http'
import proxy from 'proxy'
const server = await buildServer()
const proxyServer = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxyServer.address().port}`
proxyServer.authenticate = function (req, fn) {
fn(null, req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`)
}
server.on('request', (req, res) => {
console.log(req.url) // '/hello?foo=bar'
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ hello: 'world' }))
})
const client = new Client(proxyUrl)
const response = await client.request({
method: 'GET',
path: serverUrl + '/hello?foo=bar',
headers: {
'proxy-authorization': `Basic ${Buffer.from('user:pass').toString('base64')}`
}
})
response.body.setEncoding('utf8')
let data = ''
for await (const chunk of response.body) {
data += chunk
}
console.log(response.statusCode) // 200
console.log(JSON.parse(data)) // { hello: 'world' }
server.close()
proxyServer.close()
client.close()
function buildServer () {
return new Promise((resolve, reject) => {
const server = createServer()
server.listen(0, () => resolve(server))
})
}
function buildProxy () {
return new Promise((resolve, reject) => {
const server = proxy(createServer())
server.listen(0, () => resolve(server))
})
}
```

View File

@ -0,0 +1,20 @@
# Writing tests
Undici is tuned for a production use case and its default will keep
a socket open for a few seconds after an HTTP request is completed to
remove the overhead of opening up a new socket. These settings that makes
Undici shine in production are not a good fit for using Undici in automated
tests, as it will result in longer execution times.
The following are good defaults that will keep the socket open for only 10ms:
```js
import { request, setGlobalDispatcher, Agent } from 'undici'
const agent = new Agent({
keepAliveTimeout: 10, // milliseconds
keepAliveMaxTimeout: 10 // milliseconds
})
setGlobalDispatcher(agent)
```

15
node_modules/undici/index-fetch.js generated vendored Normal file
View File

@ -0,0 +1,15 @@
'use strict'
const fetchImpl = require('./lib/fetch').fetch
module.exports.fetch = function fetch (resource, init = undefined) {
return fetchImpl(resource, init).catch((err) => {
Error.captureStackTrace(err, this)
throw err
})
}
module.exports.FormData = require('./lib/fetch/formdata').FormData
module.exports.Headers = require('./lib/fetch/headers').Headers
module.exports.Response = require('./lib/fetch/response').Response
module.exports.Request = require('./lib/fetch/request').Request
module.exports.WebSocket = require('./lib/websocket/websocket').WebSocket

3
node_modules/undici/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,3 @@
export * from './types/index'
import Undici from './types/index'
export default Undici

165
node_modules/undici/index.js generated vendored Normal file
View File

@ -0,0 +1,165 @@
'use strict'
const Client = require('./lib/client')
const Dispatcher = require('./lib/dispatcher')
const errors = require('./lib/core/errors')
const Pool = require('./lib/pool')
const BalancedPool = require('./lib/balanced-pool')
const Agent = require('./lib/agent')
const util = require('./lib/core/util')
const { InvalidArgumentError } = errors
const api = require('./lib/api')
const buildConnector = require('./lib/core/connect')
const MockClient = require('./lib/mock/mock-client')
const MockAgent = require('./lib/mock/mock-agent')
const MockPool = require('./lib/mock/mock-pool')
const mockErrors = require('./lib/mock/mock-errors')
const ProxyAgent = require('./lib/proxy-agent')
const { getGlobalDispatcher, setGlobalDispatcher } = require('./lib/global')
const DecoratorHandler = require('./lib/handler/DecoratorHandler')
const RedirectHandler = require('./lib/handler/RedirectHandler')
const createRedirectInterceptor = require('./lib/interceptor/redirectInterceptor')
let hasCrypto
try {
require('crypto')
hasCrypto = true
} catch {
hasCrypto = false
}
Object.assign(Dispatcher.prototype, api)
module.exports.Dispatcher = Dispatcher
module.exports.Client = Client
module.exports.Pool = Pool
module.exports.BalancedPool = BalancedPool
module.exports.Agent = Agent
module.exports.ProxyAgent = ProxyAgent
module.exports.DecoratorHandler = DecoratorHandler
module.exports.RedirectHandler = RedirectHandler
module.exports.createRedirectInterceptor = createRedirectInterceptor
module.exports.buildConnector = buildConnector
module.exports.errors = errors
function makeDispatcher (fn) {
return (url, opts, handler) => {
if (typeof opts === 'function') {
handler = opts
opts = null
}
if (!url || (typeof url !== 'string' && typeof url !== 'object' && !(url instanceof URL))) {
throw new InvalidArgumentError('invalid url')
}
if (opts != null && typeof opts !== 'object') {
throw new InvalidArgumentError('invalid opts')
}
if (opts && opts.path != null) {
if (typeof opts.path !== 'string') {
throw new InvalidArgumentError('invalid opts.path')
}
let path = opts.path
if (!opts.path.startsWith('/')) {
path = `/${path}`
}
url = new URL(util.parseOrigin(url).origin + path)
} else {
if (!opts) {
opts = typeof url === 'object' ? url : {}
}
url = util.parseURL(url)
}
const { agent, dispatcher = getGlobalDispatcher() } = opts
if (agent) {
throw new InvalidArgumentError('unsupported opts.agent. Did you mean opts.client?')
}
return fn.call(dispatcher, {
...opts,
origin: url.origin,
path: url.search ? `${url.pathname}${url.search}` : url.pathname,
method: opts.method || (opts.body ? 'PUT' : 'GET')
}, handler)
}
}
module.exports.setGlobalDispatcher = setGlobalDispatcher
module.exports.getGlobalDispatcher = getGlobalDispatcher
if (util.nodeMajor > 16 || (util.nodeMajor === 16 && util.nodeMinor >= 8)) {
let fetchImpl = null
module.exports.fetch = async function fetch (resource) {
if (!fetchImpl) {
fetchImpl = require('./lib/fetch').fetch
}
try {
return await fetchImpl(...arguments)
} catch (err) {
if (typeof err === 'object') {
Error.captureStackTrace(err, this)
}
throw err
}
}
module.exports.Headers = require('./lib/fetch/headers').Headers
module.exports.Response = require('./lib/fetch/response').Response
module.exports.Request = require('./lib/fetch/request').Request
module.exports.FormData = require('./lib/fetch/formdata').FormData
module.exports.File = require('./lib/fetch/file').File
module.exports.FileReader = require('./lib/fileapi/filereader').FileReader
const { setGlobalOrigin, getGlobalOrigin } = require('./lib/fetch/global')
module.exports.setGlobalOrigin = setGlobalOrigin
module.exports.getGlobalOrigin = getGlobalOrigin
const { CacheStorage } = require('./lib/cache/cachestorage')
const { kConstruct } = require('./lib/cache/symbols')
// Cache & CacheStorage are tightly coupled with fetch. Even if it may run
// in an older version of Node, it doesn't have any use without fetch.
module.exports.caches = new CacheStorage(kConstruct)
}
if (util.nodeMajor >= 16) {
const { deleteCookie, getCookies, getSetCookies, setCookie } = require('./lib/cookies')
module.exports.deleteCookie = deleteCookie
module.exports.getCookies = getCookies
module.exports.getSetCookies = getSetCookies
module.exports.setCookie = setCookie
const { parseMIMEType, serializeAMimeType } = require('./lib/fetch/dataURL')
module.exports.parseMIMEType = parseMIMEType
module.exports.serializeAMimeType = serializeAMimeType
}
if (util.nodeMajor >= 18 && hasCrypto) {
const { WebSocket } = require('./lib/websocket/websocket')
module.exports.WebSocket = WebSocket
}
module.exports.request = makeDispatcher(api.request)
module.exports.stream = makeDispatcher(api.stream)
module.exports.pipeline = makeDispatcher(api.pipeline)
module.exports.connect = makeDispatcher(api.connect)
module.exports.upgrade = makeDispatcher(api.upgrade)
module.exports.MockClient = MockClient
module.exports.MockPool = MockPool
module.exports.MockAgent = MockAgent
module.exports.mockErrors = mockErrors

148
node_modules/undici/lib/agent.js generated vendored Normal file
View File

@ -0,0 +1,148 @@
'use strict'
const { InvalidArgumentError } = require('./core/errors')
const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = require('./core/symbols')
const DispatcherBase = require('./dispatcher-base')
const Pool = require('./pool')
const Client = require('./client')
const util = require('./core/util')
const createRedirectInterceptor = require('./interceptor/redirectInterceptor')
const { WeakRef, FinalizationRegistry } = require('./compat/dispatcher-weakref')()
const kOnConnect = Symbol('onConnect')
const kOnDisconnect = Symbol('onDisconnect')
const kOnConnectionError = Symbol('onConnectionError')
const kMaxRedirections = Symbol('maxRedirections')
const kOnDrain = Symbol('onDrain')
const kFactory = Symbol('factory')
const kFinalizer = Symbol('finalizer')
const kOptions = Symbol('options')
function defaultFactory (origin, opts) {
return opts && opts.connections === 1
? new Client(origin, opts)
: new Pool(origin, opts)
}
class Agent extends DispatcherBase {
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
super()
if (typeof factory !== 'function') {
throw new InvalidArgumentError('factory must be a function.')
}
if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {
throw new InvalidArgumentError('connect must be a function or an object')
}
if (!Number.isInteger(maxRedirections) || maxRedirections < 0) {
throw new InvalidArgumentError('maxRedirections must be a positive number')
}
if (connect && typeof connect !== 'function') {
connect = { ...connect }
}
this[kInterceptors] = options.interceptors && 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
this[kClients] = new Map()
this[kFinalizer] = new FinalizationRegistry(/* istanbul ignore next: gc is undeterministic */ key => {
const ref = this[kClients].get(key)
if (ref !== undefined && ref.deref() === undefined) {
this[kClients].delete(key)
}
})
const agent = this
this[kOnDrain] = (origin, targets) => {
agent.emit('drain', origin, [agent, ...targets])
}
this[kOnConnect] = (origin, targets) => {
agent.emit('connect', origin, [agent, ...targets])
}
this[kOnDisconnect] = (origin, targets, err) => {
agent.emit('disconnect', origin, [agent, ...targets], err)
}
this[kOnConnectionError] = (origin, targets, err) => {
agent.emit('connectionError', origin, [agent, ...targets], err)
}
}
get [kRunning] () {
let ret = 0
for (const ref of this[kClients].values()) {
const client = ref.deref()
/* istanbul ignore next: gc is undeterministic */
if (client) {
ret += client[kRunning]
}
}
return ret
}
[kDispatch] (opts, handler) {
let key
if (opts.origin && (typeof opts.origin === 'string' || opts.origin instanceof URL)) {
key = String(opts.origin)
} else {
throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.')
}
const ref = this[kClients].get(key)
let dispatcher = ref ? ref.deref() : null
if (!dispatcher) {
dispatcher = this[kFactory](opts.origin, this[kOptions])
.on('drain', this[kOnDrain])
.on('connect', this[kOnConnect])
.on('disconnect', this[kOnDisconnect])
.on('connectionError', this[kOnConnectionError])
this[kClients].set(key, new WeakRef(dispatcher))
this[kFinalizer].register(dispatcher, key)
}
return dispatcher.dispatch(opts, handler)
}
async [kClose] () {
const closePromises = []
for (const ref of this[kClients].values()) {
const client = ref.deref()
/* istanbul ignore else: gc is undeterministic */
if (client) {
closePromises.push(client.close())
}
}
await Promise.all(closePromises)
}
async [kDestroy] (err) {
const destroyPromises = []
for (const ref of this[kClients].values()) {
const client = ref.deref()
/* istanbul ignore else: gc is undeterministic */
if (client) {
destroyPromises.push(client.destroy(err))
}
}
await Promise.all(destroyPromises)
}
}
module.exports = Agent

54
node_modules/undici/lib/api/abort-signal.js generated vendored Normal file
View File

@ -0,0 +1,54 @@
const { addAbortListener } = require('../core/util')
const { RequestAbortedError } = require('../core/errors')
const kListener = Symbol('kListener')
const kSignal = Symbol('kSignal')
function abort (self) {
if (self.abort) {
self.abort()
} else {
self.onError(new RequestAbortedError())
}
}
function addSignal (self, signal) {
self[kSignal] = null
self[kListener] = null
if (!signal) {
return
}
if (signal.aborted) {
abort(self)
return
}
self[kSignal] = signal
self[kListener] = () => {
abort(self)
}
addAbortListener(self[kSignal], self[kListener])
}
function removeSignal (self) {
if (!self[kSignal]) {
return
}
if ('removeEventListener' in self[kSignal]) {
self[kSignal].removeEventListener('abort', self[kListener])
} else {
self[kSignal].removeListener('abort', self[kListener])
}
self[kSignal] = null
self[kListener] = null
}
module.exports = {
addSignal,
removeSignal
}

104
node_modules/undici/lib/api/api-connect.js generated vendored Normal file
View File

@ -0,0 +1,104 @@
'use strict'
const { AsyncResource } = require('async_hooks')
const { InvalidArgumentError, RequestAbortedError, SocketError } = require('../core/errors')
const util = require('../core/util')
const { addSignal, removeSignal } = require('./abort-signal')
class ConnectHandler extends AsyncResource {
constructor (opts, callback) {
if (!opts || typeof opts !== 'object') {
throw new InvalidArgumentError('invalid opts')
}
if (typeof callback !== 'function') {
throw new InvalidArgumentError('invalid callback')
}
const { signal, opaque, responseHeaders } = opts
if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
}
super('UNDICI_CONNECT')
this.opaque = opaque || null
this.responseHeaders = responseHeaders || null
this.callback = callback
this.abort = null
addSignal(this, signal)
}
onConnect (abort, context) {
if (!this.callback) {
throw new RequestAbortedError()
}
this.abort = abort
this.context = context
}
onHeaders () {
throw new SocketError('bad connect', null)
}
onUpgrade (statusCode, rawHeaders, socket) {
const { callback, opaque, context } = this
removeSignal(this)
this.callback = null
let headers = rawHeaders
// Indicates is an HTTP2Session
if (headers != null) {
headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
}
this.runInAsyncScope(callback, null, null, {
statusCode,
headers,
socket,
opaque,
context
})
}
onError (err) {
const { callback, opaque } = this
removeSignal(this)
if (callback) {
this.callback = null
queueMicrotask(() => {
this.runInAsyncScope(callback, null, err, { opaque })
})
}
}
}
function connect (opts, callback) {
if (callback === undefined) {
return new Promise((resolve, reject) => {
connect.call(this, opts, (err, data) => {
return err ? reject(err) : resolve(data)
})
})
}
try {
const connectHandler = new ConnectHandler(opts, callback)
this.dispatch({ ...opts, method: 'CONNECT' }, connectHandler)
} catch (err) {
if (typeof callback !== 'function') {
throw err
}
const opaque = opts && opts.opaque
queueMicrotask(() => callback(err, { opaque }))
}
}
module.exports = connect

249
node_modules/undici/lib/api/api-pipeline.js generated vendored Normal file
View File

@ -0,0 +1,249 @@
'use strict'
const {
Readable,
Duplex,
PassThrough
} = require('stream')
const {
InvalidArgumentError,
InvalidReturnValueError,
RequestAbortedError
} = require('../core/errors')
const util = require('../core/util')
const { AsyncResource } = require('async_hooks')
const { addSignal, removeSignal } = require('./abort-signal')
const assert = require('assert')
const kResume = Symbol('resume')
class PipelineRequest extends Readable {
constructor () {
super({ autoDestroy: true })
this[kResume] = null
}
_read () {
const { [kResume]: resume } = this
if (resume) {
this[kResume] = null
resume()
}
}
_destroy (err, callback) {
this._read()
callback(err)
}
}
class PipelineResponse extends Readable {
constructor (resume) {
super({ autoDestroy: true })
this[kResume] = resume
}
_read () {
this[kResume]()
}
_destroy (err, callback) {
if (!err && !this._readableState.endEmitted) {
err = new RequestAbortedError()
}
callback(err)
}
}
class PipelineHandler extends AsyncResource {
constructor (opts, handler) {
if (!opts || typeof opts !== 'object') {
throw new InvalidArgumentError('invalid opts')
}
if (typeof handler !== 'function') {
throw new InvalidArgumentError('invalid handler')
}
const { signal, method, opaque, onInfo, responseHeaders } = opts
if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
}
if (method === 'CONNECT') {
throw new InvalidArgumentError('invalid method')
}
if (onInfo && typeof onInfo !== 'function') {
throw new InvalidArgumentError('invalid onInfo callback')
}
super('UNDICI_PIPELINE')
this.opaque = opaque || null
this.responseHeaders = responseHeaders || null
this.handler = handler
this.abort = null
this.context = null
this.onInfo = onInfo || null
this.req = new PipelineRequest().on('error', util.nop)
this.ret = new Duplex({
readableObjectMode: opts.objectMode,
autoDestroy: true,
read: () => {
const { body } = this
if (body && body.resume) {
body.resume()
}
},
write: (chunk, encoding, callback) => {
const { req } = this
if (req.push(chunk, encoding) || req._readableState.destroyed) {
callback()
} else {
req[kResume] = callback
}
},
destroy: (err, callback) => {
const { body, req, res, ret, abort } = this
if (!err && !ret._readableState.endEmitted) {
err = new RequestAbortedError()
}
if (abort && err) {
abort()
}
util.destroy(body, err)
util.destroy(req, err)
util.destroy(res, err)
removeSignal(this)
callback(err)
}
}).on('prefinish', () => {
const { req } = this
// Node < 15 does not call _final in same tick.
req.push(null)
})
this.res = null
addSignal(this, signal)
}
onConnect (abort, context) {
const { ret, res } = this
assert(!res, 'pipeline cannot be retried')
if (ret.destroyed) {
throw new RequestAbortedError()
}
this.abort = abort
this.context = context
}
onHeaders (statusCode, rawHeaders, resume) {
const { opaque, handler, context } = this
if (statusCode < 200) {
if (this.onInfo) {
const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
this.onInfo({ statusCode, headers })
}
return
}
this.res = new PipelineResponse(resume)
let body
try {
this.handler = null
const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
body = this.runInAsyncScope(handler, null, {
statusCode,
headers,
opaque,
body: this.res,
context
})
} catch (err) {
this.res.on('error', util.nop)
throw err
}
if (!body || typeof body.on !== 'function') {
throw new InvalidReturnValueError('expected Readable')
}
body
.on('data', (chunk) => {
const { ret, body } = this
if (!ret.push(chunk) && body.pause) {
body.pause()
}
})
.on('error', (err) => {
const { ret } = this
util.destroy(ret, err)
})
.on('end', () => {
const { ret } = this
ret.push(null)
})
.on('close', () => {
const { ret } = this
if (!ret._readableState.ended) {
util.destroy(ret, new RequestAbortedError())
}
})
this.body = body
}
onData (chunk) {
const { res } = this
return res.push(chunk)
}
onComplete (trailers) {
const { res } = this
res.push(null)
}
onError (err) {
const { ret } = this
this.handler = null
util.destroy(ret, err)
}
}
function pipeline (opts, handler) {
try {
const pipelineHandler = new PipelineHandler(opts, handler)
this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler)
return pipelineHandler.ret
} catch (err) {
return new PassThrough().destroy(err)
}
}
module.exports = pipeline

179
node_modules/undici/lib/api/api-request.js generated vendored Normal file
View File

@ -0,0 +1,179 @@
'use strict'
const Readable = require('./readable')
const {
InvalidArgumentError,
RequestAbortedError
} = require('../core/errors')
const util = require('../core/util')
const { getResolveErrorBodyCallback } = require('./util')
const { AsyncResource } = require('async_hooks')
const { addSignal, removeSignal } = require('./abort-signal')
class RequestHandler extends AsyncResource {
constructor (opts, callback) {
if (!opts || typeof opts !== 'object') {
throw new InvalidArgumentError('invalid opts')
}
const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError, highWaterMark } = opts
try {
if (typeof callback !== 'function') {
throw new InvalidArgumentError('invalid callback')
}
if (highWaterMark && (typeof highWaterMark !== 'number' || highWaterMark < 0)) {
throw new InvalidArgumentError('invalid highWaterMark')
}
if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
}
if (method === 'CONNECT') {
throw new InvalidArgumentError('invalid method')
}
if (onInfo && typeof onInfo !== 'function') {
throw new InvalidArgumentError('invalid onInfo callback')
}
super('UNDICI_REQUEST')
} catch (err) {
if (util.isStream(body)) {
util.destroy(body.on('error', util.nop), err)
}
throw err
}
this.responseHeaders = responseHeaders || null
this.opaque = opaque || null
this.callback = callback
this.res = null
this.abort = null
this.body = body
this.trailers = {}
this.context = null
this.onInfo = onInfo || null
this.throwOnError = throwOnError
this.highWaterMark = highWaterMark
if (util.isStream(body)) {
body.on('error', (err) => {
this.onError(err)
})
}
addSignal(this, signal)
}
onConnect (abort, context) {
if (!this.callback) {
throw new RequestAbortedError()
}
this.abort = abort
this.context = context
}
onHeaders (statusCode, rawHeaders, resume, statusMessage) {
const { callback, opaque, abort, context, responseHeaders, highWaterMark } = this
const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
if (statusCode < 200) {
if (this.onInfo) {
this.onInfo({ statusCode, headers })
}
return
}
const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers
const contentType = parsedHeaders['content-type']
const body = new Readable({ resume, abort, contentType, highWaterMark })
this.callback = null
this.res = body
if (callback !== null) {
if (this.throwOnError && statusCode >= 400) {
this.runInAsyncScope(getResolveErrorBodyCallback, null,
{ callback, body, contentType, statusCode, statusMessage, headers }
)
} else {
this.runInAsyncScope(callback, null, null, {
statusCode,
headers,
trailers: this.trailers,
opaque,
body,
context
})
}
}
}
onData (chunk) {
const { res } = this
return res.push(chunk)
}
onComplete (trailers) {
const { res } = this
removeSignal(this)
util.parseHeaders(trailers, this.trailers)
res.push(null)
}
onError (err) {
const { res, callback, body, opaque } = this
removeSignal(this)
if (callback) {
// TODO: Does this need queueMicrotask?
this.callback = null
queueMicrotask(() => {
this.runInAsyncScope(callback, null, err, { opaque })
})
}
if (res) {
this.res = null
// Ensure all queued handlers are invoked before destroying res.
queueMicrotask(() => {
util.destroy(res, err)
})
}
if (body) {
this.body = null
util.destroy(body, err)
}
}
}
function request (opts, callback) {
if (callback === undefined) {
return new Promise((resolve, reject) => {
request.call(this, opts, (err, data) => {
return err ? reject(err) : resolve(data)
})
})
}
try {
this.dispatch(opts, new RequestHandler(opts, callback))
} catch (err) {
if (typeof callback !== 'function') {
throw err
}
const opaque = opts && opts.opaque
queueMicrotask(() => callback(err, { opaque }))
}
}
module.exports = request

220
node_modules/undici/lib/api/api-stream.js generated vendored Normal file
View File

@ -0,0 +1,220 @@
'use strict'
const { finished, PassThrough } = require('stream')
const {
InvalidArgumentError,
InvalidReturnValueError,
RequestAbortedError
} = require('../core/errors')
const util = require('../core/util')
const { getResolveErrorBodyCallback } = require('./util')
const { AsyncResource } = require('async_hooks')
const { addSignal, removeSignal } = require('./abort-signal')
class StreamHandler extends AsyncResource {
constructor (opts, factory, callback) {
if (!opts || typeof opts !== 'object') {
throw new InvalidArgumentError('invalid opts')
}
const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError } = opts
try {
if (typeof callback !== 'function') {
throw new InvalidArgumentError('invalid callback')
}
if (typeof factory !== 'function') {
throw new InvalidArgumentError('invalid factory')
}
if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
}
if (method === 'CONNECT') {
throw new InvalidArgumentError('invalid method')
}
if (onInfo && typeof onInfo !== 'function') {
throw new InvalidArgumentError('invalid onInfo callback')
}
super('UNDICI_STREAM')
} catch (err) {
if (util.isStream(body)) {
util.destroy(body.on('error', util.nop), err)
}
throw err
}
this.responseHeaders = responseHeaders || null
this.opaque = opaque || null
this.factory = factory
this.callback = callback
this.res = null
this.abort = null
this.context = null
this.trailers = null
this.body = body
this.onInfo = onInfo || null
this.throwOnError = throwOnError || false
if (util.isStream(body)) {
body.on('error', (err) => {
this.onError(err)
})
}
addSignal(this, signal)
}
onConnect (abort, context) {
if (!this.callback) {
throw new RequestAbortedError()
}
this.abort = abort
this.context = context
}
onHeaders (statusCode, rawHeaders, resume, statusMessage) {
const { factory, opaque, context, callback, responseHeaders } = this
const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
if (statusCode < 200) {
if (this.onInfo) {
this.onInfo({ statusCode, headers })
}
return
}
this.factory = null
let res
if (this.throwOnError && statusCode >= 400) {
const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers
const contentType = parsedHeaders['content-type']
res = new PassThrough()
this.callback = null
this.runInAsyncScope(getResolveErrorBodyCallback, null,
{ callback, body: res, contentType, statusCode, statusMessage, headers }
)
} else {
if (factory === null) {
return
}
res = this.runInAsyncScope(factory, null, {
statusCode,
headers,
opaque,
context
})
if (
!res ||
typeof res.write !== 'function' ||
typeof res.end !== 'function' ||
typeof res.on !== 'function'
) {
throw new InvalidReturnValueError('expected Writable')
}
// TODO: Avoid finished. It registers an unnecessary amount of listeners.
finished(res, { readable: false }, (err) => {
const { callback, res, opaque, trailers, abort } = this
this.res = null
if (err || !res.readable) {
util.destroy(res, err)
}
this.callback = null
this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
if (err) {
abort()
}
})
}
res.on('drain', resume)
this.res = res
const needDrain = res.writableNeedDrain !== undefined
? res.writableNeedDrain
: res._writableState && res._writableState.needDrain
return needDrain !== true
}
onData (chunk) {
const { res } = this
return res ? res.write(chunk) : true
}
onComplete (trailers) {
const { res } = this
removeSignal(this)
if (!res) {
return
}
this.trailers = util.parseHeaders(trailers)
res.end()
}
onError (err) {
const { res, callback, opaque, body } = this
removeSignal(this)
this.factory = null
if (res) {
this.res = null
util.destroy(res, err)
} else if (callback) {
this.callback = null
queueMicrotask(() => {
this.runInAsyncScope(callback, null, err, { opaque })
})
}
if (body) {
this.body = null
util.destroy(body, err)
}
}
}
function stream (opts, factory, callback) {
if (callback === undefined) {
return new Promise((resolve, reject) => {
stream.call(this, opts, factory, (err, data) => {
return err ? reject(err) : resolve(data)
})
})
}
try {
this.dispatch(opts, new StreamHandler(opts, factory, callback))
} catch (err) {
if (typeof callback !== 'function') {
throw err
}
const opaque = opts && opts.opaque
queueMicrotask(() => callback(err, { opaque }))
}
}
module.exports = stream

105
node_modules/undici/lib/api/api-upgrade.js generated vendored Normal file
View File

@ -0,0 +1,105 @@
'use strict'
const { InvalidArgumentError, RequestAbortedError, SocketError } = require('../core/errors')
const { AsyncResource } = require('async_hooks')
const util = require('../core/util')
const { addSignal, removeSignal } = require('./abort-signal')
const assert = require('assert')
class UpgradeHandler extends AsyncResource {
constructor (opts, callback) {
if (!opts || typeof opts !== 'object') {
throw new InvalidArgumentError('invalid opts')
}
if (typeof callback !== 'function') {
throw new InvalidArgumentError('invalid callback')
}
const { signal, opaque, responseHeaders } = opts
if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
}
super('UNDICI_UPGRADE')
this.responseHeaders = responseHeaders || null
this.opaque = opaque || null
this.callback = callback
this.abort = null
this.context = null
addSignal(this, signal)
}
onConnect (abort, context) {
if (!this.callback) {
throw new RequestAbortedError()
}
this.abort = abort
this.context = null
}
onHeaders () {
throw new SocketError('bad upgrade', null)
}
onUpgrade (statusCode, rawHeaders, socket) {
const { callback, opaque, context } = this
assert.strictEqual(statusCode, 101)
removeSignal(this)
this.callback = null
const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
this.runInAsyncScope(callback, null, null, {
headers,
socket,
opaque,
context
})
}
onError (err) {
const { callback, opaque } = this
removeSignal(this)
if (callback) {
this.callback = null
queueMicrotask(() => {
this.runInAsyncScope(callback, null, err, { opaque })
})
}
}
}
function upgrade (opts, callback) {
if (callback === undefined) {
return new Promise((resolve, reject) => {
upgrade.call(this, opts, (err, data) => {
return err ? reject(err) : resolve(data)
})
})
}
try {
const upgradeHandler = new UpgradeHandler(opts, callback)
this.dispatch({
...opts,
method: opts.method || 'GET',
upgrade: opts.protocol || 'Websocket'
}, upgradeHandler)
} catch (err) {
if (typeof callback !== 'function') {
throw err
}
const opaque = opts && opts.opaque
queueMicrotask(() => callback(err, { opaque }))
}
}
module.exports = upgrade

7
node_modules/undici/lib/api/index.js generated vendored Normal file
View File

@ -0,0 +1,7 @@
'use strict'
module.exports.request = require('./api-request')
module.exports.stream = require('./api-stream')
module.exports.pipeline = require('./api-pipeline')
module.exports.upgrade = require('./api-upgrade')
module.exports.connect = require('./api-connect')

307
node_modules/undici/lib/api/readable.js generated vendored Normal file
View File

@ -0,0 +1,307 @@
// Ported from https://github.com/nodejs/undici/pull/907
'use strict'
const assert = require('assert')
const { Readable } = require('stream')
const { RequestAbortedError, NotSupportedError, InvalidArgumentError } = require('../core/errors')
const util = require('../core/util')
const { ReadableStreamFrom, toUSVString } = require('../core/util')
let Blob
const kConsume = Symbol('kConsume')
const kReading = Symbol('kReading')
const kBody = Symbol('kBody')
const kAbort = Symbol('abort')
const kContentType = Symbol('kContentType')
module.exports = class BodyReadable extends Readable {
constructor ({
resume,
abort,
contentType = '',
highWaterMark = 64 * 1024 // Same as nodejs fs streams.
}) {
super({
autoDestroy: true,
read: resume,
highWaterMark
})
this._readableState.dataEmitted = false
this[kAbort] = abort
this[kConsume] = null
this[kBody] = null
this[kContentType] = contentType
// Is stream being consumed through Readable API?
// This is an optimization so that we avoid checking
// for 'data' and 'readable' listeners in the hot path
// inside push().
this[kReading] = false
}
destroy (err) {
if (this.destroyed) {
// Node < 16
return this
}
if (!err && !this._readableState.endEmitted) {
err = new RequestAbortedError()
}
if (err) {
this[kAbort]()
}
return super.destroy(err)
}
emit (ev, ...args) {
if (ev === 'data') {
// Node < 16.7
this._readableState.dataEmitted = true
} else if (ev === 'error') {
// Node < 16
this._readableState.errorEmitted = true
}
return super.emit(ev, ...args)
}
on (ev, ...args) {
if (ev === 'data' || ev === 'readable') {
this[kReading] = true
}
return super.on(ev, ...args)
}
addListener (ev, ...args) {
return this.on(ev, ...args)
}
off (ev, ...args) {
const ret = super.off(ev, ...args)
if (ev === 'data' || ev === 'readable') {
this[kReading] = (
this.listenerCount('data') > 0 ||
this.listenerCount('readable') > 0
)
}
return ret
}
removeListener (ev, ...args) {
return this.off(ev, ...args)
}
push (chunk) {
if (this[kConsume] && chunk !== null && this.readableLength === 0) {
consumePush(this[kConsume], chunk)
return this[kReading] ? super.push(chunk) : true
}
return super.push(chunk)
}
// https://fetch.spec.whatwg.org/#dom-body-text
async text () {
return consume(this, 'text')
}
// https://fetch.spec.whatwg.org/#dom-body-json
async json () {
return consume(this, 'json')
}
// https://fetch.spec.whatwg.org/#dom-body-blob
async blob () {
return consume(this, 'blob')
}
// https://fetch.spec.whatwg.org/#dom-body-arraybuffer
async arrayBuffer () {
return consume(this, 'arrayBuffer')
}
// https://fetch.spec.whatwg.org/#dom-body-formdata
async formData () {
// TODO: Implement.
throw new NotSupportedError()
}
// https://fetch.spec.whatwg.org/#dom-body-bodyused
get bodyUsed () {
return util.isDisturbed(this)
}
// https://fetch.spec.whatwg.org/#dom-body-body
get body () {
if (!this[kBody]) {
this[kBody] = ReadableStreamFrom(this)
if (this[kConsume]) {
// TODO: Is this the best way to force a lock?
this[kBody].getReader() // Ensure stream is locked.
assert(this[kBody].locked)
}
}
return this[kBody]
}
async dump (opts) {
let limit = opts && Number.isFinite(opts.limit) ? opts.limit : 262144
const signal = opts && opts.signal
const abortFn = () => {
this.destroy()
}
let signalListenerCleanup
if (signal) {
if (typeof signal !== 'object' || !('aborted' in signal)) {
throw new InvalidArgumentError('signal must be an AbortSignal')
}
util.throwIfAborted(signal)
signalListenerCleanup = util.addAbortListener(signal, abortFn)
}
try {
for await (const chunk of this) {
util.throwIfAborted(signal)
limit -= Buffer.byteLength(chunk)
if (limit < 0) {
return
}
}
} catch {
util.throwIfAborted(signal)
} finally {
if (typeof signalListenerCleanup === 'function') {
signalListenerCleanup()
} else if (signalListenerCleanup) {
signalListenerCleanup[Symbol.dispose]()
}
}
}
}
// https://streams.spec.whatwg.org/#readablestream-locked
function isLocked (self) {
// Consume is an implicit lock.
return (self[kBody] && self[kBody].locked === true) || self[kConsume]
}
// https://fetch.spec.whatwg.org/#body-unusable
function isUnusable (self) {
return util.isDisturbed(self) || isLocked(self)
}
async function consume (stream, type) {
if (isUnusable(stream)) {
throw new TypeError('unusable')
}
assert(!stream[kConsume])
return new Promise((resolve, reject) => {
stream[kConsume] = {
type,
stream,
resolve,
reject,
length: 0,
body: []
}
stream
.on('error', function (err) {
consumeFinish(this[kConsume], err)
})
.on('close', function () {
if (this[kConsume].body !== null) {
consumeFinish(this[kConsume], new RequestAbortedError())
}
})
process.nextTick(consumeStart, stream[kConsume])
})
}
function consumeStart (consume) {
if (consume.body === null) {
return
}
const { _readableState: state } = consume.stream
for (const chunk of state.buffer) {
consumePush(consume, chunk)
}
if (state.endEmitted) {
consumeEnd(this[kConsume])
} else {
consume.stream.on('end', function () {
consumeEnd(this[kConsume])
})
}
consume.stream.resume()
while (consume.stream.read() != null) {
// Loop
}
}
function consumeEnd (consume) {
const { type, body, resolve, stream, length } = consume
try {
if (type === 'text') {
resolve(toUSVString(Buffer.concat(body)))
} else if (type === 'json') {
resolve(JSON.parse(Buffer.concat(body)))
} 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)
} else if (type === 'blob') {
if (!Blob) {
Blob = require('buffer').Blob
}
resolve(new Blob(body, { type: stream[kContentType] }))
}
consumeFinish(consume)
} catch (err) {
stream.destroy(err)
}
}
function consumePush (consume, chunk) {
consume.length += chunk.length
consume.body.push(chunk)
}
function consumeFinish (consume, err) {
if (consume.body === null) {
return
}
if (err) {
consume.reject(err)
} else {
consume.resolve()
}
consume.type = null
consume.stream = null
consume.resolve = null
consume.reject = null
consume.length = 0
consume.body = null
}

46
node_modules/undici/lib/api/util.js generated vendored Normal file
View File

@ -0,0 +1,46 @@
const assert = require('assert')
const {
ResponseStatusCodeError
} = require('../core/errors')
const { toUSVString } = require('../core/util')
async function getResolveErrorBodyCallback ({ callback, body, contentType, statusCode, statusMessage, headers }) {
assert(body)
let chunks = []
let limit = 0
for await (const chunk of body) {
chunks.push(chunk)
limit += chunk.length
if (limit > 128 * 1024) {
chunks = null
break
}
}
if (statusCode === 204 || !contentType || !chunks) {
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers))
return
}
try {
if (contentType.startsWith('application/json')) {
const payload = JSON.parse(toUSVString(Buffer.concat(chunks)))
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload))
return
}
if (contentType.startsWith('text/')) {
const payload = toUSVString(Buffer.concat(chunks))
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload))
return
}
} catch (err) {
// Process in a fallback if error
}
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers))
}
module.exports = { getResolveErrorBodyCallback }

190
node_modules/undici/lib/balanced-pool.js generated vendored Normal file
View File

@ -0,0 +1,190 @@
'use strict'
const {
BalancedPoolMissingUpstreamError,
InvalidArgumentError
} = require('./core/errors')
const {
PoolBase,
kClients,
kNeedDrain,
kAddClient,
kRemoveClient,
kGetDispatcher
} = require('./pool-base')
const Pool = require('./pool')
const { kUrl, kInterceptors } = require('./core/symbols')
const { parseOrigin } = require('./core/util')
const kFactory = Symbol('factory')
const kOptions = Symbol('options')
const kGreatestCommonDivisor = Symbol('kGreatestCommonDivisor')
const kCurrentWeight = Symbol('kCurrentWeight')
const kIndex = Symbol('kIndex')
const kWeight = Symbol('kWeight')
const kMaxWeightPerServer = Symbol('kMaxWeightPerServer')
const kErrorPenalty = Symbol('kErrorPenalty')
function getGreatestCommonDivisor (a, b) {
if (b === 0) return a
return getGreatestCommonDivisor(b, a % b)
}
function defaultFactory (origin, opts) {
return new Pool(origin, opts)
}
class BalancedPool extends PoolBase {
constructor (upstreams = [], { factory = defaultFactory, ...opts } = {}) {
super()
this[kOptions] = opts
this[kIndex] = -1
this[kCurrentWeight] = 0
this[kMaxWeightPerServer] = this[kOptions].maxWeightPerServer || 100
this[kErrorPenalty] = this[kOptions].errorPenalty || 15
if (!Array.isArray(upstreams)) {
upstreams = [upstreams]
}
if (typeof factory !== 'function') {
throw new InvalidArgumentError('factory must be a function.')
}
this[kInterceptors] = opts.interceptors && opts.interceptors.BalancedPool && Array.isArray(opts.interceptors.BalancedPool)
? opts.interceptors.BalancedPool
: []
this[kFactory] = factory
for (const upstream of upstreams) {
this.addUpstream(upstream)
}
this._updateBalancedPoolStats()
}
addUpstream (upstream) {
const upstreamOrigin = parseOrigin(upstream).origin
if (this[kClients].find((pool) => (
pool[kUrl].origin === upstreamOrigin &&
pool.closed !== true &&
pool.destroyed !== true
))) {
return this
}
const pool = this[kFactory](upstreamOrigin, Object.assign({}, this[kOptions]))
this[kAddClient](pool)
pool.on('connect', () => {
pool[kWeight] = Math.min(this[kMaxWeightPerServer], pool[kWeight] + this[kErrorPenalty])
})
pool.on('connectionError', () => {
pool[kWeight] = Math.max(1, pool[kWeight] - this[kErrorPenalty])
this._updateBalancedPoolStats()
})
pool.on('disconnect', (...args) => {
const err = args[2]
if (err && err.code === 'UND_ERR_SOCKET') {
// decrease the weight of the pool.
pool[kWeight] = Math.max(1, pool[kWeight] - this[kErrorPenalty])
this._updateBalancedPoolStats()
}
})
for (const client of this[kClients]) {
client[kWeight] = this[kMaxWeightPerServer]
}
this._updateBalancedPoolStats()
return this
}
_updateBalancedPoolStats () {
this[kGreatestCommonDivisor] = this[kClients].map(p => p[kWeight]).reduce(getGreatestCommonDivisor, 0)
}
removeUpstream (upstream) {
const upstreamOrigin = parseOrigin(upstream).origin
const pool = this[kClients].find((pool) => (
pool[kUrl].origin === upstreamOrigin &&
pool.closed !== true &&
pool.destroyed !== true
))
if (pool) {
this[kRemoveClient](pool)
}
return this
}
get upstreams () {
return this[kClients]
.filter(dispatcher => dispatcher.closed !== true && dispatcher.destroyed !== true)
.map((p) => p[kUrl].origin)
}
[kGetDispatcher] () {
// We validate that pools is greater than 0,
// otherwise we would have to wait until an upstream
// is added, which might never happen.
if (this[kClients].length === 0) {
throw new BalancedPoolMissingUpstreamError()
}
const dispatcher = this[kClients].find(dispatcher => (
!dispatcher[kNeedDrain] &&
dispatcher.closed !== true &&
dispatcher.destroyed !== true
))
if (!dispatcher) {
return
}
const allClientsBusy = this[kClients].map(pool => pool[kNeedDrain]).reduce((a, b) => a && b, true)
if (allClientsBusy) {
return
}
let counter = 0
let maxWeightIndex = this[kClients].findIndex(pool => !pool[kNeedDrain])
while (counter++ < this[kClients].length) {
this[kIndex] = (this[kIndex] + 1) % this[kClients].length
const pool = this[kClients][this[kIndex]]
// find pool index with the largest weight
if (pool[kWeight] > this[kClients][maxWeightIndex][kWeight] && !pool[kNeedDrain]) {
maxWeightIndex = this[kIndex]
}
// decrease the current weight every `this[kClients].length`.
if (this[kIndex] === 0) {
// Set the current weight to the next lower weight.
this[kCurrentWeight] = this[kCurrentWeight] - this[kGreatestCommonDivisor]
if (this[kCurrentWeight] <= 0) {
this[kCurrentWeight] = this[kMaxWeightPerServer]
}
}
if (pool[kWeight] >= this[kCurrentWeight] && (!pool[kNeedDrain])) {
return pool
}
}
this[kCurrentWeight] = this[kClients][maxWeightIndex][kWeight]
this[kIndex] = maxWeightIndex
return this[kClients][maxWeightIndex]
}
}
module.exports = BalancedPool

838
node_modules/undici/lib/cache/cache.js generated vendored Normal file
View File

@ -0,0 +1,838 @@
'use strict'
const { kConstruct } = require('./symbols')
const { urlEquals, fieldValues: getFieldValues } = require('./util')
const { kEnumerableProperty, isDisturbed } = require('../core/util')
const { kHeadersList } = require('../core/symbols')
const { webidl } = require('../fetch/webidl')
const { Response, cloneResponse } = require('../fetch/response')
const { Request } = require('../fetch/request')
const { kState, kHeaders, kGuard, kRealm } = require('../fetch/symbols')
const { fetching } = require('../fetch/index')
const { urlIsHttpHttpsScheme, createDeferredPromise, readAllBytes } = require('../fetch/util')
const assert = require('assert')
const { getGlobalDispatcher } = require('../global')
/**
* @see https://w3c.github.io/ServiceWorker/#dfn-cache-batch-operation
* @typedef {Object} CacheBatchOperation
* @property {'delete' | 'put'} type
* @property {any} request
* @property {any} response
* @property {import('../../types/cache').CacheQueryOptions} options
*/
/**
* @see https://w3c.github.io/ServiceWorker/#dfn-request-response-list
* @typedef {[any, any][]} requestResponseList
*/
class Cache {
/**
* @see https://w3c.github.io/ServiceWorker/#dfn-relevant-request-response-list
* @type {requestResponseList}
*/
#relevantRequestResponseList
constructor () {
if (arguments[0] !== kConstruct) {
webidl.illegalConstructor()
}
this.#relevantRequestResponseList = arguments[1]
}
async match (request, options = {}) {
webidl.brandCheck(this, Cache)
webidl.argumentLengthCheck(arguments, 1, { header: 'Cache.match' })
request = webidl.converters.RequestInfo(request)
options = webidl.converters.CacheQueryOptions(options)
const p = await this.matchAll(request, options)
if (p.length === 0) {
return
}
return p[0]
}
async matchAll (request = undefined, options = {}) {
webidl.brandCheck(this, Cache)
if (request !== undefined) request = webidl.converters.RequestInfo(request)
options = webidl.converters.CacheQueryOptions(options)
// 1.
let r = null
// 2.
if (request !== undefined) {
if (request instanceof Request) {
// 2.1.1
r = request[kState]
// 2.1.2
if (r.method !== 'GET' && !options.ignoreMethod) {
return []
}
} else if (typeof request === 'string') {
// 2.2.1
r = new Request(request)[kState]
}
}
// 5.
// 5.1
const responses = []
// 5.2
if (request === undefined) {
// 5.2.1
for (const requestResponse of this.#relevantRequestResponseList) {
responses.push(requestResponse[1])
}
} else { // 5.3
// 5.3.1
const requestResponses = this.#queryCache(r, options)
// 5.3.2
for (const requestResponse of requestResponses) {
responses.push(requestResponse[1])
}
}
// 5.4
// We don't implement CORs so we don't need to loop over the responses, yay!
// 5.5.1
const responseList = []
// 5.5.2
for (const response of responses) {
// 5.5.2.1
const responseObject = new Response(response.body?.source ?? null)
const body = responseObject[kState].body
responseObject[kState] = response
responseObject[kState].body = body
responseObject[kHeaders][kHeadersList] = response.headersList
responseObject[kHeaders][kGuard] = 'immutable'
responseList.push(responseObject)
}
// 6.
return Object.freeze(responseList)
}
async add (request) {
webidl.brandCheck(this, Cache)
webidl.argumentLengthCheck(arguments, 1, { header: 'Cache.add' })
request = webidl.converters.RequestInfo(request)
// 1.
const requests = [request]
// 2.
const responseArrayPromise = this.addAll(requests)
// 3.
return await responseArrayPromise
}
async addAll (requests) {
webidl.brandCheck(this, Cache)
webidl.argumentLengthCheck(arguments, 1, { header: 'Cache.addAll' })
requests = webidl.converters['sequence<RequestInfo>'](requests)
// 1.
const responsePromises = []
// 2.
const requestList = []
// 3.
for (const request of requests) {
if (typeof request === 'string') {
continue
}
// 3.1
const r = request[kState]
// 3.2
if (!urlIsHttpHttpsScheme(r.url) || r.method !== 'GET') {
throw webidl.errors.exception({
header: 'Cache.addAll',
message: 'Expected http/s scheme when method is not GET.'
})
}
}
// 4.
/** @type {ReturnType<typeof fetching>[]} */
const fetchControllers = []
// 5.
for (const request of requests) {
// 5.1
const r = new Request(request)[kState]
// 5.2
if (!urlIsHttpHttpsScheme(r.url)) {
throw webidl.errors.exception({
header: 'Cache.addAll',
message: 'Expected http/s scheme.'
})
}
// 5.4
r.initiator = 'fetch'
r.destination = 'subresource'
// 5.5
requestList.push(r)
// 5.6
const responsePromise = createDeferredPromise()
// 5.7
fetchControllers.push(fetching({
request: r,
dispatcher: getGlobalDispatcher(),
processResponse (response) {
// 1.
if (response.type === 'error' || response.status === 206 || response.status < 200 || response.status > 299) {
responsePromise.reject(webidl.errors.exception({
header: 'Cache.addAll',
message: 'Received an invalid status code or the request failed.'
}))
} else if (response.headersList.contains('vary')) { // 2.
// 2.1
const fieldValues = getFieldValues(response.headersList.get('vary'))
// 2.2
for (const fieldValue of fieldValues) {
// 2.2.1
if (fieldValue === '*') {
responsePromise.reject(webidl.errors.exception({
header: 'Cache.addAll',
message: 'invalid vary field value'
}))
for (const controller of fetchControllers) {
controller.abort()
}
return
}
}
}
},
processResponseEndOfBody (response) {
// 1.
if (response.aborted) {
responsePromise.reject(new DOMException('aborted', 'AbortError'))
return
}
// 2.
responsePromise.resolve(response)
}
}))
// 5.8
responsePromises.push(responsePromise.promise)
}
// 6.
const p = Promise.all(responsePromises)
// 7.
const responses = await p
// 7.1
const operations = []
// 7.2
let index = 0
// 7.3
for (const response of responses) {
// 7.3.1
/** @type {CacheBatchOperation} */
const operation = {
type: 'put', // 7.3.2
request: requestList[index], // 7.3.3
response // 7.3.4
}
operations.push(operation) // 7.3.5
index++ // 7.3.6
}
// 7.5
const cacheJobPromise = createDeferredPromise()
// 7.6.1
let errorData = null
// 7.6.2
try {
this.#batchCacheOperations(operations)
} catch (e) {
errorData = e
}
// 7.6.3
queueMicrotask(() => {
// 7.6.3.1
if (errorData === null) {
cacheJobPromise.resolve(undefined)
} else {
// 7.6.3.2
cacheJobPromise.reject(errorData)
}
})
// 7.7
return cacheJobPromise.promise
}
async put (request, response) {
webidl.brandCheck(this, Cache)
webidl.argumentLengthCheck(arguments, 2, { header: 'Cache.put' })
request = webidl.converters.RequestInfo(request)
response = webidl.converters.Response(response)
// 1.
let innerRequest = null
// 2.
if (request instanceof Request) {
innerRequest = request[kState]
} else { // 3.
innerRequest = new Request(request)[kState]
}
// 4.
if (!urlIsHttpHttpsScheme(innerRequest.url) || innerRequest.method !== 'GET') {
throw webidl.errors.exception({
header: 'Cache.put',
message: 'Expected an http/s scheme when method is not GET'
})
}
// 5.
const innerResponse = response[kState]
// 6.
if (innerResponse.status === 206) {
throw webidl.errors.exception({
header: 'Cache.put',
message: 'Got 206 status'
})
}
// 7.
if (innerResponse.headersList.contains('vary')) {
// 7.1.
const fieldValues = getFieldValues(innerResponse.headersList.get('vary'))
// 7.2.
for (const fieldValue of fieldValues) {
// 7.2.1
if (fieldValue === '*') {
throw webidl.errors.exception({
header: 'Cache.put',
message: 'Got * vary field value'
})
}
}
}
// 8.
if (innerResponse.body && (isDisturbed(innerResponse.body.stream) || innerResponse.body.stream.locked)) {
throw webidl.errors.exception({
header: 'Cache.put',
message: 'Response body is locked or disturbed'
})
}
// 9.
const clonedResponse = cloneResponse(innerResponse)
// 10.
const bodyReadPromise = createDeferredPromise()
// 11.
if (innerResponse.body != null) {
// 11.1
const stream = innerResponse.body.stream
// 11.2
const reader = stream.getReader()
// 11.3
readAllBytes(reader).then(bodyReadPromise.resolve, bodyReadPromise.reject)
} else {
bodyReadPromise.resolve(undefined)
}
// 12.
/** @type {CacheBatchOperation[]} */
const operations = []
// 13.
/** @type {CacheBatchOperation} */
const operation = {
type: 'put', // 14.
request: innerRequest, // 15.
response: clonedResponse // 16.
}
// 17.
operations.push(operation)
// 19.
const bytes = await bodyReadPromise.promise
if (clonedResponse.body != null) {
clonedResponse.body.source = bytes
}
// 19.1
const cacheJobPromise = createDeferredPromise()
// 19.2.1
let errorData = null
// 19.2.2
try {
this.#batchCacheOperations(operations)
} catch (e) {
errorData = e
}
// 19.2.3
queueMicrotask(() => {
// 19.2.3.1
if (errorData === null) {
cacheJobPromise.resolve()
} else { // 19.2.3.2
cacheJobPromise.reject(errorData)
}
})
return cacheJobPromise.promise
}
async delete (request, options = {}) {
webidl.brandCheck(this, Cache)
webidl.argumentLengthCheck(arguments, 1, { header: 'Cache.delete' })
request = webidl.converters.RequestInfo(request)
options = webidl.converters.CacheQueryOptions(options)
/**
* @type {Request}
*/
let r = null
if (request instanceof Request) {
r = request[kState]
if (r.method !== 'GET' && !options.ignoreMethod) {
return false
}
} else {
assert(typeof request === 'string')
r = new Request(request)[kState]
}
/** @type {CacheBatchOperation[]} */
const operations = []
/** @type {CacheBatchOperation} */
const operation = {
type: 'delete',
request: r,
options
}
operations.push(operation)
const cacheJobPromise = createDeferredPromise()
let errorData = null
let requestResponses
try {
requestResponses = this.#batchCacheOperations(operations)
} catch (e) {
errorData = e
}
queueMicrotask(() => {
if (errorData === null) {
cacheJobPromise.resolve(!!requestResponses?.length)
} else {
cacheJobPromise.reject(errorData)
}
})
return cacheJobPromise.promise
}
/**
* @see https://w3c.github.io/ServiceWorker/#dom-cache-keys
* @param {any} request
* @param {import('../../types/cache').CacheQueryOptions} options
* @returns {readonly Request[]}
*/
async keys (request = undefined, options = {}) {
webidl.brandCheck(this, Cache)
if (request !== undefined) request = webidl.converters.RequestInfo(request)
options = webidl.converters.CacheQueryOptions(options)
// 1.
let r = null
// 2.
if (request !== undefined) {
// 2.1
if (request instanceof Request) {
// 2.1.1
r = request[kState]
// 2.1.2
if (r.method !== 'GET' && !options.ignoreMethod) {
return []
}
} else if (typeof request === 'string') { // 2.2
r = new Request(request)[kState]
}
}
// 4.
const promise = createDeferredPromise()
// 5.
// 5.1
const requests = []
// 5.2
if (request === undefined) {
// 5.2.1
for (const requestResponse of this.#relevantRequestResponseList) {
// 5.2.1.1
requests.push(requestResponse[0])
}
} else { // 5.3
// 5.3.1
const requestResponses = this.#queryCache(r, options)
// 5.3.2
for (const requestResponse of requestResponses) {
// 5.3.2.1
requests.push(requestResponse[0])
}
}
// 5.4
queueMicrotask(() => {
// 5.4.1
const requestList = []
// 5.4.2
for (const request of requests) {
const requestObject = new Request('https://a')
requestObject[kState] = request
requestObject[kHeaders][kHeadersList] = request.headersList
requestObject[kHeaders][kGuard] = 'immutable'
requestObject[kRealm] = request.client
// 5.4.2.1
requestList.push(requestObject)
}
// 5.4.3
promise.resolve(Object.freeze(requestList))
})
return promise.promise
}
/**
* @see https://w3c.github.io/ServiceWorker/#batch-cache-operations-algorithm
* @param {CacheBatchOperation[]} operations
* @returns {requestResponseList}
*/
#batchCacheOperations (operations) {
// 1.
const cache = this.#relevantRequestResponseList
// 2.
const backupCache = [...cache]
// 3.
const addedItems = []
// 4.1
const resultList = []
try {
// 4.2
for (const operation of operations) {
// 4.2.1
if (operation.type !== 'delete' && operation.type !== 'put') {
throw webidl.errors.exception({
header: 'Cache.#batchCacheOperations',
message: 'operation type does not match "delete" or "put"'
})
}
// 4.2.2
if (operation.type === 'delete' && operation.response != null) {
throw webidl.errors.exception({
header: 'Cache.#batchCacheOperations',
message: 'delete operation should not have an associated response'
})
}
// 4.2.3
if (this.#queryCache(operation.request, operation.options, addedItems).length) {
throw new DOMException('???', 'InvalidStateError')
}
// 4.2.4
let requestResponses
// 4.2.5
if (operation.type === 'delete') {
// 4.2.5.1
requestResponses = this.#queryCache(operation.request, operation.options)
// TODO: the spec is wrong, this is needed to pass WPTs
if (requestResponses.length === 0) {
return []
}
// 4.2.5.2
for (const requestResponse of requestResponses) {
const idx = cache.indexOf(requestResponse)
assert(idx !== -1)
// 4.2.5.2.1
cache.splice(idx, 1)
}
} else if (operation.type === 'put') { // 4.2.6
// 4.2.6.1
if (operation.response == null) {
throw webidl.errors.exception({
header: 'Cache.#batchCacheOperations',
message: 'put operation should have an associated response'
})
}
// 4.2.6.2
const r = operation.request
// 4.2.6.3
if (!urlIsHttpHttpsScheme(r.url)) {
throw webidl.errors.exception({
header: 'Cache.#batchCacheOperations',
message: 'expected http or https scheme'
})
}
// 4.2.6.4
if (r.method !== 'GET') {
throw webidl.errors.exception({
header: 'Cache.#batchCacheOperations',
message: 'not get method'
})
}
// 4.2.6.5
if (operation.options != null) {
throw webidl.errors.exception({
header: 'Cache.#batchCacheOperations',
message: 'options must not be defined'
})
}
// 4.2.6.6
requestResponses = this.#queryCache(operation.request)
// 4.2.6.7
for (const requestResponse of requestResponses) {
const idx = cache.indexOf(requestResponse)
assert(idx !== -1)
// 4.2.6.7.1
cache.splice(idx, 1)
}
// 4.2.6.8
cache.push([operation.request, operation.response])
// 4.2.6.10
addedItems.push([operation.request, operation.response])
}
// 4.2.7
resultList.push([operation.request, operation.response])
}
// 4.3
return resultList
} catch (e) { // 5.
// 5.1
this.#relevantRequestResponseList.length = 0
// 5.2
this.#relevantRequestResponseList = backupCache
// 5.3
throw e
}
}
/**
* @see https://w3c.github.io/ServiceWorker/#query-cache
* @param {any} requestQuery
* @param {import('../../types/cache').CacheQueryOptions} options
* @param {requestResponseList} targetStorage
* @returns {requestResponseList}
*/
#queryCache (requestQuery, options, targetStorage) {
/** @type {requestResponseList} */
const resultList = []
const storage = targetStorage ?? this.#relevantRequestResponseList
for (const requestResponse of storage) {
const [cachedRequest, cachedResponse] = requestResponse
if (this.#requestMatchesCachedItem(requestQuery, cachedRequest, cachedResponse, options)) {
resultList.push(requestResponse)
}
}
return resultList
}
/**
* @see https://w3c.github.io/ServiceWorker/#request-matches-cached-item-algorithm
* @param {any} requestQuery
* @param {any} request
* @param {any | null} response
* @param {import('../../types/cache').CacheQueryOptions | undefined} options
* @returns {boolean}
*/
#requestMatchesCachedItem (requestQuery, request, response = null, options) {
// if (options?.ignoreMethod === false && request.method === 'GET') {
// return false
// }
const queryURL = new URL(requestQuery.url)
const cachedURL = new URL(request.url)
if (options?.ignoreSearch) {
cachedURL.search = ''
queryURL.search = ''
}
if (!urlEquals(queryURL, cachedURL, true)) {
return false
}
if (
response == null ||
options?.ignoreVary ||
!response.headersList.contains('vary')
) {
return true
}
const fieldValues = getFieldValues(response.headersList.get('vary'))
for (const fieldValue of fieldValues) {
if (fieldValue === '*') {
return false
}
const requestValue = request.headersList.get(fieldValue)
const queryValue = requestQuery.headersList.get(fieldValue)
// If one has the header and the other doesn't, or one has
// a different value than the other, return false
if (requestValue !== queryValue) {
return false
}
}
return true
}
}
Object.defineProperties(Cache.prototype, {
[Symbol.toStringTag]: {
value: 'Cache',
configurable: true
},
match: kEnumerableProperty,
matchAll: kEnumerableProperty,
add: kEnumerableProperty,
addAll: kEnumerableProperty,
put: kEnumerableProperty,
delete: kEnumerableProperty,
keys: kEnumerableProperty
})
const cacheQueryOptionConverters = [
{
key: 'ignoreSearch',
converter: webidl.converters.boolean,
defaultValue: false
},
{
key: 'ignoreMethod',
converter: webidl.converters.boolean,
defaultValue: false
},
{
key: 'ignoreVary',
converter: webidl.converters.boolean,
defaultValue: false
}
]
webidl.converters.CacheQueryOptions = webidl.dictionaryConverter(cacheQueryOptionConverters)
webidl.converters.MultiCacheQueryOptions = webidl.dictionaryConverter([
...cacheQueryOptionConverters,
{
key: 'cacheName',
converter: webidl.converters.DOMString
}
])
webidl.converters.Response = webidl.interfaceConverter(Response)
webidl.converters['sequence<RequestInfo>'] = webidl.sequenceConverter(
webidl.converters.RequestInfo
)
module.exports = {
Cache
}

144
node_modules/undici/lib/cache/cachestorage.js generated vendored Normal file
View File

@ -0,0 +1,144 @@
'use strict'
const { kConstruct } = require('./symbols')
const { Cache } = require('./cache')
const { webidl } = require('../fetch/webidl')
const { kEnumerableProperty } = require('../core/util')
class CacheStorage {
/**
* @see https://w3c.github.io/ServiceWorker/#dfn-relevant-name-to-cache-map
* @type {Map<string, import('./cache').requestResponseList}
*/
#caches = new Map()
constructor () {
if (arguments[0] !== kConstruct) {
webidl.illegalConstructor()
}
}
async match (request, options = {}) {
webidl.brandCheck(this, CacheStorage)
webidl.argumentLengthCheck(arguments, 1, { header: 'CacheStorage.match' })
request = webidl.converters.RequestInfo(request)
options = webidl.converters.MultiCacheQueryOptions(options)
// 1.
if (options.cacheName != null) {
// 1.1.1.1
if (this.#caches.has(options.cacheName)) {
// 1.1.1.1.1
const cacheList = this.#caches.get(options.cacheName)
const cache = new Cache(kConstruct, cacheList)
return await cache.match(request, options)
}
} else { // 2.
// 2.2
for (const cacheList of this.#caches.values()) {
const cache = new Cache(kConstruct, cacheList)
// 2.2.1.2
const response = await cache.match(request, options)
if (response !== undefined) {
return response
}
}
}
}
/**
* @see https://w3c.github.io/ServiceWorker/#cache-storage-has
* @param {string} cacheName
* @returns {Promise<boolean>}
*/
async has (cacheName) {
webidl.brandCheck(this, CacheStorage)
webidl.argumentLengthCheck(arguments, 1, { header: 'CacheStorage.has' })
cacheName = webidl.converters.DOMString(cacheName)
// 2.1.1
// 2.2
return this.#caches.has(cacheName)
}
/**
* @see https://w3c.github.io/ServiceWorker/#dom-cachestorage-open
* @param {string} cacheName
* @returns {Promise<Cache>}
*/
async open (cacheName) {
webidl.brandCheck(this, CacheStorage)
webidl.argumentLengthCheck(arguments, 1, { header: 'CacheStorage.open' })
cacheName = webidl.converters.DOMString(cacheName)
// 2.1
if (this.#caches.has(cacheName)) {
// await caches.open('v1') !== await caches.open('v1')
// 2.1.1
const cache = this.#caches.get(cacheName)
// 2.1.1.1
return new Cache(kConstruct, cache)
}
// 2.2
const cache = []
// 2.3
this.#caches.set(cacheName, cache)
// 2.4
return new Cache(kConstruct, cache)
}
/**
* @see https://w3c.github.io/ServiceWorker/#cache-storage-delete
* @param {string} cacheName
* @returns {Promise<boolean>}
*/
async delete (cacheName) {
webidl.brandCheck(this, CacheStorage)
webidl.argumentLengthCheck(arguments, 1, { header: 'CacheStorage.delete' })
cacheName = webidl.converters.DOMString(cacheName)
return this.#caches.delete(cacheName)
}
/**
* @see https://w3c.github.io/ServiceWorker/#cache-storage-keys
* @returns {string[]}
*/
async keys () {
webidl.brandCheck(this, CacheStorage)
// 2.1
const keys = this.#caches.keys()
// 2.2
return [...keys]
}
}
Object.defineProperties(CacheStorage.prototype, {
[Symbol.toStringTag]: {
value: 'CacheStorage',
configurable: true
},
match: kEnumerableProperty,
has: kEnumerableProperty,
open: kEnumerableProperty,
delete: kEnumerableProperty,
keys: kEnumerableProperty
})
module.exports = {
CacheStorage
}

5
node_modules/undici/lib/cache/symbols.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
'use strict'
module.exports = {
kConstruct: Symbol('constructable')
}

49
node_modules/undici/lib/cache/util.js generated vendored Normal file
View File

@ -0,0 +1,49 @@
'use strict'
const assert = require('assert')
const { URLSerializer } = require('../fetch/dataURL')
const { isValidHeaderName } = require('../fetch/util')
/**
* @see https://url.spec.whatwg.org/#concept-url-equals
* @param {URL} A
* @param {URL} B
* @param {boolean | undefined} excludeFragment
* @returns {boolean}
*/
function urlEquals (A, B, excludeFragment = false) {
const serializedA = URLSerializer(A, excludeFragment)
const serializedB = URLSerializer(B, excludeFragment)
return serializedA === serializedB
}
/**
* @see https://github.com/chromium/chromium/blob/694d20d134cb553d8d89e5500b9148012b1ba299/content/browser/cache_storage/cache_storage_cache.cc#L260-L262
* @param {string} header
*/
function fieldValues (header) {
assert(header !== null)
const values = []
for (let value of header.split(',')) {
value = value.trim()
if (!value.length) {
continue
} else if (!isValidHeaderName(value)) {
continue
}
values.push(value)
}
return values
}
module.exports = {
urlEquals,
fieldValues
}

2287
node_modules/undici/lib/client.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

48
node_modules/undici/lib/compat/dispatcher-weakref.js generated vendored Normal file
View File

@ -0,0 +1,48 @@
'use strict'
/* istanbul ignore file: only for Node 12 */
const { kConnected, kSize } = require('../core/symbols')
class CompatWeakRef {
constructor (value) {
this.value = value
}
deref () {
return this.value[kConnected] === 0 && this.value[kSize] === 0
? undefined
: this.value
}
}
class CompatFinalizer {
constructor (finalizer) {
this.finalizer = finalizer
}
register (dispatcher, key) {
if (dispatcher.on) {
dispatcher.on('disconnect', () => {
if (dispatcher[kConnected] === 0 && dispatcher[kSize] === 0) {
this.finalizer(key)
}
})
}
}
}
module.exports = function () {
// FIXME: remove workaround when the Node bug is fixed
// https://github.com/nodejs/node/issues/49344#issuecomment-1741776308
if (process.env.NODE_V8_COVERAGE) {
return {
WeakRef: CompatWeakRef,
FinalizationRegistry: CompatFinalizer
}
}
return {
WeakRef: global.WeakRef || CompatWeakRef,
FinalizationRegistry: global.FinalizationRegistry || CompatFinalizer
}
}

12
node_modules/undici/lib/cookies/constants.js generated vendored Normal file
View File

@ -0,0 +1,12 @@
'use strict'
// https://wicg.github.io/cookie-store/#cookie-maximum-attribute-value-size
const maxAttributeValueSize = 1024
// https://wicg.github.io/cookie-store/#cookie-maximum-name-value-pair-size
const maxNameValuePairSize = 4096
module.exports = {
maxAttributeValueSize,
maxNameValuePairSize
}

184
node_modules/undici/lib/cookies/index.js generated vendored Normal file
View File

@ -0,0 +1,184 @@
'use strict'
const { parseSetCookie } = require('./parse')
const { stringify, getHeadersList } = require('./util')
const { webidl } = require('../fetch/webidl')
const { Headers } = require('../fetch/headers')
/**
* @typedef {Object} Cookie
* @property {string} name
* @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
*/
/**
* @param {Headers} headers
* @returns {Record<string, string>}
*/
function getCookies (headers) {
webidl.argumentLengthCheck(arguments, 1, { header: 'getCookies' })
webidl.brandCheck(headers, Headers, { strict: false })
const cookie = headers.get('cookie')
const out = {}
if (!cookie) {
return out
}
for (const piece of cookie.split(';')) {
const [name, ...value] = piece.split('=')
out[name.trim()] = value.join('=')
}
return out
}
/**
* @param {Headers} headers
* @param {string} name
* @param {{ path?: string, domain?: string }|undefined} attributes
* @returns {void}
*/
function deleteCookie (headers, name, attributes) {
webidl.argumentLengthCheck(arguments, 2, { header: 'deleteCookie' })
webidl.brandCheck(headers, Headers, { strict: false })
name = webidl.converters.DOMString(name)
attributes = webidl.converters.DeleteCookieAttributes(attributes)
// Matches behavior of
// https://github.com/denoland/deno_std/blob/63827b16330b82489a04614027c33b7904e08be5/http/cookie.ts#L278
setCookie(headers, {
name,
value: '',
expires: new Date(0),
...attributes
})
}
/**
* @param {Headers} headers
* @returns {Cookie[]}
*/
function getSetCookies (headers) {
webidl.argumentLengthCheck(arguments, 1, { header: 'getSetCookies' })
webidl.brandCheck(headers, Headers, { strict: false })
const cookies = getHeadersList(headers).cookies
if (!cookies) {
return []
}
// In older versions of undici, cookies is a list of name:value.
return cookies.map((pair) => parseSetCookie(Array.isArray(pair) ? pair[1] : pair))
}
/**
* @param {Headers} headers
* @param {Cookie} cookie
* @returns {void}
*/
function setCookie (headers, cookie) {
webidl.argumentLengthCheck(arguments, 2, { header: 'setCookie' })
webidl.brandCheck(headers, Headers, { strict: false })
cookie = webidl.converters.Cookie(cookie)
const str = stringify(cookie)
if (str) {
headers.append('Set-Cookie', stringify(cookie))
}
}
webidl.converters.DeleteCookieAttributes = webidl.dictionaryConverter([
{
converter: webidl.nullableConverter(webidl.converters.DOMString),
key: 'path',
defaultValue: null
},
{
converter: webidl.nullableConverter(webidl.converters.DOMString),
key: 'domain',
defaultValue: null
}
])
webidl.converters.Cookie = webidl.dictionaryConverter([
{
converter: webidl.converters.DOMString,
key: 'name'
},
{
converter: webidl.converters.DOMString,
key: 'value'
},
{
converter: webidl.nullableConverter((value) => {
if (typeof value === 'number') {
return webidl.converters['unsigned long long'](value)
}
return new Date(value)
}),
key: 'expires',
defaultValue: null
},
{
converter: webidl.nullableConverter(webidl.converters['long long']),
key: 'maxAge',
defaultValue: null
},
{
converter: webidl.nullableConverter(webidl.converters.DOMString),
key: 'domain',
defaultValue: null
},
{
converter: webidl.nullableConverter(webidl.converters.DOMString),
key: 'path',
defaultValue: null
},
{
converter: webidl.nullableConverter(webidl.converters.boolean),
key: 'secure',
defaultValue: null
},
{
converter: webidl.nullableConverter(webidl.converters.boolean),
key: 'httpOnly',
defaultValue: null
},
{
converter: webidl.converters.USVString,
key: 'sameSite',
allowedValues: ['Strict', 'Lax', 'None']
},
{
converter: webidl.sequenceConverter(webidl.converters.DOMString),
key: 'unparsed',
defaultValue: []
}
])
module.exports = {
getCookies,
deleteCookie,
getSetCookies,
setCookie
}

317
node_modules/undici/lib/cookies/parse.js generated vendored Normal file
View File

@ -0,0 +1,317 @@
'use strict'
const { maxNameValuePairSize, maxAttributeValueSize } = require('./constants')
const { isCTLExcludingHtab } = require('./util')
const { collectASequenceOfCodePointsFast } = require('../fetch/dataURL')
const assert = require('assert')
/**
* @description Parses the field-value attributes of a set-cookie header string.
* @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4
* @param {string} header
* @returns if the header is invalid, null will be returned
*/
function parseSetCookie (header) {
// 1. If the set-cookie-string contains a %x00-08 / %x0A-1F / %x7F
// character (CTL characters excluding HTAB): Abort these steps and
// ignore the set-cookie-string entirely.
if (isCTLExcludingHtab(header)) {
return null
}
let nameValuePair = ''
let unparsedAttributes = ''
let name = ''
let value = ''
// 2. If the set-cookie-string contains a %x3B (";") character:
if (header.includes(';')) {
// 1. The name-value-pair string consists of the characters up to,
// but not including, the first %x3B (";"), and the unparsed-
// attributes consist of the remainder of the set-cookie-string
// (including the %x3B (";") in question).
const position = { position: 0 }
nameValuePair = collectASequenceOfCodePointsFast(';', header, position)
unparsedAttributes = header.slice(position.position)
} else {
// Otherwise:
// 1. The name-value-pair string consists of all the characters
// contained in the set-cookie-string, and the unparsed-
// attributes is the empty string.
nameValuePair = header
}
// 3. If the name-value-pair string lacks a %x3D ("=") character, then
// the name string is empty, and the value string is the value of
// name-value-pair.
if (!nameValuePair.includes('=')) {
value = nameValuePair
} else {
// Otherwise, the name string consists of the characters up to, but
// not including, the first %x3D ("=") character, and the (possibly
// empty) value string consists of the characters after the first
// %x3D ("=") character.
const position = { position: 0 }
name = collectASequenceOfCodePointsFast(
'=',
nameValuePair,
position
)
value = nameValuePair.slice(position.position + 1)
}
// 4. Remove any leading or trailing WSP characters from the name
// string and the value string.
name = name.trim()
value = value.trim()
// 5. If the sum of the lengths of the name string and the value string
// is more than 4096 octets, abort these steps and ignore the set-
// cookie-string entirely.
if (name.length + value.length > maxNameValuePairSize) {
return null
}
// 6. The cookie-name is the name string, and the cookie-value is the
// value string.
return {
name, value, ...parseUnparsedAttributes(unparsedAttributes)
}
}
/**
* Parses the remaining attributes of a set-cookie header
* @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4
* @param {string} unparsedAttributes
* @param {[Object.<string, unknown>]={}} cookieAttributeList
*/
function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {}) {
// 1. If the unparsed-attributes string is empty, skip the rest of
// these steps.
if (unparsedAttributes.length === 0) {
return cookieAttributeList
}
// 2. Discard the first character of the unparsed-attributes (which
// will be a %x3B (";") character).
assert(unparsedAttributes[0] === ';')
unparsedAttributes = unparsedAttributes.slice(1)
let cookieAv = ''
// 3. If the remaining unparsed-attributes contains a %x3B (";")
// character:
if (unparsedAttributes.includes(';')) {
// 1. Consume the characters of the unparsed-attributes up to, but
// not including, the first %x3B (";") character.
cookieAv = collectASequenceOfCodePointsFast(
';',
unparsedAttributes,
{ position: 0 }
)
unparsedAttributes = unparsedAttributes.slice(cookieAv.length)
} else {
// Otherwise:
// 1. Consume the remainder of the unparsed-attributes.
cookieAv = unparsedAttributes
unparsedAttributes = ''
}
// Let the cookie-av string be the characters consumed in this step.
let attributeName = ''
let attributeValue = ''
// 4. If the cookie-av string contains a %x3D ("=") character:
if (cookieAv.includes('=')) {
// 1. The (possibly empty) attribute-name string consists of the
// characters up to, but not including, the first %x3D ("=")
// character, and the (possibly empty) attribute-value string
// consists of the characters after the first %x3D ("=")
// character.
const position = { position: 0 }
attributeName = collectASequenceOfCodePointsFast(
'=',
cookieAv,
position
)
attributeValue = cookieAv.slice(position.position + 1)
} else {
// Otherwise:
// 1. The attribute-name string consists of the entire cookie-av
// string, and the attribute-value string is empty.
attributeName = cookieAv
}
// 5. Remove any leading or trailing WSP characters from the attribute-
// name string and the attribute-value string.
attributeName = attributeName.trim()
attributeValue = attributeValue.trim()
// 6. If the attribute-value is longer than 1024 octets, ignore the
// cookie-av string and return to Step 1 of this algorithm.
if (attributeValue.length > maxAttributeValueSize) {
return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)
}
// 7. Process the attribute-name and attribute-value according to the
// requirements in the following subsections. (Notice that
// attributes with unrecognized attribute-names are ignored.)
const attributeNameLowercase = attributeName.toLowerCase()
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.1
// If the attribute-name case-insensitively matches the string
// "Expires", the user agent MUST process the cookie-av as follows.
if (attributeNameLowercase === 'expires') {
// 1. Let the expiry-time be the result of parsing the attribute-value
// as cookie-date (see Section 5.1.1).
const expiryTime = new Date(attributeValue)
// 2. If the attribute-value failed to parse as a cookie date, ignore
// the cookie-av.
cookieAttributeList.expires = expiryTime
} else if (attributeNameLowercase === 'max-age') {
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.2
// If the attribute-name case-insensitively matches the string "Max-
// Age", the user agent MUST process the cookie-av as follows.
// 1. If the first character of the attribute-value is not a DIGIT or a
// "-" character, ignore the cookie-av.
const charCode = attributeValue.charCodeAt(0)
if ((charCode < 48 || charCode > 57) && attributeValue[0] !== '-') {
return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)
}
// 2. If the remainder of attribute-value contains a non-DIGIT
// character, ignore the cookie-av.
if (!/^\d+$/.test(attributeValue)) {
return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)
}
// 3. Let delta-seconds be the attribute-value converted to an integer.
const deltaSeconds = Number(attributeValue)
// 4. Let cookie-age-limit be the maximum age of the cookie (which
// SHOULD be 400 days or less, see Section 4.1.2.2).
// 5. Set delta-seconds to the smaller of its present value and cookie-
// age-limit.
// deltaSeconds = Math.min(deltaSeconds * 1000, maxExpiresMs)
// 6. If delta-seconds is less than or equal to zero (0), let expiry-
// time be the earliest representable date and time. Otherwise, let
// the expiry-time be the current date and time plus delta-seconds
// seconds.
// const expiryTime = deltaSeconds <= 0 ? Date.now() : Date.now() + deltaSeconds
// 7. Append an attribute to the cookie-attribute-list with an
// attribute-name of Max-Age and an attribute-value of expiry-time.
cookieAttributeList.maxAge = deltaSeconds
} else if (attributeNameLowercase === 'domain') {
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.3
// If the attribute-name case-insensitively matches the string "Domain",
// the user agent MUST process the cookie-av as follows.
// 1. Let cookie-domain be the attribute-value.
let cookieDomain = attributeValue
// 2. If cookie-domain starts with %x2E ("."), let cookie-domain be
// cookie-domain without its leading %x2E (".").
if (cookieDomain[0] === '.') {
cookieDomain = cookieDomain.slice(1)
}
// 3. Convert the cookie-domain to lower case.
cookieDomain = cookieDomain.toLowerCase()
// 4. Append an attribute to the cookie-attribute-list with an
// attribute-name of Domain and an attribute-value of cookie-domain.
cookieAttributeList.domain = cookieDomain
} else if (attributeNameLowercase === 'path') {
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.4
// If the attribute-name case-insensitively matches the string "Path",
// the user agent MUST process the cookie-av as follows.
// 1. If the attribute-value is empty or if the first character of the
// attribute-value is not %x2F ("/"):
let cookiePath = ''
if (attributeValue.length === 0 || attributeValue[0] !== '/') {
// 1. Let cookie-path be the default-path.
cookiePath = '/'
} else {
// Otherwise:
// 1. Let cookie-path be the attribute-value.
cookiePath = attributeValue
}
// 2. Append an attribute to the cookie-attribute-list with an
// attribute-name of Path and an attribute-value of cookie-path.
cookieAttributeList.path = cookiePath
} else if (attributeNameLowercase === 'secure') {
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.5
// If the attribute-name case-insensitively matches the string "Secure",
// the user agent MUST append an attribute to the cookie-attribute-list
// with an attribute-name of Secure and an empty attribute-value.
cookieAttributeList.secure = true
} else if (attributeNameLowercase === 'httponly') {
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.6
// If the attribute-name case-insensitively matches the string
// "HttpOnly", the user agent MUST append an attribute to the cookie-
// attribute-list with an attribute-name of HttpOnly and an empty
// attribute-value.
cookieAttributeList.httpOnly = true
} else if (attributeNameLowercase === 'samesite') {
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.7
// If the attribute-name case-insensitively matches the string
// "SameSite", the user agent MUST process the cookie-av as follows:
// 1. Let enforcement be "Default".
let enforcement = 'Default'
const attributeValueLowercase = attributeValue.toLowerCase()
// 2. If cookie-av's attribute-value is a case-insensitive match for
// "None", set enforcement to "None".
if (attributeValueLowercase.includes('none')) {
enforcement = 'None'
}
// 3. If cookie-av's attribute-value is a case-insensitive match for
// "Strict", set enforcement to "Strict".
if (attributeValueLowercase.includes('strict')) {
enforcement = 'Strict'
}
// 4. If cookie-av's attribute-value is a case-insensitive match for
// "Lax", set enforcement to "Lax".
if (attributeValueLowercase.includes('lax')) {
enforcement = 'Lax'
}
// 5. Append an attribute to the cookie-attribute-list with an
// attribute-name of "SameSite" and an attribute-value of
// enforcement.
cookieAttributeList.sameSite = enforcement
} else {
cookieAttributeList.unparsed ??= []
cookieAttributeList.unparsed.push(`${attributeName}=${attributeValue}`)
}
// 8. Return to Step 1 of this algorithm.
return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)
}
module.exports = {
parseSetCookie,
parseUnparsedAttributes
}

291
node_modules/undici/lib/cookies/util.js generated vendored Normal file
View File

@ -0,0 +1,291 @@
'use strict'
const assert = require('assert')
const { kHeadersList } = require('../core/symbols')
function isCTLExcludingHtab (value) {
if (value.length === 0) {
return false
}
for (const char of value) {
const code = char.charCodeAt(0)
if (
(code >= 0x00 || code <= 0x08) ||
(code >= 0x0A || code <= 0x1F) ||
code === 0x7F
) {
return false
}
}
}
/**
CHAR = <any US-ASCII character (octets 0 - 127)>
token = 1*<any CHAR except CTLs or separators>
separators = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
* @param {string} name
*/
function validateCookieName (name) {
for (const char of name) {
const code = char.charCodeAt(0)
if (
(code <= 0x20 || code > 0x7F) ||
char === '(' ||
char === ')' ||
char === '>' ||
char === '<' ||
char === '@' ||
char === ',' ||
char === ';' ||
char === ':' ||
char === '\\' ||
char === '"' ||
char === '/' ||
char === '[' ||
char === ']' ||
char === '?' ||
char === '=' ||
char === '{' ||
char === '}'
) {
throw new Error('Invalid cookie name')
}
}
}
/**
cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
; US-ASCII characters excluding CTLs,
; whitespace DQUOTE, comma, semicolon,
; and backslash
* @param {string} value
*/
function validateCookieValue (value) {
for (const char of value) {
const code = char.charCodeAt(0)
if (
code < 0x21 || // exclude CTLs (0-31)
code === 0x22 ||
code === 0x2C ||
code === 0x3B ||
code === 0x5C ||
code > 0x7E // non-ascii
) {
throw new Error('Invalid header value')
}
}
}
/**
* path-value = <any CHAR except CTLs or ";">
* @param {string} path
*/
function validateCookiePath (path) {
for (const char of path) {
const code = char.charCodeAt(0)
if (code < 0x21 || char === ';') {
throw new Error('Invalid cookie path')
}
}
}
/**
* I have no idea why these values aren't allowed to be honest,
* but Deno tests these. - Khafra
* @param {string} domain
*/
function validateCookieDomain (domain) {
if (
domain.startsWith('-') ||
domain.endsWith('.') ||
domain.endsWith('-')
) {
throw new Error('Invalid cookie domain')
}
}
/**
* @see https://www.rfc-editor.org/rfc/rfc7231#section-7.1.1.1
* @param {number|Date} date
IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT
; fixed length/zone/capitalization subset of the format
; see Section 3.3 of [RFC5322]
day-name = %x4D.6F.6E ; "Mon", case-sensitive
/ %x54.75.65 ; "Tue", case-sensitive
/ %x57.65.64 ; "Wed", case-sensitive
/ %x54.68.75 ; "Thu", case-sensitive
/ %x46.72.69 ; "Fri", case-sensitive
/ %x53.61.74 ; "Sat", case-sensitive
/ %x53.75.6E ; "Sun", case-sensitive
date1 = day SP month SP year
; e.g., 02 Jun 1982
day = 2DIGIT
month = %x4A.61.6E ; "Jan", case-sensitive
/ %x46.65.62 ; "Feb", case-sensitive
/ %x4D.61.72 ; "Mar", case-sensitive
/ %x41.70.72 ; "Apr", case-sensitive
/ %x4D.61.79 ; "May", case-sensitive
/ %x4A.75.6E ; "Jun", case-sensitive
/ %x4A.75.6C ; "Jul", case-sensitive
/ %x41.75.67 ; "Aug", case-sensitive
/ %x53.65.70 ; "Sep", case-sensitive
/ %x4F.63.74 ; "Oct", case-sensitive
/ %x4E.6F.76 ; "Nov", case-sensitive
/ %x44.65.63 ; "Dec", case-sensitive
year = 4DIGIT
GMT = %x47.4D.54 ; "GMT", case-sensitive
time-of-day = hour ":" minute ":" second
; 00:00:00 - 23:59:60 (leap second)
hour = 2DIGIT
minute = 2DIGIT
second = 2DIGIT
*/
function toIMFDate (date) {
if (typeof date === 'number') {
date = new Date(date)
}
const days = [
'Sun', 'Mon', 'Tue', 'Wed',
'Thu', 'Fri', 'Sat'
]
const months = [
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
]
const dayName = days[date.getUTCDay()]
const day = date.getUTCDate().toString().padStart(2, '0')
const month = months[date.getUTCMonth()]
const year = date.getUTCFullYear()
const hour = date.getUTCHours().toString().padStart(2, '0')
const minute = date.getUTCMinutes().toString().padStart(2, '0')
const second = date.getUTCSeconds().toString().padStart(2, '0')
return `${dayName}, ${day} ${month} ${year} ${hour}:${minute}:${second} GMT`
}
/**
max-age-av = "Max-Age=" non-zero-digit *DIGIT
; In practice, both expires-av and max-age-av
; are limited to dates representable by the
; user agent.
* @param {number} maxAge
*/
function validateCookieMaxAge (maxAge) {
if (maxAge < 0) {
throw new Error('Invalid cookie max-age')
}
}
/**
* @see https://www.rfc-editor.org/rfc/rfc6265#section-4.1.1
* @param {import('./index').Cookie} cookie
*/
function stringify (cookie) {
if (cookie.name.length === 0) {
return null
}
validateCookieName(cookie.name)
validateCookieValue(cookie.value)
const out = [`${cookie.name}=${cookie.value}`]
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.2
if (cookie.name.startsWith('__Secure-')) {
cookie.secure = true
}
if (cookie.name.startsWith('__Host-')) {
cookie.secure = true
cookie.domain = null
cookie.path = '/'
}
if (cookie.secure) {
out.push('Secure')
}
if (cookie.httpOnly) {
out.push('HttpOnly')
}
if (typeof cookie.maxAge === 'number') {
validateCookieMaxAge(cookie.maxAge)
out.push(`Max-Age=${cookie.maxAge}`)
}
if (cookie.domain) {
validateCookieDomain(cookie.domain)
out.push(`Domain=${cookie.domain}`)
}
if (cookie.path) {
validateCookiePath(cookie.path)
out.push(`Path=${cookie.path}`)
}
if (cookie.expires && cookie.expires.toString() !== 'Invalid Date') {
out.push(`Expires=${toIMFDate(cookie.expires)}`)
}
if (cookie.sameSite) {
out.push(`SameSite=${cookie.sameSite}`)
}
for (const part of cookie.unparsed) {
if (!part.includes('=')) {
throw new Error('Invalid unparsed')
}
const [key, ...value] = part.split('=')
out.push(`${key.trim()}=${value.join('=')}`)
}
return out.join('; ')
}
let kHeadersListNode
function getHeadersList (headers) {
if (headers[kHeadersList]) {
return headers[kHeadersList]
}
if (!kHeadersListNode) {
kHeadersListNode = Object.getOwnPropertySymbols(headers).find(
(symbol) => symbol.description === 'headers list'
)
assert(kHeadersListNode, 'Headers cannot be parsed')
}
const headersList = headers[kHeadersListNode]
assert(headersList)
return headersList
}
module.exports = {
isCTLExcludingHtab,
stringify,
getHeadersList
}

189
node_modules/undici/lib/core/connect.js generated vendored Normal file
View File

@ -0,0 +1,189 @@
'use strict'
const net = require('net')
const assert = require('assert')
const util = require('./util')
const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
let tls // include tls conditionally since it is not always available
// TODO: session re-use does not wait for the first
// connection to resolve the session and might therefore
// resolve the same servername multiple times even when
// re-use is enabled.
let SessionCache
// FIXME: remove workaround when the Node bug is fixed
// https://github.com/nodejs/node/issues/49344#issuecomment-1741776308
if (global.FinalizationRegistry && !process.env.NODE_V8_COVERAGE) {
SessionCache = class WeakSessionCache {
constructor (maxCachedSessions) {
this._maxCachedSessions = maxCachedSessions
this._sessionCache = new Map()
this._sessionRegistry = new global.FinalizationRegistry((key) => {
if (this._sessionCache.size < this._maxCachedSessions) {
return
}
const ref = this._sessionCache.get(key)
if (ref !== undefined && ref.deref() === undefined) {
this._sessionCache.delete(key)
}
})
}
get (sessionKey) {
const ref = this._sessionCache.get(sessionKey)
return ref ? ref.deref() : null
}
set (sessionKey, session) {
if (this._maxCachedSessions === 0) {
return
}
this._sessionCache.set(sessionKey, new WeakRef(session))
this._sessionRegistry.register(session, sessionKey)
}
}
} else {
SessionCache = class SimpleSessionCache {
constructor (maxCachedSessions) {
this._maxCachedSessions = maxCachedSessions
this._sessionCache = new Map()
}
get (sessionKey) {
return this._sessionCache.get(sessionKey)
}
set (sessionKey, session) {
if (this._maxCachedSessions === 0) {
return
}
if (this._sessionCache.size >= this._maxCachedSessions) {
// remove the oldest session
const { value: oldestKey } = this._sessionCache.keys().next()
this._sessionCache.delete(oldestKey)
}
this._sessionCache.set(sessionKey, session)
}
}
}
function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, ...opts }) {
if (maxCachedSessions != null && (!Number.isInteger(maxCachedSessions) || maxCachedSessions < 0)) {
throw new InvalidArgumentError('maxCachedSessions must be a positive integer or zero')
}
const options = { path: socketPath, ...opts }
const sessionCache = new SessionCache(maxCachedSessions == null ? 100 : maxCachedSessions)
timeout = timeout == null ? 10e3 : timeout
allowH2 = allowH2 != null ? allowH2 : false
return function connect ({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) {
let socket
if (protocol === 'https:') {
if (!tls) {
tls = require('tls')
}
servername = servername || options.servername || util.getServerName(host) || null
const sessionKey = servername || hostname
const session = sessionCache.get(sessionKey) || null
assert(sessionKey)
socket = tls.connect({
highWaterMark: 16384, // TLS in node can't have bigger HWM anyway...
...options,
servername,
session,
localAddress,
// TODO(HTTP/2): Add support for h2c
ALPNProtocols: allowH2 ? ['http/1.1', 'h2'] : ['http/1.1'],
socket: httpSocket, // upgrade socket connection
port: port || 443,
host: hostname
})
socket
.on('session', function (session) {
// TODO (fix): Can a session become invalid once established? Don't think so?
sessionCache.set(sessionKey, session)
})
} else {
assert(!httpSocket, 'httpSocket can only be sent on TLS update')
socket = net.connect({
highWaterMark: 64 * 1024, // Same as nodejs fs streams.
...options,
localAddress,
port: port || 80,
host: hostname
})
}
// Set TCP keep alive options on the socket here instead of in connect() for the case of assigning the socket
if (options.keepAlive == null || options.keepAlive) {
const keepAliveInitialDelay = options.keepAliveInitialDelay === undefined ? 60e3 : options.keepAliveInitialDelay
socket.setKeepAlive(true, keepAliveInitialDelay)
}
const cancelTimeout = setupTimeout(() => onConnectTimeout(socket), timeout)
socket
.setNoDelay(true)
.once(protocol === 'https:' ? 'secureConnect' : 'connect', function () {
cancelTimeout()
if (callback) {
const cb = callback
callback = null
cb(null, this)
}
})
.on('error', function (err) {
cancelTimeout()
if (callback) {
const cb = callback
callback = null
cb(err)
}
})
return socket
}
}
function setupTimeout (onConnectTimeout, timeout) {
if (!timeout) {
return () => {}
}
let s1 = null
let s2 = null
const timeoutId = setTimeout(() => {
// setImmediate is added to make sure that we priotorise socket error events over timeouts
s1 = setImmediate(() => {
if (process.platform === 'win32') {
// Windows needs an extra setImmediate probably due to implementation differences in the socket logic
s2 = setImmediate(() => onConnectTimeout())
} else {
onConnectTimeout()
}
})
}, timeout)
return () => {
clearTimeout(timeoutId)
clearImmediate(s1)
clearImmediate(s2)
}
}
function onConnectTimeout (socket) {
util.destroy(socket, new ConnectTimeoutError())
}
module.exports = buildConnector

216
node_modules/undici/lib/core/errors.js generated vendored Normal file
View File

@ -0,0 +1,216 @@
'use strict'
class UndiciError extends Error {
constructor (message) {
super(message)
this.name = 'UndiciError'
this.code = 'UND_ERR'
}
}
class ConnectTimeoutError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, ConnectTimeoutError)
this.name = 'ConnectTimeoutError'
this.message = message || 'Connect Timeout Error'
this.code = 'UND_ERR_CONNECT_TIMEOUT'
}
}
class HeadersTimeoutError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, HeadersTimeoutError)
this.name = 'HeadersTimeoutError'
this.message = message || 'Headers Timeout Error'
this.code = 'UND_ERR_HEADERS_TIMEOUT'
}
}
class HeadersOverflowError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, HeadersOverflowError)
this.name = 'HeadersOverflowError'
this.message = message || 'Headers Overflow Error'
this.code = 'UND_ERR_HEADERS_OVERFLOW'
}
}
class BodyTimeoutError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, BodyTimeoutError)
this.name = 'BodyTimeoutError'
this.message = message || 'Body Timeout Error'
this.code = 'UND_ERR_BODY_TIMEOUT'
}
}
class ResponseStatusCodeError extends UndiciError {
constructor (message, statusCode, headers, body) {
super(message)
Error.captureStackTrace(this, ResponseStatusCodeError)
this.name = 'ResponseStatusCodeError'
this.message = message || 'Response Status Code Error'
this.code = 'UND_ERR_RESPONSE_STATUS_CODE'
this.body = body
this.status = statusCode
this.statusCode = statusCode
this.headers = headers
}
}
class InvalidArgumentError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, InvalidArgumentError)
this.name = 'InvalidArgumentError'
this.message = message || 'Invalid Argument Error'
this.code = 'UND_ERR_INVALID_ARG'
}
}
class InvalidReturnValueError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, InvalidReturnValueError)
this.name = 'InvalidReturnValueError'
this.message = message || 'Invalid Return Value Error'
this.code = 'UND_ERR_INVALID_RETURN_VALUE'
}
}
class RequestAbortedError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, RequestAbortedError)
this.name = 'AbortError'
this.message = message || 'Request aborted'
this.code = 'UND_ERR_ABORTED'
}
}
class InformationalError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, InformationalError)
this.name = 'InformationalError'
this.message = message || 'Request information'
this.code = 'UND_ERR_INFO'
}
}
class RequestContentLengthMismatchError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, RequestContentLengthMismatchError)
this.name = 'RequestContentLengthMismatchError'
this.message = message || 'Request body length does not match content-length header'
this.code = 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
}
}
class ResponseContentLengthMismatchError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, ResponseContentLengthMismatchError)
this.name = 'ResponseContentLengthMismatchError'
this.message = message || 'Response body length does not match content-length header'
this.code = 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH'
}
}
class ClientDestroyedError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, ClientDestroyedError)
this.name = 'ClientDestroyedError'
this.message = message || 'The client is destroyed'
this.code = 'UND_ERR_DESTROYED'
}
}
class ClientClosedError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, ClientClosedError)
this.name = 'ClientClosedError'
this.message = message || 'The client is closed'
this.code = 'UND_ERR_CLOSED'
}
}
class SocketError extends UndiciError {
constructor (message, socket) {
super(message)
Error.captureStackTrace(this, SocketError)
this.name = 'SocketError'
this.message = message || 'Socket error'
this.code = 'UND_ERR_SOCKET'
this.socket = socket
}
}
class NotSupportedError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, NotSupportedError)
this.name = 'NotSupportedError'
this.message = message || 'Not supported error'
this.code = 'UND_ERR_NOT_SUPPORTED'
}
}
class BalancedPoolMissingUpstreamError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, NotSupportedError)
this.name = 'MissingUpstreamError'
this.message = message || 'No upstream has been added to the BalancedPool'
this.code = 'UND_ERR_BPL_MISSING_UPSTREAM'
}
}
class HTTPParserError extends Error {
constructor (message, code, data) {
super(message)
Error.captureStackTrace(this, HTTPParserError)
this.name = 'HTTPParserError'
this.code = code ? `HPE_${code}` : undefined
this.data = data ? data.toString() : undefined
}
}
class ResponseExceededMaxSizeError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, ResponseExceededMaxSizeError)
this.name = 'ResponseExceededMaxSizeError'
this.message = message || 'Response content exceeded max size'
this.code = 'UND_ERR_RES_EXCEEDED_MAX_SIZE'
}
}
module.exports = {
HTTPParserError,
UndiciError,
HeadersTimeoutError,
HeadersOverflowError,
BodyTimeoutError,
RequestContentLengthMismatchError,
ConnectTimeoutError,
ResponseStatusCodeError,
InvalidArgumentError,
InvalidReturnValueError,
RequestAbortedError,
ClientDestroyedError,
ClientClosedError,
InformationalError,
SocketError,
NotSupportedError,
ResponseContentLengthMismatchError,
BalancedPoolMissingUpstreamError,
ResponseExceededMaxSizeError
}

483
node_modules/undici/lib/core/request.js generated vendored Normal file
View File

@ -0,0 +1,483 @@
'use strict'
const {
InvalidArgumentError,
NotSupportedError
} = require('./errors')
const assert = require('assert')
const { kHTTP2BuildRequest, kHTTP2CopyHeaders, kHTTP1BuildRequest } = require('./symbols')
const util = require('./util')
// tokenRegExp and headerCharRegex have been lifted from
// https://github.com/nodejs/node/blob/main/lib/_http_common.js
/**
* Verifies that the given val is a valid HTTP token
* per the rules defined in RFC 7230
* See https://tools.ietf.org/html/rfc7230#section-3.2.6
*/
const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/
/**
* Matches if val contains an invalid field-vchar
* field-value = *( field-content / obs-fold )
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
* field-vchar = VCHAR / obs-text
*/
const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/
// Verifies that a given path is valid does not contain control chars \x00 to \x20
const invalidPathRegex = /[^\u0021-\u00ff]/
const kHandler = Symbol('handler')
const channels = {}
let extractBody
try {
const diagnosticsChannel = require('diagnostics_channel')
channels.create = diagnosticsChannel.channel('undici:request:create')
channels.bodySent = diagnosticsChannel.channel('undici:request:bodySent')
channels.headers = diagnosticsChannel.channel('undici:request:headers')
channels.trailers = diagnosticsChannel.channel('undici:request:trailers')
channels.error = diagnosticsChannel.channel('undici:request:error')
} catch {
channels.create = { hasSubscribers: false }
channels.bodySent = { hasSubscribers: false }
channels.headers = { hasSubscribers: false }
channels.trailers = { hasSubscribers: false }
channels.error = { hasSubscribers: false }
}
class Request {
constructor (origin, {
path,
method,
body,
headers,
query,
idempotent,
blocking,
upgrade,
headersTimeout,
bodyTimeout,
reset,
throwOnError,
expectContinue
}, handler) {
if (typeof path !== 'string') {
throw new InvalidArgumentError('path must be a string')
} else if (
path[0] !== '/' &&
!(path.startsWith('http://') || path.startsWith('https://')) &&
method !== 'CONNECT'
) {
throw new InvalidArgumentError('path must be an absolute URL or start with a slash')
} else if (invalidPathRegex.exec(path) !== null) {
throw new InvalidArgumentError('invalid request path')
}
if (typeof method !== 'string') {
throw new InvalidArgumentError('method must be a string')
} else if (tokenRegExp.exec(method) === null) {
throw new InvalidArgumentError('invalid request method')
}
if (upgrade && typeof upgrade !== 'string') {
throw new InvalidArgumentError('upgrade must be a string')
}
if (headersTimeout != null && (!Number.isFinite(headersTimeout) || headersTimeout < 0)) {
throw new InvalidArgumentError('invalid headersTimeout')
}
if (bodyTimeout != null && (!Number.isFinite(bodyTimeout) || bodyTimeout < 0)) {
throw new InvalidArgumentError('invalid bodyTimeout')
}
if (reset != null && typeof reset !== 'boolean') {
throw new InvalidArgumentError('invalid reset')
}
if (expectContinue != null && typeof expectContinue !== 'boolean') {
throw new InvalidArgumentError('invalid expectContinue')
}
this.headersTimeout = headersTimeout
this.bodyTimeout = bodyTimeout
this.throwOnError = throwOnError === true
this.method = method
this.abort = null
if (body == null) {
this.body = null
} else if (util.isStream(body)) {
this.body = body
const rState = this.body._readableState
if (!rState || !rState.autoDestroy) {
this.endHandler = function autoDestroy () {
util.destroy(this)
}
this.body.on('end', this.endHandler)
}
this.errorHandler = err => {
if (this.abort) {
this.abort(err)
} else {
this.error = err
}
}
this.body.on('error', this.errorHandler)
} else if (util.isBuffer(body)) {
this.body = body.byteLength ? body : null
} else if (ArrayBuffer.isView(body)) {
this.body = body.buffer.byteLength ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : null
} else if (body instanceof ArrayBuffer) {
this.body = body.byteLength ? Buffer.from(body) : null
} else if (typeof body === 'string') {
this.body = body.length ? Buffer.from(body) : null
} else if (util.isFormDataLike(body) || util.isIterable(body) || util.isBlobLike(body)) {
this.body = body
} else {
throw new InvalidArgumentError('body must be a string, a Buffer, a Readable stream, an iterable, or an async iterable')
}
this.completed = false
this.aborted = false
this.upgrade = upgrade || null
this.path = query ? util.buildURL(path, query) : path
this.origin = origin
this.idempotent = idempotent == null
? method === 'HEAD' || method === 'GET'
: idempotent
this.blocking = blocking == null ? false : blocking
this.reset = reset == null ? null : reset
this.host = null
this.contentLength = null
this.contentType = null
this.headers = ''
// Only for H2
this.expectContinue = expectContinue != null ? expectContinue : false
if (Array.isArray(headers)) {
if (headers.length % 2 !== 0) {
throw new InvalidArgumentError('headers array must be even')
}
for (let i = 0; i < headers.length; i += 2) {
processHeader(this, headers[i], headers[i + 1])
}
} else if (headers && typeof headers === 'object') {
const keys = Object.keys(headers)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
processHeader(this, key, headers[key])
}
} else if (headers != null) {
throw new InvalidArgumentError('headers must be an object or an array')
}
if (util.isFormDataLike(this.body)) {
if (util.nodeMajor < 16 || (util.nodeMajor === 16 && util.nodeMinor < 8)) {
throw new InvalidArgumentError('Form-Data bodies are only supported in node v16.8 and newer.')
}
if (!extractBody) {
extractBody = require('../fetch/body.js').extractBody
}
const [bodyStream, contentType] = extractBody(body)
if (this.contentType == null) {
this.contentType = contentType
this.headers += `content-type: ${contentType}\r\n`
}
this.body = bodyStream.stream
this.contentLength = bodyStream.length
} else if (util.isBlobLike(body) && this.contentType == null && body.type) {
this.contentType = body.type
this.headers += `content-type: ${body.type}\r\n`
}
util.validateHandler(handler, method, upgrade)
this.servername = util.getServerName(this.host)
this[kHandler] = handler
if (channels.create.hasSubscribers) {
channels.create.publish({ request: this })
}
}
onBodySent (chunk) {
if (this[kHandler].onBodySent) {
try {
this[kHandler].onBodySent(chunk)
} catch (err) {
this.onError(err)
}
}
}
onRequestSent () {
if (channels.bodySent.hasSubscribers) {
channels.bodySent.publish({ request: this })
}
if (this[kHandler].onRequestSent) {
try {
this[kHandler].onRequestSent()
} catch (err) {
this.onError(err)
}
}
}
onConnect (abort) {
assert(!this.aborted)
assert(!this.completed)
if (this.error) {
abort(this.error)
} else {
this.abort = abort
return this[kHandler].onConnect(abort)
}
}
onHeaders (statusCode, headers, resume, statusText) {
assert(!this.aborted)
assert(!this.completed)
if (channels.headers.hasSubscribers) {
channels.headers.publish({ request: this, response: { statusCode, headers, statusText } })
}
return this[kHandler].onHeaders(statusCode, headers, resume, statusText)
}
onData (chunk) {
assert(!this.aborted)
assert(!this.completed)
return this[kHandler].onData(chunk)
}
onUpgrade (statusCode, headers, socket) {
assert(!this.aborted)
assert(!this.completed)
return this[kHandler].onUpgrade(statusCode, headers, socket)
}
onComplete (trailers) {
this.onFinally()
assert(!this.aborted)
this.completed = true
if (channels.trailers.hasSubscribers) {
channels.trailers.publish({ request: this, trailers })
}
return this[kHandler].onComplete(trailers)
}
onError (error) {
this.onFinally()
if (channels.error.hasSubscribers) {
channels.error.publish({ request: this, error })
}
if (this.aborted) {
return
}
this.aborted = true
return this[kHandler].onError(error)
}
onFinally () {
if (this.errorHandler) {
this.body.off('error', this.errorHandler)
this.errorHandler = null
}
if (this.endHandler) {
this.body.off('end', this.endHandler)
this.endHandler = null
}
}
// TODO: adjust to support H2
addHeader (key, value) {
processHeader(this, key, value)
return this
}
static [kHTTP1BuildRequest] (origin, opts, handler) {
// TODO: Migrate header parsing here, to make Requests
// HTTP agnostic
return new Request(origin, opts, handler)
}
static [kHTTP2BuildRequest] (origin, opts, handler) {
const headers = opts.headers
opts = { ...opts, headers: null }
const request = new Request(origin, opts, handler)
request.headers = {}
if (Array.isArray(headers)) {
if (headers.length % 2 !== 0) {
throw new InvalidArgumentError('headers array must be even')
}
for (let i = 0; i < headers.length; i += 2) {
processHeader(request, headers[i], headers[i + 1], true)
}
} else if (headers && typeof headers === 'object') {
const keys = Object.keys(headers)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
processHeader(request, key, headers[key], true)
}
} else if (headers != null) {
throw new InvalidArgumentError('headers must be an object or an array')
}
return request
}
static [kHTTP2CopyHeaders] (raw) {
const rawHeaders = raw.split('\r\n')
const headers = {}
for (const header of rawHeaders) {
const [key, value] = header.split(': ')
if (value == null || value.length === 0) continue
if (headers[key]) headers[key] += `,${value}`
else headers[key] = value
}
return headers
}
}
function processHeaderValue (key, val, skipAppend) {
if (val && typeof val === 'object') {
throw new InvalidArgumentError(`invalid ${key} header`)
}
val = val != null ? `${val}` : ''
if (headerCharRegex.exec(val) !== null) {
throw new InvalidArgumentError(`invalid ${key} header`)
}
return skipAppend ? val : `${key}: ${val}\r\n`
}
function processHeader (request, key, val, skipAppend = false) {
if (val && (typeof val === 'object' && !Array.isArray(val))) {
throw new InvalidArgumentError(`invalid ${key} header`)
} else if (val === undefined) {
return
}
if (
request.host === null &&
key.length === 4 &&
key.toLowerCase() === 'host'
) {
if (headerCharRegex.exec(val) !== null) {
throw new InvalidArgumentError(`invalid ${key} header`)
}
// Consumed by Client
request.host = val
} else if (
request.contentLength === null &&
key.length === 14 &&
key.toLowerCase() === 'content-length'
) {
request.contentLength = parseInt(val, 10)
if (!Number.isFinite(request.contentLength)) {
throw new InvalidArgumentError('invalid content-length header')
}
} else if (
request.contentType === null &&
key.length === 12 &&
key.toLowerCase() === 'content-type'
) {
request.contentType = val
if (skipAppend) request.headers[key] = processHeaderValue(key, val, skipAppend)
else request.headers += processHeaderValue(key, val)
} else if (
key.length === 17 &&
key.toLowerCase() === 'transfer-encoding'
) {
throw new InvalidArgumentError('invalid transfer-encoding header')
} else if (
key.length === 10 &&
key.toLowerCase() === 'connection'
) {
const value = typeof val === 'string' ? val.toLowerCase() : null
if (value !== 'close' && value !== 'keep-alive') {
throw new InvalidArgumentError('invalid connection header')
} else if (value === 'close') {
request.reset = true
}
} else if (
key.length === 10 &&
key.toLowerCase() === 'keep-alive'
) {
throw new InvalidArgumentError('invalid keep-alive header')
} else if (
key.length === 7 &&
key.toLowerCase() === 'upgrade'
) {
throw new InvalidArgumentError('invalid upgrade header')
} else if (
key.length === 6 &&
key.toLowerCase() === 'expect'
) {
throw new NotSupportedError('expect header not supported')
} else if (tokenRegExp.exec(key) === null) {
throw new InvalidArgumentError('invalid header key')
} else {
if (Array.isArray(val)) {
for (let i = 0; i < val.length; i++) {
if (skipAppend) {
if (request.headers[key]) request.headers[key] += `,${processHeaderValue(key, val[i], skipAppend)}`
else request.headers[key] = processHeaderValue(key, val[i], skipAppend)
} else {
request.headers += processHeaderValue(key, val[i])
}
}
} else {
if (skipAppend) request.headers[key] = processHeaderValue(key, val, skipAppend)
else request.headers += processHeaderValue(key, val)
}
}
}
module.exports = Request

61
node_modules/undici/lib/core/symbols.js generated vendored Normal file
View File

@ -0,0 +1,61 @@
module.exports = {
kClose: Symbol('close'),
kDestroy: Symbol('destroy'),
kDispatch: Symbol('dispatch'),
kUrl: Symbol('url'),
kWriting: Symbol('writing'),
kResuming: Symbol('resuming'),
kQueue: Symbol('queue'),
kConnect: Symbol('connect'),
kConnecting: Symbol('connecting'),
kHeadersList: Symbol('headers list'),
kKeepAliveDefaultTimeout: Symbol('default keep alive timeout'),
kKeepAliveMaxTimeout: Symbol('max keep alive timeout'),
kKeepAliveTimeoutThreshold: Symbol('keep alive timeout threshold'),
kKeepAliveTimeoutValue: Symbol('keep alive timeout'),
kKeepAlive: Symbol('keep alive'),
kHeadersTimeout: Symbol('headers timeout'),
kBodyTimeout: Symbol('body timeout'),
kServerName: Symbol('server name'),
kLocalAddress: Symbol('local address'),
kHost: Symbol('host'),
kNoRef: Symbol('no ref'),
kBodyUsed: Symbol('used'),
kRunning: Symbol('running'),
kBlocking: Symbol('blocking'),
kPending: Symbol('pending'),
kSize: Symbol('size'),
kBusy: Symbol('busy'),
kQueued: Symbol('queued'),
kFree: Symbol('free'),
kConnected: Symbol('connected'),
kClosed: Symbol('closed'),
kNeedDrain: Symbol('need drain'),
kReset: Symbol('reset'),
kDestroyed: Symbol.for('nodejs.stream.destroyed'),
kMaxHeadersSize: Symbol('max headers size'),
kRunningIdx: Symbol('running index'),
kPendingIdx: Symbol('pending index'),
kError: Symbol('error'),
kClients: Symbol('clients'),
kClient: Symbol('client'),
kParser: Symbol('parser'),
kOnDestroyed: Symbol('destroy callbacks'),
kPipelining: Symbol('pipelining'),
kSocket: Symbol('socket'),
kHostHeader: Symbol('host header'),
kConnector: Symbol('connector'),
kStrictContentLength: Symbol('strict content length'),
kMaxRedirections: Symbol('maxRedirections'),
kMaxRequests: Symbol('maxRequestsPerClient'),
kProxy: Symbol('proxy agent options'),
kCounter: Symbol('socket request counter'),
kInterceptors: Symbol('dispatch interceptors'),
kMaxResponseSize: Symbol('max response size'),
kHTTP2Session: Symbol('http2Session'),
kHTTP2SessionState: Symbol('http2Session state'),
kHTTP2BuildRequest: Symbol('http2 build request'),
kHTTP1BuildRequest: Symbol('http1 build request'),
kHTTP2CopyHeaders: Symbol('http2 copy headers'),
kHTTPConnVersion: Symbol('http connection version')
}

503
node_modules/undici/lib/core/util.js generated vendored Normal file
View File

@ -0,0 +1,503 @@
'use strict'
const assert = require('assert')
const { kDestroyed, kBodyUsed } = require('./symbols')
const { IncomingMessage } = require('http')
const stream = require('stream')
const net = require('net')
const { InvalidArgumentError } = require('./errors')
const { Blob } = require('buffer')
const nodeUtil = require('util')
const { stringify } = require('querystring')
const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(v))
function nop () {}
function isStream (obj) {
return obj && typeof obj === 'object' && typeof obj.pipe === 'function' && typeof obj.on === 'function'
}
// based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License)
function isBlobLike (object) {
return (Blob && object instanceof Blob) || (
object &&
typeof object === 'object' &&
(typeof object.stream === 'function' ||
typeof object.arrayBuffer === 'function') &&
/^(Blob|File)$/.test(object[Symbol.toStringTag])
)
}
function buildURL (url, queryParams) {
if (url.includes('?') || url.includes('#')) {
throw new Error('Query params cannot be passed when url already contains "?" or "#".')
}
const stringified = stringify(queryParams)
if (stringified) {
url += '?' + stringified
}
return url
}
function parseURL (url) {
if (typeof url === 'string') {
url = new URL(url)
if (!/^https?:/.test(url.origin || url.protocol)) {
throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')
}
return url
}
if (!url || typeof url !== 'object') {
throw new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.')
}
if (!/^https?:/.test(url.origin || url.protocol)) {
throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')
}
if (!(url instanceof URL)) {
if (url.port != null && url.port !== '' && !Number.isFinite(parseInt(url.port))) {
throw new InvalidArgumentError('Invalid URL: port must be a valid integer or a string representation of an integer.')
}
if (url.path != null && typeof url.path !== 'string') {
throw new InvalidArgumentError('Invalid URL path: the path must be a string or null/undefined.')
}
if (url.pathname != null && typeof url.pathname !== 'string') {
throw new InvalidArgumentError('Invalid URL pathname: the pathname must be a string or null/undefined.')
}
if (url.hostname != null && typeof url.hostname !== 'string') {
throw new InvalidArgumentError('Invalid URL hostname: the hostname must be a string or null/undefined.')
}
if (url.origin != null && typeof url.origin !== 'string') {
throw new InvalidArgumentError('Invalid URL origin: the origin must be a string or null/undefined.')
}
const port = url.port != null
? url.port
: (url.protocol === 'https:' ? 443 : 80)
let origin = url.origin != null
? url.origin
: `${url.protocol}//${url.hostname}:${port}`
let path = url.path != null
? url.path
: `${url.pathname || ''}${url.search || ''}`
if (origin.endsWith('/')) {
origin = origin.substring(0, origin.length - 1)
}
if (path && !path.startsWith('/')) {
path = `/${path}`
}
// new URL(path, origin) is unsafe when `path` contains an absolute URL
// From https://developer.mozilla.org/en-US/docs/Web/API/URL/URL:
// If first parameter is a relative URL, second param is required, and will be used as the base URL.
// If first parameter is an absolute URL, a given second param will be ignored.
url = new URL(origin + path)
}
return url
}
function parseOrigin (url) {
url = parseURL(url)
if (url.pathname !== '/' || url.search || url.hash) {
throw new InvalidArgumentError('invalid url')
}
return url
}
function getHostname (host) {
if (host[0] === '[') {
const idx = host.indexOf(']')
assert(idx !== -1)
return host.substr(1, idx - 1)
}
const idx = host.indexOf(':')
if (idx === -1) return host
return host.substr(0, idx)
}
// IP addresses are not valid server names per RFC6066
// > Currently, the only server names supported are DNS hostnames
function getServerName (host) {
if (!host) {
return null
}
assert.strictEqual(typeof host, 'string')
const servername = getHostname(host)
if (net.isIP(servername)) {
return ''
}
return servername
}
function deepClone (obj) {
return JSON.parse(JSON.stringify(obj))
}
function isAsyncIterable (obj) {
return !!(obj != null && typeof obj[Symbol.asyncIterator] === 'function')
}
function isIterable (obj) {
return !!(obj != null && (typeof obj[Symbol.iterator] === 'function' || typeof obj[Symbol.asyncIterator] === 'function'))
}
function bodyLength (body) {
if (body == null) {
return 0
} else if (isStream(body)) {
const state = body._readableState
return state && state.objectMode === false && state.ended === true && Number.isFinite(state.length)
? state.length
: null
} else if (isBlobLike(body)) {
return body.size != null ? body.size : null
} else if (isBuffer(body)) {
return body.byteLength
}
return null
}
function isDestroyed (stream) {
return !stream || !!(stream.destroyed || stream[kDestroyed])
}
function isReadableAborted (stream) {
const state = stream && stream._readableState
return isDestroyed(stream) && state && !state.endEmitted
}
function destroy (stream, err) {
if (stream == null || !isStream(stream) || isDestroyed(stream)) {
return
}
if (typeof stream.destroy === 'function') {
if (Object.getPrototypeOf(stream).constructor === IncomingMessage) {
// See: https://github.com/nodejs/node/pull/38505/files
stream.socket = null
}
stream.destroy(err)
} else if (err) {
process.nextTick((stream, err) => {
stream.emit('error', err)
}, stream, err)
}
if (stream.destroyed !== true) {
stream[kDestroyed] = true
}
}
const KEEPALIVE_TIMEOUT_EXPR = /timeout=(\d+)/
function parseKeepAliveTimeout (val) {
const m = val.toString().match(KEEPALIVE_TIMEOUT_EXPR)
return m ? parseInt(m[1], 10) * 1000 : null
}
function parseHeaders (headers, obj = {}) {
// For H2 support
if (!Array.isArray(headers)) return headers
for (let i = 0; i < headers.length; i += 2) {
const key = headers[i].toString().toLowerCase()
let val = obj[key]
if (!val) {
if (Array.isArray(headers[i + 1])) {
obj[key] = headers[i + 1]
} else {
obj[key] = headers[i + 1].toString('utf8')
}
} else {
if (!Array.isArray(val)) {
val = [val]
obj[key] = val
}
val.push(headers[i + 1].toString('utf8'))
}
}
// See https://github.com/nodejs/node/pull/46528
if ('content-length' in obj && 'content-disposition' in obj) {
obj['content-disposition'] = Buffer.from(obj['content-disposition']).toString('latin1')
}
return obj
}
function parseRawHeaders (headers) {
const ret = []
let hasContentLength = false
let contentDispositionIdx = -1
for (let n = 0; n < headers.length; n += 2) {
const key = headers[n + 0].toString()
const val = headers[n + 1].toString('utf8')
if (key.length === 14 && (key === 'content-length' || key.toLowerCase() === 'content-length')) {
ret.push(key, val)
hasContentLength = true
} else if (key.length === 19 && (key === 'content-disposition' || key.toLowerCase() === 'content-disposition')) {
contentDispositionIdx = ret.push(key, val) - 1
} else {
ret.push(key, val)
}
}
// See https://github.com/nodejs/node/pull/46528
if (hasContentLength && contentDispositionIdx !== -1) {
ret[contentDispositionIdx] = Buffer.from(ret[contentDispositionIdx]).toString('latin1')
}
return ret
}
function isBuffer (buffer) {
// See, https://github.com/mcollina/undici/pull/319
return buffer instanceof Uint8Array || Buffer.isBuffer(buffer)
}
function validateHandler (handler, method, upgrade) {
if (!handler || typeof handler !== 'object') {
throw new InvalidArgumentError('handler must be an object')
}
if (typeof handler.onConnect !== 'function') {
throw new InvalidArgumentError('invalid onConnect method')
}
if (typeof handler.onError !== 'function') {
throw new InvalidArgumentError('invalid onError method')
}
if (typeof handler.onBodySent !== 'function' && handler.onBodySent !== undefined) {
throw new InvalidArgumentError('invalid onBodySent method')
}
if (upgrade || method === 'CONNECT') {
if (typeof handler.onUpgrade !== 'function') {
throw new InvalidArgumentError('invalid onUpgrade method')
}
} else {
if (typeof handler.onHeaders !== 'function') {
throw new InvalidArgumentError('invalid onHeaders method')
}
if (typeof handler.onData !== 'function') {
throw new InvalidArgumentError('invalid onData method')
}
if (typeof handler.onComplete !== 'function') {
throw new InvalidArgumentError('invalid onComplete method')
}
}
}
// A body is disturbed if it has been read from and it cannot
// be re-used without losing state or data.
function isDisturbed (body) {
return !!(body && (
stream.isDisturbed
? stream.isDisturbed(body) || body[kBodyUsed] // TODO (fix): Why is body[kBodyUsed] needed?
: body[kBodyUsed] ||
body.readableDidRead ||
(body._readableState && body._readableState.dataEmitted) ||
isReadableAborted(body)
))
}
function isErrored (body) {
return !!(body && (
stream.isErrored
? stream.isErrored(body)
: /state: 'errored'/.test(nodeUtil.inspect(body)
)))
}
function isReadable (body) {
return !!(body && (
stream.isReadable
? stream.isReadable(body)
: /state: 'readable'/.test(nodeUtil.inspect(body)
)))
}
function getSocketInfo (socket) {
return {
localAddress: socket.localAddress,
localPort: socket.localPort,
remoteAddress: socket.remoteAddress,
remotePort: socket.remotePort,
remoteFamily: socket.remoteFamily,
timeout: socket.timeout,
bytesWritten: socket.bytesWritten,
bytesRead: socket.bytesRead
}
}
async function * convertIterableToBuffer (iterable) {
for await (const chunk of iterable) {
yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
}
}
let ReadableStream
function ReadableStreamFrom (iterable) {
if (!ReadableStream) {
ReadableStream = require('stream/web').ReadableStream
}
if (ReadableStream.from) {
return ReadableStream.from(convertIterableToBuffer(iterable))
}
let iterator
return new ReadableStream(
{
async start () {
iterator = iterable[Symbol.asyncIterator]()
},
async pull (controller) {
const { done, value } = await iterator.next()
if (done) {
queueMicrotask(() => {
controller.close()
})
} else {
const buf = Buffer.isBuffer(value) ? value : Buffer.from(value)
controller.enqueue(new Uint8Array(buf))
}
return controller.desiredSize > 0
},
async cancel (reason) {
await iterator.return()
}
},
0
)
}
// The chunk should be a FormData instance and contains
// all the required methods.
function isFormDataLike (object) {
return (
object &&
typeof object === 'object' &&
typeof object.append === 'function' &&
typeof object.delete === 'function' &&
typeof object.get === 'function' &&
typeof object.getAll === 'function' &&
typeof object.has === 'function' &&
typeof object.set === 'function' &&
object[Symbol.toStringTag] === 'FormData'
)
}
function throwIfAborted (signal) {
if (!signal) { return }
if (typeof signal.throwIfAborted === 'function') {
signal.throwIfAborted()
} else {
if (signal.aborted) {
// DOMException not available < v17.0.0
const err = new Error('The operation was aborted')
err.name = 'AbortError'
throw err
}
}
}
let events
function addAbortListener (signal, listener) {
if (typeof Symbol.dispose === 'symbol') {
if (!events) {
events = require('events')
}
if (typeof events.addAbortListener === 'function' && 'aborted' in signal) {
return events.addAbortListener(signal, listener)
}
}
if ('addEventListener' in signal) {
signal.addEventListener('abort', listener, { once: true })
return () => signal.removeEventListener('abort', listener)
}
signal.addListener('abort', listener)
return () => signal.removeListener('abort', listener)
}
const hasToWellFormed = !!String.prototype.toWellFormed
/**
* @param {string} val
*/
function toUSVString (val) {
if (hasToWellFormed) {
return `${val}`.toWellFormed()
} else if (nodeUtil.toUSVString) {
return nodeUtil.toUSVString(val)
}
return `${val}`
}
const kEnumerableProperty = Object.create(null)
kEnumerableProperty.enumerable = true
module.exports = {
kEnumerableProperty,
nop,
isDisturbed,
isErrored,
isReadable,
toUSVString,
isReadableAborted,
isBlobLike,
parseOrigin,
parseURL,
getServerName,
isStream,
isIterable,
isAsyncIterable,
isDestroyed,
parseRawHeaders,
parseHeaders,
parseKeepAliveTimeout,
destroy,
bodyLength,
deepClone,
ReadableStreamFrom,
isBuffer,
validateHandler,
getSocketInfo,
isFormDataLike,
buildURL,
throwIfAborted,
addAbortListener,
nodeMajor,
nodeMinor,
nodeHasAutoSelectFamily: nodeMajor > 18 || (nodeMajor === 18 && nodeMinor >= 13)
}

192
node_modules/undici/lib/dispatcher-base.js generated vendored Normal file
View File

@ -0,0 +1,192 @@
'use strict'
const Dispatcher = require('./dispatcher')
const {
ClientDestroyedError,
ClientClosedError,
InvalidArgumentError
} = require('./core/errors')
const { kDestroy, kClose, kDispatch, kInterceptors } = require('./core/symbols')
const kDestroyed = Symbol('destroyed')
const kClosed = Symbol('closed')
const kOnDestroyed = Symbol('onDestroyed')
const kOnClosed = Symbol('onClosed')
const kInterceptedDispatch = Symbol('Intercepted Dispatch')
class DispatcherBase extends Dispatcher {
constructor () {
super()
this[kDestroyed] = false
this[kOnDestroyed] = null
this[kClosed] = false
this[kOnClosed] = []
}
get destroyed () {
return this[kDestroyed]
}
get closed () {
return this[kClosed]
}
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) {
if (callback === undefined) {
return new Promise((resolve, reject) => {
this.close((err, data) => {
return err ? reject(err) : resolve(data)
})
})
}
if (typeof callback !== 'function') {
throw new InvalidArgumentError('invalid callback')
}
if (this[kDestroyed]) {
queueMicrotask(() => callback(new ClientDestroyedError(), null))
return
}
if (this[kClosed]) {
if (this[kOnClosed]) {
this[kOnClosed].push(callback)
} else {
queueMicrotask(() => callback(null, null))
}
return
}
this[kClosed] = true
this[kOnClosed].push(callback)
const onClosed = () => {
const callbacks = this[kOnClosed]
this[kOnClosed] = null
for (let i = 0; i < callbacks.length; i++) {
callbacks[i](null, null)
}
}
// Should not error.
this[kClose]()
.then(() => this.destroy())
.then(() => {
queueMicrotask(onClosed)
})
}
destroy (err, callback) {
if (typeof err === 'function') {
callback = err
err = null
}
if (callback === undefined) {
return new Promise((resolve, reject) => {
this.destroy(err, (err, data) => {
return err ? /* istanbul ignore next: should never error */ reject(err) : resolve(data)
})
})
}
if (typeof callback !== 'function') {
throw new InvalidArgumentError('invalid callback')
}
if (this[kDestroyed]) {
if (this[kOnDestroyed]) {
this[kOnDestroyed].push(callback)
} else {
queueMicrotask(() => callback(null, null))
}
return
}
if (!err) {
err = new ClientDestroyedError()
}
this[kDestroyed] = true
this[kOnDestroyed] = this[kOnDestroyed] || []
this[kOnDestroyed].push(callback)
const onDestroyed = () => {
const callbacks = this[kOnDestroyed]
this[kOnDestroyed] = null
for (let i = 0; i < callbacks.length; i++) {
callbacks[i](null, null)
}
}
// Should not error.
this[kDestroy](err).then(() => {
queueMicrotask(onDestroyed)
})
}
[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) {
if (!handler || typeof handler !== 'object') {
throw new InvalidArgumentError('handler must be an object')
}
try {
if (!opts || typeof opts !== 'object') {
throw new InvalidArgumentError('opts must be an object.')
}
if (this[kDestroyed] || this[kOnDestroyed]) {
throw new ClientDestroyedError()
}
if (this[kClosed]) {
throw new ClientClosedError()
}
return this[kInterceptedDispatch](opts, handler)
} catch (err) {
if (typeof handler.onError !== 'function') {
throw new InvalidArgumentError('invalid onError method')
}
handler.onError(err)
return false
}
}
}
module.exports = DispatcherBase

19
node_modules/undici/lib/dispatcher.js generated vendored Normal file
View File

@ -0,0 +1,19 @@
'use strict'
const EventEmitter = require('events')
class Dispatcher extends EventEmitter {
dispatch () {
throw new Error('not implemented')
}
close () {
throw new Error('not implemented')
}
destroy () {
throw new Error('not implemented')
}
}
module.exports = Dispatcher

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

290
node_modules/undici/lib/fileapi/encoding.js generated vendored Normal file
View File

@ -0,0 +1,290 @@
'use strict'
/**
* @see https://encoding.spec.whatwg.org/#concept-encoding-get
* @param {string|undefined} label
*/
function getEncoding (label) {
if (!label) {
return 'failure'
}
// 1. Remove any leading and trailing ASCII whitespace from label.
// 2. If label is an ASCII case-insensitive match for any of the
// labels listed in the table below, then return the
// corresponding encoding; otherwise return failure.
switch (label.trim().toLowerCase()) {
case 'unicode-1-1-utf-8':
case 'unicode11utf8':
case 'unicode20utf8':
case 'utf-8':
case 'utf8':
case 'x-unicode20utf8':
return 'UTF-8'
case '866':
case 'cp866':
case 'csibm866':
case 'ibm866':
return 'IBM866'
case 'csisolatin2':
case 'iso-8859-2':
case 'iso-ir-101':
case 'iso8859-2':
case 'iso88592':
case 'iso_8859-2':
case 'iso_8859-2:1987':
case 'l2':
case 'latin2':
return 'ISO-8859-2'
case 'csisolatin3':
case 'iso-8859-3':
case 'iso-ir-109':
case 'iso8859-3':
case 'iso88593':
case 'iso_8859-3':
case 'iso_8859-3:1988':
case 'l3':
case 'latin3':
return 'ISO-8859-3'
case 'csisolatin4':
case 'iso-8859-4':
case 'iso-ir-110':
case 'iso8859-4':
case 'iso88594':
case 'iso_8859-4':
case 'iso_8859-4:1988':
case 'l4':
case 'latin4':
return 'ISO-8859-4'
case 'csisolatincyrillic':
case 'cyrillic':
case 'iso-8859-5':
case 'iso-ir-144':
case 'iso8859-5':
case 'iso88595':
case 'iso_8859-5':
case 'iso_8859-5:1988':
return 'ISO-8859-5'
case 'arabic':
case 'asmo-708':
case 'csiso88596e':
case 'csiso88596i':
case 'csisolatinarabic':
case 'ecma-114':
case 'iso-8859-6':
case 'iso-8859-6-e':
case 'iso-8859-6-i':
case 'iso-ir-127':
case 'iso8859-6':
case 'iso88596':
case 'iso_8859-6':
case 'iso_8859-6:1987':
return 'ISO-8859-6'
case 'csisolatingreek':
case 'ecma-118':
case 'elot_928':
case 'greek':
case 'greek8':
case 'iso-8859-7':
case 'iso-ir-126':
case 'iso8859-7':
case 'iso88597':
case 'iso_8859-7':
case 'iso_8859-7:1987':
case 'sun_eu_greek':
return 'ISO-8859-7'
case 'csiso88598e':
case 'csisolatinhebrew':
case 'hebrew':
case 'iso-8859-8':
case 'iso-8859-8-e':
case 'iso-ir-138':
case 'iso8859-8':
case 'iso88598':
case 'iso_8859-8':
case 'iso_8859-8:1988':
case 'visual':
return 'ISO-8859-8'
case 'csiso88598i':
case 'iso-8859-8-i':
case 'logical':
return 'ISO-8859-8-I'
case 'csisolatin6':
case 'iso-8859-10':
case 'iso-ir-157':
case 'iso8859-10':
case 'iso885910':
case 'l6':
case 'latin6':
return 'ISO-8859-10'
case 'iso-8859-13':
case 'iso8859-13':
case 'iso885913':
return 'ISO-8859-13'
case 'iso-8859-14':
case 'iso8859-14':
case 'iso885914':
return 'ISO-8859-14'
case 'csisolatin9':
case 'iso-8859-15':
case 'iso8859-15':
case 'iso885915':
case 'iso_8859-15':
case 'l9':
return 'ISO-8859-15'
case 'iso-8859-16':
return 'ISO-8859-16'
case 'cskoi8r':
case 'koi':
case 'koi8':
case 'koi8-r':
case 'koi8_r':
return 'KOI8-R'
case 'koi8-ru':
case 'koi8-u':
return 'KOI8-U'
case 'csmacintosh':
case 'mac':
case 'macintosh':
case 'x-mac-roman':
return 'macintosh'
case 'iso-8859-11':
case 'iso8859-11':
case 'iso885911':
case 'tis-620':
case 'windows-874':
return 'windows-874'
case 'cp1250':
case 'windows-1250':
case 'x-cp1250':
return 'windows-1250'
case 'cp1251':
case 'windows-1251':
case 'x-cp1251':
return 'windows-1251'
case 'ansi_x3.4-1968':
case 'ascii':
case 'cp1252':
case 'cp819':
case 'csisolatin1':
case 'ibm819':
case 'iso-8859-1':
case 'iso-ir-100':
case 'iso8859-1':
case 'iso88591':
case 'iso_8859-1':
case 'iso_8859-1:1987':
case 'l1':
case 'latin1':
case 'us-ascii':
case 'windows-1252':
case 'x-cp1252':
return 'windows-1252'
case 'cp1253':
case 'windows-1253':
case 'x-cp1253':
return 'windows-1253'
case 'cp1254':
case 'csisolatin5':
case 'iso-8859-9':
case 'iso-ir-148':
case 'iso8859-9':
case 'iso88599':
case 'iso_8859-9':
case 'iso_8859-9:1989':
case 'l5':
case 'latin5':
case 'windows-1254':
case 'x-cp1254':
return 'windows-1254'
case 'cp1255':
case 'windows-1255':
case 'x-cp1255':
return 'windows-1255'
case 'cp1256':
case 'windows-1256':
case 'x-cp1256':
return 'windows-1256'
case 'cp1257':
case 'windows-1257':
case 'x-cp1257':
return 'windows-1257'
case 'cp1258':
case 'windows-1258':
case 'x-cp1258':
return 'windows-1258'
case 'x-mac-cyrillic':
case 'x-mac-ukrainian':
return 'x-mac-cyrillic'
case 'chinese':
case 'csgb2312':
case 'csiso58gb231280':
case 'gb2312':
case 'gb_2312':
case 'gb_2312-80':
case 'gbk':
case 'iso-ir-58':
case 'x-gbk':
return 'GBK'
case 'gb18030':
return 'gb18030'
case 'big5':
case 'big5-hkscs':
case 'cn-big5':
case 'csbig5':
case 'x-x-big5':
return 'Big5'
case 'cseucpkdfmtjapanese':
case 'euc-jp':
case 'x-euc-jp':
return 'EUC-JP'
case 'csiso2022jp':
case 'iso-2022-jp':
return 'ISO-2022-JP'
case 'csshiftjis':
case 'ms932':
case 'ms_kanji':
case 'shift-jis':
case 'shift_jis':
case 'sjis':
case 'windows-31j':
case 'x-sjis':
return 'Shift_JIS'
case 'cseuckr':
case 'csksc56011987':
case 'euc-kr':
case 'iso-ir-149':
case 'korean':
case 'ks_c_5601-1987':
case 'ks_c_5601-1989':
case 'ksc5601':
case 'ksc_5601':
case 'windows-949':
return 'EUC-KR'
case 'csiso2022kr':
case 'hz-gb-2312':
case 'iso-2022-cn':
case 'iso-2022-cn-ext':
case 'iso-2022-kr':
case 'replacement':
return 'replacement'
case 'unicodefffe':
case 'utf-16be':
return 'UTF-16BE'
case 'csunicode':
case 'iso-10646-ucs-2':
case 'ucs-2':
case 'unicode':
case 'unicodefeff':
case 'utf-16':
case 'utf-16le':
return 'UTF-16LE'
case 'x-user-defined':
return 'x-user-defined'
default: return 'failure'
}
}
module.exports = {
getEncoding
}

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

@ -0,0 +1,344 @@
'use strict'
const {
staticPropertyDescriptors,
readOperation,
fireAProgressEvent
} = require('./util')
const {
kState,
kError,
kResult,
kEvents,
kAborted
} = require('./symbols')
const { webidl } = require('../fetch/webidl')
const { kEnumerableProperty } = require('../core/util')
class FileReader extends EventTarget {
constructor () {
super()
this[kState] = 'empty'
this[kResult] = null
this[kError] = null
this[kEvents] = {
loadend: null,
error: null,
abort: null,
load: null,
progress: null,
loadstart: null
}
}
/**
* @see https://w3c.github.io/FileAPI/#dfn-readAsArrayBuffer
* @param {import('buffer').Blob} blob
*/
readAsArrayBuffer (blob) {
webidl.brandCheck(this, FileReader)
webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsArrayBuffer' })
blob = webidl.converters.Blob(blob, { strict: false })
// The readAsArrayBuffer(blob) method, when invoked,
// must initiate a read operation for blob with ArrayBuffer.
readOperation(this, blob, 'ArrayBuffer')
}
/**
* @see https://w3c.github.io/FileAPI/#readAsBinaryString
* @param {import('buffer').Blob} blob
*/
readAsBinaryString (blob) {
webidl.brandCheck(this, FileReader)
webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsBinaryString' })
blob = webidl.converters.Blob(blob, { strict: false })
// The readAsBinaryString(blob) method, when invoked,
// must initiate a read operation for blob with BinaryString.
readOperation(this, blob, 'BinaryString')
}
/**
* @see https://w3c.github.io/FileAPI/#readAsDataText
* @param {import('buffer').Blob} blob
* @param {string?} encoding
*/
readAsText (blob, encoding = undefined) {
webidl.brandCheck(this, FileReader)
webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsText' })
blob = webidl.converters.Blob(blob, { strict: false })
if (encoding !== undefined) {
encoding = webidl.converters.DOMString(encoding)
}
// The readAsText(blob, encoding) method, when invoked,
// must initiate a read operation for blob with Text and encoding.
readOperation(this, blob, 'Text', encoding)
}
/**
* @see https://w3c.github.io/FileAPI/#dfn-readAsDataURL
* @param {import('buffer').Blob} blob
*/
readAsDataURL (blob) {
webidl.brandCheck(this, FileReader)
webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsDataURL' })
blob = webidl.converters.Blob(blob, { strict: false })
// The readAsDataURL(blob) method, when invoked, must
// initiate a read operation for blob with DataURL.
readOperation(this, blob, 'DataURL')
}
/**
* @see https://w3c.github.io/FileAPI/#dfn-abort
*/
abort () {
// 1. If this's state is "empty" or if this's state is
// "done" set this's result to null and terminate
// this algorithm.
if (this[kState] === 'empty' || this[kState] === 'done') {
this[kResult] = null
return
}
// 2. If this's state is "loading" set this's state to
// "done" and set this's result to null.
if (this[kState] === 'loading') {
this[kState] = 'done'
this[kResult] = null
}
// 3. If there are any tasks from this on the file reading
// task source in an affiliated task queue, then remove
// those tasks from that task queue.
this[kAborted] = true
// 4. Terminate the algorithm for the read method being processed.
// TODO
// 5. Fire a progress event called abort at this.
fireAProgressEvent('abort', this)
// 6. If this's state is not "loading", fire a progress
// event called loadend at this.
if (this[kState] !== 'loading') {
fireAProgressEvent('loadend', this)
}
}
/**
* @see https://w3c.github.io/FileAPI/#dom-filereader-readystate
*/
get readyState () {
webidl.brandCheck(this, FileReader)
switch (this[kState]) {
case 'empty': return this.EMPTY
case 'loading': return this.LOADING
case 'done': return this.DONE
}
}
/**
* @see https://w3c.github.io/FileAPI/#dom-filereader-result
*/
get result () {
webidl.brandCheck(this, FileReader)
// The result attributes getter, when invoked, must return
// this's result.
return this[kResult]
}
/**
* @see https://w3c.github.io/FileAPI/#dom-filereader-error
*/
get error () {
webidl.brandCheck(this, FileReader)
// The error attributes getter, when invoked, must return
// this's error.
return this[kError]
}
get onloadend () {
webidl.brandCheck(this, FileReader)
return this[kEvents].loadend
}
set onloadend (fn) {
webidl.brandCheck(this, FileReader)
if (this[kEvents].loadend) {
this.removeEventListener('loadend', this[kEvents].loadend)
}
if (typeof fn === 'function') {
this[kEvents].loadend = fn
this.addEventListener('loadend', fn)
} else {
this[kEvents].loadend = null
}
}
get onerror () {
webidl.brandCheck(this, FileReader)
return this[kEvents].error
}
set onerror (fn) {
webidl.brandCheck(this, FileReader)
if (this[kEvents].error) {
this.removeEventListener('error', this[kEvents].error)
}
if (typeof fn === 'function') {
this[kEvents].error = fn
this.addEventListener('error', fn)
} else {
this[kEvents].error = null
}
}
get onloadstart () {
webidl.brandCheck(this, FileReader)
return this[kEvents].loadstart
}
set onloadstart (fn) {
webidl.brandCheck(this, FileReader)
if (this[kEvents].loadstart) {
this.removeEventListener('loadstart', this[kEvents].loadstart)
}
if (typeof fn === 'function') {
this[kEvents].loadstart = fn
this.addEventListener('loadstart', fn)
} else {
this[kEvents].loadstart = null
}
}
get onprogress () {
webidl.brandCheck(this, FileReader)
return this[kEvents].progress
}
set onprogress (fn) {
webidl.brandCheck(this, FileReader)
if (this[kEvents].progress) {
this.removeEventListener('progress', this[kEvents].progress)
}
if (typeof fn === 'function') {
this[kEvents].progress = fn
this.addEventListener('progress', fn)
} else {
this[kEvents].progress = null
}
}
get onload () {
webidl.brandCheck(this, FileReader)
return this[kEvents].load
}
set onload (fn) {
webidl.brandCheck(this, FileReader)
if (this[kEvents].load) {
this.removeEventListener('load', this[kEvents].load)
}
if (typeof fn === 'function') {
this[kEvents].load = fn
this.addEventListener('load', fn)
} else {
this[kEvents].load = null
}
}
get onabort () {
webidl.brandCheck(this, FileReader)
return this[kEvents].abort
}
set onabort (fn) {
webidl.brandCheck(this, FileReader)
if (this[kEvents].abort) {
this.removeEventListener('abort', this[kEvents].abort)
}
if (typeof fn === 'function') {
this[kEvents].abort = fn
this.addEventListener('abort', fn)
} else {
this[kEvents].abort = null
}
}
}
// https://w3c.github.io/FileAPI/#dom-filereader-empty
FileReader.EMPTY = FileReader.prototype.EMPTY = 0
// https://w3c.github.io/FileAPI/#dom-filereader-loading
FileReader.LOADING = FileReader.prototype.LOADING = 1
// https://w3c.github.io/FileAPI/#dom-filereader-done
FileReader.DONE = FileReader.prototype.DONE = 2
Object.defineProperties(FileReader.prototype, {
EMPTY: staticPropertyDescriptors,
LOADING: staticPropertyDescriptors,
DONE: staticPropertyDescriptors,
readAsArrayBuffer: kEnumerableProperty,
readAsBinaryString: kEnumerableProperty,
readAsText: kEnumerableProperty,
readAsDataURL: kEnumerableProperty,
abort: kEnumerableProperty,
readyState: kEnumerableProperty,
result: kEnumerableProperty,
error: kEnumerableProperty,
onloadstart: kEnumerableProperty,
onprogress: kEnumerableProperty,
onload: kEnumerableProperty,
onabort: kEnumerableProperty,
onerror: kEnumerableProperty,
onloadend: kEnumerableProperty,
[Symbol.toStringTag]: {
value: 'FileReader',
writable: false,
enumerable: false,
configurable: true
}
})
Object.defineProperties(FileReader, {
EMPTY: staticPropertyDescriptors,
LOADING: staticPropertyDescriptors,
DONE: staticPropertyDescriptors
})
module.exports = {
FileReader
}

78
node_modules/undici/lib/fileapi/progressevent.js generated vendored Normal file
View File

@ -0,0 +1,78 @@
'use strict'
const { webidl } = require('../fetch/webidl')
const kState = Symbol('ProgressEvent state')
/**
* @see https://xhr.spec.whatwg.org/#progressevent
*/
class ProgressEvent extends Event {
constructor (type, eventInitDict = {}) {
type = webidl.converters.DOMString(type)
eventInitDict = webidl.converters.ProgressEventInit(eventInitDict ?? {})
super(type, eventInitDict)
this[kState] = {
lengthComputable: eventInitDict.lengthComputable,
loaded: eventInitDict.loaded,
total: eventInitDict.total
}
}
get lengthComputable () {
webidl.brandCheck(this, ProgressEvent)
return this[kState].lengthComputable
}
get loaded () {
webidl.brandCheck(this, ProgressEvent)
return this[kState].loaded
}
get total () {
webidl.brandCheck(this, ProgressEvent)
return this[kState].total
}
}
webidl.converters.ProgressEventInit = webidl.dictionaryConverter([
{
key: 'lengthComputable',
converter: webidl.converters.boolean,
defaultValue: false
},
{
key: 'loaded',
converter: webidl.converters['unsigned long long'],
defaultValue: 0
},
{
key: 'total',
converter: webidl.converters['unsigned long long'],
defaultValue: 0
},
{
key: 'bubbles',
converter: webidl.converters.boolean,
defaultValue: false
},
{
key: 'cancelable',
converter: webidl.converters.boolean,
defaultValue: false
},
{
key: 'composed',
converter: webidl.converters.boolean,
defaultValue: false
}
])
module.exports = {
ProgressEvent
}

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

@ -0,0 +1,10 @@
'use strict'
module.exports = {
kState: Symbol('FileReader state'),
kResult: Symbol('FileReader result'),
kError: Symbol('FileReader error'),
kLastProgressEventFired: Symbol('FileReader last progress event fired timestamp'),
kEvents: Symbol('FileReader events'),
kAborted: Symbol('FileReader aborted')
}

392
node_modules/undici/lib/fileapi/util.js generated vendored Normal file
View File

@ -0,0 +1,392 @@
'use strict'
const {
kState,
kError,
kResult,
kAborted,
kLastProgressEventFired
} = require('./symbols')
const { ProgressEvent } = require('./progressevent')
const { getEncoding } = require('./encoding')
const { DOMException } = require('../fetch/constants')
const { serializeAMimeType, parseMIMEType } = require('../fetch/dataURL')
const { types } = require('util')
const { StringDecoder } = require('string_decoder')
const { btoa } = require('buffer')
/** @type {PropertyDescriptor} */
const staticPropertyDescriptors = {
enumerable: true,
writable: false,
configurable: false
}
/**
* @see https://w3c.github.io/FileAPI/#readOperation
* @param {import('./filereader').FileReader} fr
* @param {import('buffer').Blob} blob
* @param {string} type
* @param {string?} encodingName
*/
function readOperation (fr, blob, type, encodingName) {
// 1. If frs state is "loading", throw an InvalidStateError
// DOMException.
if (fr[kState] === 'loading') {
throw new DOMException('Invalid state', 'InvalidStateError')
}
// 2. Set frs state to "loading".
fr[kState] = 'loading'
// 3. Set frs result to null.
fr[kResult] = null
// 4. Set frs error to null.
fr[kError] = null
// 5. Let stream be the result of calling get stream on blob.
/** @type {import('stream/web').ReadableStream} */
const stream = blob.stream()
// 6. Let reader be the result of getting a reader from stream.
const reader = stream.getReader()
// 7. Let bytes be an empty byte sequence.
/** @type {Uint8Array[]} */
const bytes = []
// 8. Let chunkPromise be the result of reading a chunk from
// stream with reader.
let chunkPromise = reader.read()
// 9. Let isFirstChunk be true.
let isFirstChunk = true
// 10. In parallel, while true:
// Note: "In parallel" just means non-blocking
// Note 2: readOperation itself cannot be async as double
// reading the body would then reject the promise, instead
// of throwing an error.
;(async () => {
while (!fr[kAborted]) {
// 1. Wait for chunkPromise to be fulfilled or rejected.
try {
const { done, value } = await chunkPromise
// 2. If chunkPromise is fulfilled, and isFirstChunk is
// true, queue a task to fire a progress event called
// loadstart at fr.
if (isFirstChunk && !fr[kAborted]) {
queueMicrotask(() => {
fireAProgressEvent('loadstart', fr)
})
}
// 3. Set isFirstChunk to false.
isFirstChunk = false
// 4. If chunkPromise is fulfilled with an object whose
// done property is false and whose value property is
// a Uint8Array object, run these steps:
if (!done && types.isUint8Array(value)) {
// 1. Let bs be the byte sequence represented by the
// Uint8Array object.
// 2. Append bs to bytes.
bytes.push(value)
// 3. If roughly 50ms have passed since these steps
// were last invoked, queue a task to fire a
// progress event called progress at fr.
if (
(
fr[kLastProgressEventFired] === undefined ||
Date.now() - fr[kLastProgressEventFired] >= 50
) &&
!fr[kAborted]
) {
fr[kLastProgressEventFired] = Date.now()
queueMicrotask(() => {
fireAProgressEvent('progress', fr)
})
}
// 4. Set chunkPromise to the result of reading a
// chunk from stream with reader.
chunkPromise = reader.read()
} else if (done) {
// 5. Otherwise, if chunkPromise is fulfilled with an
// object whose done property is true, queue a task
// to run the following steps and abort this algorithm:
queueMicrotask(() => {
// 1. Set frs state to "done".
fr[kState] = 'done'
// 2. Let result be the result of package data given
// bytes, type, blobs type, and encodingName.
try {
const result = packageData(bytes, type, blob.type, encodingName)
// 4. Else:
if (fr[kAborted]) {
return
}
// 1. Set frs result to result.
fr[kResult] = result
// 2. Fire a progress event called load at the fr.
fireAProgressEvent('load', fr)
} catch (error) {
// 3. If package data threw an exception error:
// 1. Set frs error to error.
fr[kError] = error
// 2. Fire a progress event called error at fr.
fireAProgressEvent('error', fr)
}
// 5. If frs state is not "loading", fire a progress
// event called loadend at the fr.
if (fr[kState] !== 'loading') {
fireAProgressEvent('loadend', fr)
}
})
break
}
} catch (error) {
if (fr[kAborted]) {
return
}
// 6. Otherwise, if chunkPromise is rejected with an
// error error, queue a task to run the following
// steps and abort this algorithm:
queueMicrotask(() => {
// 1. Set frs state to "done".
fr[kState] = 'done'
// 2. Set frs error to error.
fr[kError] = error
// 3. Fire a progress event called error at fr.
fireAProgressEvent('error', fr)
// 4. If frs state is not "loading", fire a progress
// event called loadend at fr.
if (fr[kState] !== 'loading') {
fireAProgressEvent('loadend', fr)
}
})
break
}
}
})()
}
/**
* @see https://w3c.github.io/FileAPI/#fire-a-progress-event
* @see https://dom.spec.whatwg.org/#concept-event-fire
* @param {string} e The name of the event
* @param {import('./filereader').FileReader} reader
*/
function fireAProgressEvent (e, reader) {
// The progress event e does not bubble. e.bubbles must be false
// The progress event e is NOT cancelable. e.cancelable must be false
const event = new ProgressEvent(e, {
bubbles: false,
cancelable: false
})
reader.dispatchEvent(event)
}
/**
* @see https://w3c.github.io/FileAPI/#blob-package-data
* @param {Uint8Array[]} bytes
* @param {string} type
* @param {string?} mimeType
* @param {string?} encodingName
*/
function packageData (bytes, type, mimeType, encodingName) {
// 1. A Blob has an associated package data algorithm, given
// bytes, a type, a optional mimeType, and a optional
// encodingName, which switches on type and runs the
// associated steps:
switch (type) {
case 'DataURL': {
// 1. Return bytes as a DataURL [RFC2397] subject to
// the considerations below:
// * Use mimeType as part of the Data URL if it is
// available in keeping with the Data URL
// specification [RFC2397].
// * If mimeType is not available return a Data URL
// without a media-type. [RFC2397].
// https://datatracker.ietf.org/doc/html/rfc2397#section-3
// dataurl := "data:" [ mediatype ] [ ";base64" ] "," data
// mediatype := [ type "/" subtype ] *( ";" parameter )
// data := *urlchar
// parameter := attribute "=" value
let dataURL = 'data:'
const parsed = parseMIMEType(mimeType || 'application/octet-stream')
if (parsed !== 'failure') {
dataURL += serializeAMimeType(parsed)
}
dataURL += ';base64,'
const decoder = new StringDecoder('latin1')
for (const chunk of bytes) {
dataURL += btoa(decoder.write(chunk))
}
dataURL += btoa(decoder.end())
return dataURL
}
case 'Text': {
// 1. Let encoding be failure
let encoding = 'failure'
// 2. If the encodingName is present, set encoding to the
// result of getting an encoding from encodingName.
if (encodingName) {
encoding = getEncoding(encodingName)
}
// 3. If encoding is failure, and mimeType is present:
if (encoding === 'failure' && mimeType) {
// 1. Let type be the result of parse a MIME type
// given mimeType.
const type = parseMIMEType(mimeType)
// 2. If type is not failure, set encoding to the result
// of getting an encoding from types parameters["charset"].
if (type !== 'failure') {
encoding = getEncoding(type.parameters.get('charset'))
}
}
// 4. If encoding is failure, then set encoding to UTF-8.
if (encoding === 'failure') {
encoding = 'UTF-8'
}
// 5. Decode bytes using fallback encoding encoding, and
// return the result.
return decode(bytes, encoding)
}
case 'ArrayBuffer': {
// Return a new ArrayBuffer whose contents are bytes.
const sequence = combineByteSequences(bytes)
return sequence.buffer
}
case 'BinaryString': {
// Return bytes as a binary string, in which every byte
// is represented by a code unit of equal value [0..255].
let binaryString = ''
const decoder = new StringDecoder('latin1')
for (const chunk of bytes) {
binaryString += decoder.write(chunk)
}
binaryString += decoder.end()
return binaryString
}
}
}
/**
* @see https://encoding.spec.whatwg.org/#decode
* @param {Uint8Array[]} ioQueue
* @param {string} encoding
*/
function decode (ioQueue, encoding) {
const bytes = combineByteSequences(ioQueue)
// 1. Let BOMEncoding be the result of BOM sniffing ioQueue.
const BOMEncoding = BOMSniffing(bytes)
let slice = 0
// 2. If BOMEncoding is non-null:
if (BOMEncoding !== null) {
// 1. Set encoding to BOMEncoding.
encoding = BOMEncoding
// 2. Read three bytes from ioQueue, if BOMEncoding is
// UTF-8; otherwise read two bytes.
// (Do nothing with those bytes.)
slice = BOMEncoding === 'UTF-8' ? 3 : 2
}
// 3. Process a queue with an instance of encodings
// decoder, ioQueue, output, and "replacement".
// 4. Return output.
const sliced = bytes.slice(slice)
return new TextDecoder(encoding).decode(sliced)
}
/**
* @see https://encoding.spec.whatwg.org/#bom-sniff
* @param {Uint8Array} ioQueue
*/
function BOMSniffing (ioQueue) {
// 1. Let BOM be the result of peeking 3 bytes from ioQueue,
// converted to a byte sequence.
const [a, b, c] = ioQueue
// 2. For each of the rows in the table below, starting with
// the first one and going down, if BOM starts with the
// bytes given in the first column, then return the
// encoding given in the cell in the second column of that
// row. Otherwise, return null.
if (a === 0xEF && b === 0xBB && c === 0xBF) {
return 'UTF-8'
} else if (a === 0xFE && b === 0xFF) {
return 'UTF-16BE'
} else if (a === 0xFF && b === 0xFE) {
return 'UTF-16LE'
}
return null
}
/**
* @param {Uint8Array[]} sequences
*/
function combineByteSequences (sequences) {
const size = sequences.reduce((a, b) => {
return a + b.byteLength
}, 0)
let offset = 0
return sequences.reduce((a, b) => {
a.set(b, offset)
offset += b.byteLength
return a
}, new Uint8Array(size))
}
module.exports = {
staticPropertyDescriptors,
readOperation,
fireAProgressEvent
}

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

@ -0,0 +1,32 @@
'use strict'
// We include a version number for the Dispatcher API. In case of breaking changes,
// this version number must be increased to avoid conflicts.
const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
const { InvalidArgumentError } = require('./core/errors')
const Agent = require('./agent')
if (getGlobalDispatcher() === undefined) {
setGlobalDispatcher(new Agent())
}
function setGlobalDispatcher (agent) {
if (!agent || typeof agent.dispatch !== 'function') {
throw new InvalidArgumentError('Argument agent must implement Agent')
}
Object.defineProperty(globalThis, globalDispatcher, {
value: agent,
writable: true,
enumerable: false,
configurable: false
})
}
function getGlobalDispatcher () {
return globalThis[globalDispatcher]
}
module.exports = {
setGlobalDispatcher,
getGlobalDispatcher
}

35
node_modules/undici/lib/handler/DecoratorHandler.js generated vendored Normal file
View File

@ -0,0 +1,35 @@
'use strict'
module.exports = class DecoratorHandler {
constructor (handler) {
this.handler = handler
}
onConnect (...args) {
return this.handler.onConnect(...args)
}
onError (...args) {
return this.handler.onError(...args)
}
onUpgrade (...args) {
return this.handler.onUpgrade(...args)
}
onHeaders (...args) {
return this.handler.onHeaders(...args)
}
onData (...args) {
return this.handler.onData(...args)
}
onComplete (...args) {
return this.handler.onComplete(...args)
}
onBodySent (...args) {
return this.handler.onBodySent(...args)
}
}

216
node_modules/undici/lib/handler/RedirectHandler.js generated vendored Normal file
View File

@ -0,0 +1,216 @@
'use strict'
const util = require('../core/util')
const { kBodyUsed } = require('../core/symbols')
const assert = require('assert')
const { InvalidArgumentError } = require('../core/errors')
const EE = require('events')
const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
const kBody = Symbol('body')
class BodyAsyncIterable {
constructor (body) {
this[kBody] = body
this[kBodyUsed] = false
}
async * [Symbol.asyncIterator] () {
assert(!this[kBodyUsed], 'disturbed')
this[kBodyUsed] = true
yield * this[kBody]
}
}
class RedirectHandler {
constructor (dispatch, maxRedirections, opts, handler) {
if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {
throw new InvalidArgumentError('maxRedirections must be a positive number')
}
util.validateHandler(handler, opts.method, opts.upgrade)
this.dispatch = dispatch
this.location = null
this.abort = null
this.opts = { ...opts, maxRedirections: 0 } // opts must be a copy
this.maxRedirections = maxRedirections
this.handler = handler
this.history = []
if (util.isStream(this.opts.body)) {
// TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
// so that it can be dispatched again?
// TODO (fix): Do we need 100-expect support to provide a way to do this properly?
if (util.bodyLength(this.opts.body) === 0) {
this.opts.body
.on('data', function () {
assert(false)
})
}
if (typeof this.opts.body.readableDidRead !== 'boolean') {
this.opts.body[kBodyUsed] = false
EE.prototype.on.call(this.opts.body, 'data', function () {
this[kBodyUsed] = true
})
}
} else if (this.opts.body && typeof this.opts.body.pipeTo === 'function') {
// TODO (fix): We can't access ReadableStream internal state
// to determine whether or not it has been disturbed. This is just
// a workaround.
this.opts.body = new BodyAsyncIterable(this.opts.body)
} else if (
this.opts.body &&
typeof this.opts.body !== 'string' &&
!ArrayBuffer.isView(this.opts.body) &&
util.isIterable(this.opts.body)
) {
// TODO: Should we allow re-using iterable if !this.opts.idempotent
// or through some other flag?
this.opts.body = new BodyAsyncIterable(this.opts.body)
}
}
onConnect (abort) {
this.abort = abort
this.handler.onConnect(abort, { history: this.history })
}
onUpgrade (statusCode, headers, socket) {
this.handler.onUpgrade(statusCode, headers, socket)
}
onError (error) {
this.handler.onError(error)
}
onHeaders (statusCode, headers, resume, statusText) {
this.location = this.history.length >= this.maxRedirections || util.isDisturbed(this.opts.body)
? null
: parseLocation(statusCode, headers)
if (this.opts.origin) {
this.history.push(new URL(this.opts.path, this.opts.origin))
}
if (!this.location) {
return this.handler.onHeaders(statusCode, headers, resume, statusText)
}
const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)))
const path = search ? `${pathname}${search}` : pathname
// Remove headers referring to the original URL.
// By default it is Host only, unless it's a 303 (see below), which removes also all Content-* headers.
// https://tools.ietf.org/html/rfc7231#section-6.4
this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin)
this.opts.path = path
this.opts.origin = origin
this.opts.maxRedirections = 0
this.opts.query = null
// https://tools.ietf.org/html/rfc7231#section-6.4.4
// In case of HTTP 303, always replace method to be either HEAD or GET
if (statusCode === 303 && this.opts.method !== 'HEAD') {
this.opts.method = 'GET'
this.opts.body = null
}
}
onData (chunk) {
if (this.location) {
/*
https://tools.ietf.org/html/rfc7231#section-6.4
TLDR: undici always ignores 3xx response bodies.
Redirection is used to serve the requested resource from another URL, so it is assumes that
no body is generated (and thus can be ignored). Even though generating a body is not prohibited.
For status 301, 302, 303, 307 and 308 (the latter from RFC 7238), the specs mention that the body usually
(which means it's optional and not mandated) contain just an hyperlink to the value of
the Location response header, so the body can be ignored safely.
For status 300, which is "Multiple Choices", the spec mentions both generating a Location
response header AND a response body with the other possible location to follow.
Since the spec explicitily chooses not to specify a format for such body and leave it to
servers and browsers implementors, we ignore the body as there is no specified way to eventually parse it.
*/
} else {
return this.handler.onData(chunk)
}
}
onComplete (trailers) {
if (this.location) {
/*
https://tools.ietf.org/html/rfc7231#section-6.4
TLDR: undici always ignores 3xx response trailers as they are not expected in case of redirections
and neither are useful if present.
See comment on onData method above for more detailed informations.
*/
this.location = null
this.abort = null
this.dispatch(this.opts, this)
} else {
this.handler.onComplete(trailers)
}
}
onBodySent (chunk) {
if (this.handler.onBodySent) {
this.handler.onBodySent(chunk)
}
}
}
function parseLocation (statusCode, headers) {
if (redirectableStatusCodes.indexOf(statusCode) === -1) {
return null
}
for (let i = 0; i < headers.length; i += 2) {
if (headers[i].toString().toLowerCase() === 'location') {
return headers[i + 1]
}
}
}
// https://tools.ietf.org/html/rfc7231#section-6.4.4
function shouldRemoveHeader (header, removeContent, unknownOrigin) {
return (
(header.length === 4 && header.toString().toLowerCase() === 'host') ||
(removeContent && header.toString().toLowerCase().indexOf('content-') === 0) ||
(unknownOrigin && header.length === 13 && header.toString().toLowerCase() === 'authorization') ||
(unknownOrigin && header.length === 6 && header.toString().toLowerCase() === 'cookie')
)
}
// https://tools.ietf.org/html/rfc7231#section-6.4
function cleanRequestHeaders (headers, removeContent, unknownOrigin) {
const ret = []
if (Array.isArray(headers)) {
for (let i = 0; i < headers.length; i += 2) {
if (!shouldRemoveHeader(headers[i], removeContent, unknownOrigin)) {
ret.push(headers[i], headers[i + 1])
}
}
} else if (headers && typeof headers === 'object') {
for (const key of Object.keys(headers)) {
if (!shouldRemoveHeader(key, removeContent, unknownOrigin)) {
ret.push(key, headers[key])
}
}
} else {
assert(headers == null, 'headers must be an object or an array')
}
return ret
}
module.exports = RedirectHandler

View File

@ -0,0 +1,21 @@
'use strict'
const RedirectHandler = require('../handler/RedirectHandler')
function createRedirectInterceptor ({ maxRedirections: defaultMaxRedirections }) {
return (dispatch) => {
return function Intercept (opts, handler) {
const { maxRedirections = defaultMaxRedirections } = opts
if (!maxRedirections) {
return dispatch(opts, handler)
}
const redirectHandler = new RedirectHandler(dispatch, maxRedirections, opts, handler)
opts = { ...opts, maxRedirections: 0 } // Stop sub dispatcher from also redirecting.
return dispatch(opts, redirectHandler)
}
}
}
module.exports = createRedirectInterceptor

199
node_modules/undici/lib/llhttp/constants.d.ts generated vendored Normal file
View File

@ -0,0 +1,199 @@
import { IEnumMap } from './utils';
export declare type HTTPMode = 'loose' | 'strict';
export declare enum ERROR {
OK = 0,
INTERNAL = 1,
STRICT = 2,
LF_EXPECTED = 3,
UNEXPECTED_CONTENT_LENGTH = 4,
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
}
export declare enum TYPE {
BOTH = 0,
REQUEST = 1,
RESPONSE = 2
}
export declare enum FLAGS {
CONNECTION_KEEP_ALIVE = 1,
CONNECTION_CLOSE = 2,
CONNECTION_UPGRADE = 4,
CHUNKED = 8,
UPGRADE = 16,
CONTENT_LENGTH = 32,
SKIPBODY = 64,
TRAILING = 128,
TRANSFER_ENCODING = 512
}
export declare enum LENIENT_FLAGS {
HEADERS = 1,
CHUNKED_LENGTH = 2,
KEEP_ALIVE = 4
}
export declare enum METHODS {
DELETE = 0,
GET = 1,
HEAD = 2,
POST = 3,
PUT = 4,
CONNECT = 5,
OPTIONS = 6,
TRACE = 7,
COPY = 8,
LOCK = 9,
MKCOL = 10,
MOVE = 11,
PROPFIND = 12,
PROPPATCH = 13,
SEARCH = 14,
UNLOCK = 15,
BIND = 16,
REBIND = 17,
UNBIND = 18,
ACL = 19,
REPORT = 20,
MKACTIVITY = 21,
CHECKOUT = 22,
MERGE = 23,
'M-SEARCH' = 24,
NOTIFY = 25,
SUBSCRIBE = 26,
UNSUBSCRIBE = 27,
PATCH = 28,
PURGE = 29,
MKCALENDAR = 30,
LINK = 31,
UNLINK = 32,
SOURCE = 33,
PRI = 34,
DESCRIBE = 35,
ANNOUNCE = 36,
SETUP = 37,
PLAY = 38,
PAUSE = 39,
TEARDOWN = 40,
GET_PARAMETER = 41,
SET_PARAMETER = 42,
REDIRECT = 43,
RECORD = 44,
FLUSH = 45
}
export declare const METHODS_HTTP: METHODS[];
export declare const METHODS_ICE: METHODS[];
export declare const METHODS_RTSP: METHODS[];
export declare const METHOD_MAP: IEnumMap;
export declare const H_METHOD_MAP: IEnumMap;
export declare enum FINISH {
SAFE = 0,
SAFE_WITH_CB = 1,
UNSAFE = 2
}
export declare type CharList = Array<string | number>;
export declare const ALPHA: CharList;
export declare const NUM_MAP: {
0: number;
1: number;
2: number;
3: number;
4: number;
5: number;
6: number;
7: number;
8: number;
9: number;
};
export declare const HEX_MAP: {
0: number;
1: number;
2: number;
3: number;
4: number;
5: number;
6: number;
7: number;
8: number;
9: number;
A: number;
B: number;
C: number;
D: number;
E: number;
F: number;
a: number;
b: number;
c: number;
d: number;
e: number;
f: number;
};
export declare const NUM: CharList;
export declare const ALPHANUM: CharList;
export declare const MARK: CharList;
export declare const USERINFO_CHARS: CharList;
export declare const STRICT_URL_CHAR: CharList;
export declare const URL_CHAR: CharList;
export declare const HEX: CharList;
export declare const STRICT_TOKEN: CharList;
export declare const TOKEN: CharList;
export declare const HEADER_CHARS: CharList;
export declare const CONNECTION_TOKEN_CHARS: CharList;
export declare const MAJOR: {
0: number;
1: number;
2: number;
3: number;
4: number;
5: number;
6: number;
7: number;
8: number;
9: number;
};
export declare const MINOR: {
0: number;
1: number;
2: number;
3: number;
4: number;
5: number;
6: number;
7: number;
8: number;
9: number;
};
export declare enum 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
}
export declare const SPECIAL_HEADERS: {
connection: HEADER_STATE;
'content-length': HEADER_STATE;
'proxy-connection': HEADER_STATE;
'transfer-encoding': HEADER_STATE;
upgrade: HEADER_STATE;
};

278
node_modules/undici/lib/llhttp/constants.js generated vendored Normal file
View File

@ -0,0 +1,278 @@
"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;
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";
// 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";
/* pathological */
METHODS[METHODS["CONNECT"] = 5] = "CONNECT";
METHODS[METHODS["OPTIONS"] = 6] = "OPTIONS";
METHODS[METHODS["TRACE"] = 7] = "TRACE";
/* 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";
/* subversion */
METHODS[METHODS["REPORT"] = 20] = "REPORT";
METHODS[METHODS["MKACTIVITY"] = 21] = "MKACTIVITY";
METHODS[METHODS["CHECKOUT"] = 22] = "CHECKOUT";
METHODS[METHODS["MERGE"] = 23] = "MERGE";
/* upnp */
METHODS[METHODS["M-SEARCH"] = 24] = "M-SEARCH";
METHODS[METHODS["NOTIFY"] = 25] = "NOTIFY";
METHODS[METHODS["SUBSCRIBE"] = 26] = "SUBSCRIBE";
METHODS[METHODS["UNSUBSCRIBE"] = 27] = "UNSUBSCRIBE";
/* RFC-5789 */
METHODS[METHODS["PATCH"] = 28] = "PATCH";
METHODS[METHODS["PURGE"] = 29] = "PURGE";
/* CalDAV */
METHODS[METHODS["MKCALENDAR"] = 30] = "MKCALENDAR";
/* RFC-2068, section 19.6.1.2 */
METHODS[METHODS["LINK"] = 31] = "LINK";
METHODS[METHODS["UNLINK"] = 32] = "UNLINK";
/* icecast */
METHODS[METHODS["SOURCE"] = 33] = "SOURCE";
/* RFC-7540, section 11.6 */
METHODS[METHODS["PRI"] = 34] = "PRI";
/* 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";
/* RAOP */
METHODS[METHODS["FLUSH"] = 45] = "FLUSH";
})(METHODS = exports.METHODS || (exports.METHODS = {}));
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,
// TODO(indutny): should we allow it with HTTP?
METHODS.SOURCE,
];
exports.METHODS_ICE = [
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,
// For AirPlay
METHODS.GET,
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.ALPHA = [];
for (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); i++) {
// Upper case
exports.ALPHA.push(String.fromCharCode(i));
// Lower case
exports.ALPHA.push(String.fromCharCode(i + 0x20));
}
exports.NUM_MAP = {
0: 0, 1: 1, 2: 2, 3: 3, 4: 4,
5: 5, 6: 6, 7: 7, 8: 8, 9: 9,
};
exports.HEX_MAP = {
0: 0, 1: 1, 2: 2, 3: 3, 4: 4,
5: 5, 6: 6, 7: 7, 8: 8, 9: 9,
A: 0XA, B: 0XB, C: 0XC, D: 0XD, E: 0XE, F: 0XF,
a: 0xa, b: 0xb, c: 0xc, d: 0xd, e: 0xe, f: 0xf,
};
exports.NUM = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
];
exports.ALPHANUM = exports.ALPHA.concat(exports.NUM);
exports.MARK = ['-', '_', '.', '!', '~', '*', '\'', '(', ')'];
exports.USERINFO_CHARS = exports.ALPHANUM
.concat(exports.MARK)
.concat(['%', ';', ':', '&', '=', '+', '$', ',']);
// TODO(indutny): use RFC
exports.STRICT_URL_CHAR = [
'!', '"', '$', '%', '&', '\'',
'(', ')', '*', '+', ',', '-', '.', '/',
':', ';', '<', '=', '>',
'@', '[', '\\', ']', '^', '_',
'`',
'{', '|', '}', '~',
].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']);
/* Tokens as defined by rfc 2616. Also lowercases them.
* token = 1*<any CHAR except CTLs or separators>
* separators = "(" | ")" | "<" | ">" | "@"
* | "," | ";" | ":" | "\" | <">
* | "/" | "[" | "]" | "?" | "="
* | "{" | "}" | SP | HT
*/
exports.STRICT_TOKEN = [
'!', '#', '$', '%', '&', '\'',
'*', '+', '-', '.',
'^', '_', '`',
'|', '~',
].concat(exports.ALPHANUM);
exports.TOKEN = exports.STRICT_TOKEN.concat([' ']);
/*
* Verify that a char is a valid visible (printable) US-ASCII
* character or %x80-FF
*/
exports.HEADER_CHARS = ['\t'];
for (let i = 32; i <= 255; i++) {
if (i !== 127) {
exports.HEADER_CHARS.push(i);
}
}
// ',' = \x44
exports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS.filter((c) => c !== 44);
exports.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,
};
//# sourceMappingURL=constants.js.map

1
node_modules/undici/lib/llhttp/constants.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

1
node_modules/undici/lib/llhttp/llhttp-wasm.js generated vendored Normal file

File diff suppressed because one or more lines are too long

BIN
node_modules/undici/lib/llhttp/llhttp.wasm generated vendored Normal file

Binary file not shown.

1
node_modules/undici/lib/llhttp/llhttp_simd-wasm.js generated vendored Normal file

File diff suppressed because one or more lines are too long

BIN
node_modules/undici/lib/llhttp/llhttp_simd.wasm generated vendored Normal file

Binary file not shown.

4
node_modules/undici/lib/llhttp/utils.d.ts generated vendored Normal file
View File

@ -0,0 +1,4 @@
export interface IEnumMap {
[key: string]: number;
}
export declare function enumToMap(obj: any): IEnumMap;

15
node_modules/undici/lib/llhttp/utils.js generated vendored Normal file
View File

@ -0,0 +1,15 @@
"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;
}
exports.enumToMap = enumToMap;
//# sourceMappingURL=utils.js.map

1
node_modules/undici/lib/llhttp/utils.js.map generated vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/llhttp/utils.ts"],"names":[],"mappings":";;;AAIA,SAAgB,SAAS,CAAC,GAAQ;IAChC,MAAM,GAAG,GAAa,EAAE,CAAC;IAEzB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;YAC7B,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;SAClB;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC;AAXD,8BAWC"}

32
node_modules/undici/lib/llhttp/wasm_build_env.txt generated vendored Normal file
View File

@ -0,0 +1,32 @@
alpine-baselayout-data-3.4.0-r0
musl-1.2.3-r4
busybox-1.35.0-r29
busybox-binsh-1.35.0-r29
alpine-baselayout-3.4.0-r0
alpine-keys-2.4-r1
ca-certificates-bundle-20220614-r4
libcrypto3-3.0.8-r3
libssl3-3.0.8-r3
ssl_client-1.35.0-r29
zlib-1.2.13-r0
apk-tools-2.12.10-r1
scanelf-1.3.5-r1
musl-utils-1.2.3-r4
libc-utils-0.7.2-r3
libgcc-12.2.1_git20220924-r4
libstdc++-12.2.1_git20220924-r4
libffi-3.4.4-r0
xz-libs-5.2.9-r0
libxml2-2.10.4-r0
zstd-libs-1.5.5-r0
llvm15-libs-15.0.7-r0
clang15-libs-15.0.7-r0
libstdc++-dev-12.2.1_git20220924-r4
clang15-15.0.7-r0
lld-libs-15.0.7-r0
lld-15.0.7-r0
wasi-libc-0.20220525-r1
wasi-libcxx-15.0.7-r0
wasi-libcxxabi-15.0.7-r0
wasi-compiler-rt-15.0.7-r0
wasi-sdk-16-r0

171
node_modules/undici/lib/mock/mock-agent.js generated vendored Normal file
View File

@ -0,0 +1,171 @@
'use strict'
const { kClients } = require('../core/symbols')
const Agent = require('../agent')
const {
kAgent,
kMockAgentSet,
kMockAgentGet,
kDispatches,
kIsMockActive,
kNetConnect,
kGetNetConnect,
kOptions,
kFactory
} = require('./mock-symbols')
const MockClient = require('./mock-client')
const MockPool = require('./mock-pool')
const { matchValue, buildMockOptions } = require('./mock-utils')
const { InvalidArgumentError, UndiciError } = require('../core/errors')
const Dispatcher = require('../dispatcher')
const Pluralizer = require('./pluralizer')
const PendingInterceptorsFormatter = require('./pending-interceptors-formatter')
class FakeWeakRef {
constructor (value) {
this.value = value
}
deref () {
return this.value
}
}
class MockAgent extends Dispatcher {
constructor (opts) {
super(opts)
this[kNetConnect] = true
this[kIsMockActive] = true
// Instantiate Agent and encapsulate
if ((opts && opts.agent && typeof opts.agent.dispatch !== 'function')) {
throw new InvalidArgumentError('Argument opts.agent must implement Agent')
}
const agent = opts && opts.agent ? opts.agent : new Agent(opts)
this[kAgent] = agent
this[kClients] = agent[kClients]
this[kOptions] = buildMockOptions(opts)
}
get (origin) {
let dispatcher = this[kMockAgentGet](origin)
if (!dispatcher) {
dispatcher = this[kFactory](origin)
this[kMockAgentSet](origin, dispatcher)
}
return dispatcher
}
dispatch (opts, handler) {
// Call MockAgent.get to perform additional setup before dispatching as normal
this.get(opts.origin)
return this[kAgent].dispatch(opts, handler)
}
async close () {
await this[kAgent].close()
this[kClients].clear()
}
deactivate () {
this[kIsMockActive] = false
}
activate () {
this[kIsMockActive] = true
}
enableNetConnect (matcher) {
if (typeof matcher === 'string' || typeof matcher === 'function' || matcher instanceof RegExp) {
if (Array.isArray(this[kNetConnect])) {
this[kNetConnect].push(matcher)
} else {
this[kNetConnect] = [matcher]
}
} else if (typeof matcher === 'undefined') {
this[kNetConnect] = true
} else {
throw new InvalidArgumentError('Unsupported matcher. Must be one of String|Function|RegExp.')
}
}
disableNetConnect () {
this[kNetConnect] = false
}
// This is required to bypass issues caused by using global symbols - see:
// https://github.com/nodejs/undici/issues/1447
get isMockActive () {
return this[kIsMockActive]
}
[kMockAgentSet] (origin, dispatcher) {
this[kClients].set(origin, new FakeWeakRef(dispatcher))
}
[kFactory] (origin) {
const mockOptions = Object.assign({ agent: this }, this[kOptions])
return this[kOptions] && this[kOptions].connections === 1
? new MockClient(origin, mockOptions)
: new MockPool(origin, mockOptions)
}
[kMockAgentGet] (origin) {
// First check if we can immediately find it
const ref = this[kClients].get(origin)
if (ref) {
return ref.deref()
}
// If the origin is not a string create a dummy parent pool and return to user
if (typeof origin !== 'string') {
const dispatcher = this[kFactory]('http://localhost:9999')
this[kMockAgentSet](origin, dispatcher)
return dispatcher
}
// If we match, create a pool and assign the same dispatches
for (const [keyMatcher, nonExplicitRef] of Array.from(this[kClients])) {
const nonExplicitDispatcher = nonExplicitRef.deref()
if (nonExplicitDispatcher && typeof keyMatcher !== 'string' && matchValue(keyMatcher, origin)) {
const dispatcher = this[kFactory](origin)
this[kMockAgentSet](origin, dispatcher)
dispatcher[kDispatches] = nonExplicitDispatcher[kDispatches]
return dispatcher
}
}
}
[kGetNetConnect] () {
return this[kNetConnect]
}
pendingInterceptors () {
const mockAgentClients = this[kClients]
return Array.from(mockAgentClients.entries())
.flatMap(([origin, scope]) => scope.deref()[kDispatches].map(dispatch => ({ ...dispatch, origin })))
.filter(({ pending }) => pending)
}
assertNoPendingInterceptors ({ pendingInterceptorsFormatter = new PendingInterceptorsFormatter() } = {}) {
const pending = this.pendingInterceptors()
if (pending.length === 0) {
return
}
const pluralizer = new Pluralizer('interceptor', 'interceptors').pluralize(pending.length)
throw new UndiciError(`
${pluralizer.count} ${pluralizer.noun} ${pluralizer.is} pending:
${pendingInterceptorsFormatter.format(pending)}
`.trim())
}
}
module.exports = MockAgent

59
node_modules/undici/lib/mock/mock-client.js generated vendored Normal file
View File

@ -0,0 +1,59 @@
'use strict'
const { promisify } = require('util')
const Client = require('../client')
const { buildMockDispatch } = require('./mock-utils')
const {
kDispatches,
kMockAgent,
kClose,
kOriginalClose,
kOrigin,
kOriginalDispatch,
kConnected
} = require('./mock-symbols')
const { MockInterceptor } = require('./mock-interceptor')
const Symbols = require('../core/symbols')
const { InvalidArgumentError } = require('../core/errors')
/**
* MockClient provides an API that extends the Client to influence the mockDispatches.
*/
class MockClient extends Client {
constructor (origin, opts) {
super(origin, opts)
if (!opts || !opts.agent || typeof opts.agent.dispatch !== 'function') {
throw new InvalidArgumentError('Argument opts.agent must implement Agent')
}
this[kMockAgent] = opts.agent
this[kOrigin] = origin
this[kDispatches] = []
this[kConnected] = 1
this[kOriginalDispatch] = this.dispatch
this[kOriginalClose] = this.close.bind(this)
this.dispatch = buildMockDispatch.call(this)
this.close = this[kClose]
}
get [Symbols.kConnected] () {
return this[kConnected]
}
/**
* Sets up the base interceptor for mocking replies from undici.
*/
intercept (opts) {
return new MockInterceptor(opts, this[kDispatches])
}
async [kClose] () {
await promisify(this[kOriginalClose])()
this[kConnected] = 0
this[kMockAgent][Symbols.kClients].delete(this[kOrigin])
}
}
module.exports = MockClient

17
node_modules/undici/lib/mock/mock-errors.js generated vendored Normal file
View File

@ -0,0 +1,17 @@
'use strict'
const { UndiciError } = require('../core/errors')
class MockNotMatchedError extends UndiciError {
constructor (message) {
super(message)
Error.captureStackTrace(this, MockNotMatchedError)
this.name = 'MockNotMatchedError'
this.message = message || 'The request does not match any registered mock dispatches'
this.code = 'UND_MOCK_ERR_MOCK_NOT_MATCHED'
}
}
module.exports = {
MockNotMatchedError
}

206
node_modules/undici/lib/mock/mock-interceptor.js generated vendored Normal file
View File

@ -0,0 +1,206 @@
'use strict'
const { getResponseData, buildKey, addMockDispatch } = require('./mock-utils')
const {
kDispatches,
kDispatchKey,
kDefaultHeaders,
kDefaultTrailers,
kContentLength,
kMockDispatch
} = require('./mock-symbols')
const { InvalidArgumentError } = require('../core/errors')
const { buildURL } = require('../core/util')
/**
* Defines the scope API for an interceptor reply
*/
class MockScope {
constructor (mockDispatch) {
this[kMockDispatch] = mockDispatch
}
/**
* Delay a reply by a set amount in ms.
*/
delay (waitInMs) {
if (typeof waitInMs !== 'number' || !Number.isInteger(waitInMs) || waitInMs <= 0) {
throw new InvalidArgumentError('waitInMs must be a valid integer > 0')
}
this[kMockDispatch].delay = waitInMs
return this
}
/**
* For a defined reply, never mark as consumed.
*/
persist () {
this[kMockDispatch].persist = true
return this
}
/**
* Allow one to define a reply for a set amount of matching requests.
*/
times (repeatTimes) {
if (typeof repeatTimes !== 'number' || !Number.isInteger(repeatTimes) || repeatTimes <= 0) {
throw new InvalidArgumentError('repeatTimes must be a valid integer > 0')
}
this[kMockDispatch].times = repeatTimes
return this
}
}
/**
* Defines an interceptor for a Mock
*/
class MockInterceptor {
constructor (opts, mockDispatches) {
if (typeof opts !== 'object') {
throw new InvalidArgumentError('opts must be an object')
}
if (typeof opts.path === 'undefined') {
throw new InvalidArgumentError('opts.path must be defined')
}
if (typeof opts.method === 'undefined') {
opts.method = 'GET'
}
// See https://github.com/nodejs/undici/issues/1245
// As per RFC 3986, clients are not supposed to send URI
// fragments to servers when they retrieve a document,
if (typeof opts.path === 'string') {
if (opts.query) {
opts.path = buildURL(opts.path, opts.query)
} else {
// Matches https://github.com/nodejs/undici/blob/main/lib/fetch/index.js#L1811
const parsedURL = new URL(opts.path, 'data://')
opts.path = parsedURL.pathname + parsedURL.search
}
}
if (typeof opts.method === 'string') {
opts.method = opts.method.toUpperCase()
}
this[kDispatchKey] = buildKey(opts)
this[kDispatches] = mockDispatches
this[kDefaultHeaders] = {}
this[kDefaultTrailers] = {}
this[kContentLength] = false
}
createMockScopeDispatchData (statusCode, data, responseOptions = {}) {
const responseData = getResponseData(data)
const contentLength = this[kContentLength] ? { 'content-length': responseData.length } : {}
const headers = { ...this[kDefaultHeaders], ...contentLength, ...responseOptions.headers }
const trailers = { ...this[kDefaultTrailers], ...responseOptions.trailers }
return { statusCode, data, headers, trailers }
}
validateReplyParameters (statusCode, data, responseOptions) {
if (typeof statusCode === 'undefined') {
throw new InvalidArgumentError('statusCode must be defined')
}
if (typeof data === 'undefined') {
throw new InvalidArgumentError('data must be defined')
}
if (typeof responseOptions !== 'object') {
throw new InvalidArgumentError('responseOptions must be an object')
}
}
/**
* Mock an undici request with a defined reply.
*/
reply (replyData) {
// Values of reply aren't available right now as they
// can only be available when the reply callback is invoked.
if (typeof replyData === 'function') {
// We'll first wrap the provided callback in another function,
// this function will properly resolve the data from the callback
// when invoked.
const wrappedDefaultsCallback = (opts) => {
// Our reply options callback contains the parameter for statusCode, data and options.
const resolvedData = replyData(opts)
// Check if it is in the right format
if (typeof resolvedData !== 'object') {
throw new InvalidArgumentError('reply options callback must return an object')
}
const { statusCode, data = '', responseOptions = {} } = resolvedData
this.validateReplyParameters(statusCode, data, responseOptions)
// Since the values can be obtained immediately we return them
// from this higher order function that will be resolved later.
return {
...this.createMockScopeDispatchData(statusCode, data, responseOptions)
}
}
// Add usual dispatch data, but this time set the data parameter to function that will eventually provide data.
const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], wrappedDefaultsCallback)
return new MockScope(newMockDispatch)
}
// We can have either one or three parameters, if we get here,
// we should have 1-3 parameters. So we spread the arguments of
// this function to obtain the parameters, since replyData will always
// just be the statusCode.
const [statusCode, data = '', responseOptions = {}] = [...arguments]
this.validateReplyParameters(statusCode, data, responseOptions)
// Send in-already provided data like usual
const dispatchData = this.createMockScopeDispatchData(statusCode, data, responseOptions)
const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], dispatchData)
return new MockScope(newMockDispatch)
}
/**
* Mock an undici request with a defined error.
*/
replyWithError (error) {
if (typeof error === 'undefined') {
throw new InvalidArgumentError('error must be defined')
}
const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], { error })
return new MockScope(newMockDispatch)
}
/**
* Set default reply headers on the interceptor for subsequent replies
*/
defaultReplyHeaders (headers) {
if (typeof headers === 'undefined') {
throw new InvalidArgumentError('headers must be defined')
}
this[kDefaultHeaders] = headers
return this
}
/**
* Set default reply trailers on the interceptor for subsequent replies
*/
defaultReplyTrailers (trailers) {
if (typeof trailers === 'undefined') {
throw new InvalidArgumentError('trailers must be defined')
}
this[kDefaultTrailers] = trailers
return this
}
/**
* Set reply content length header for replies on the interceptor
*/
replyContentLength () {
this[kContentLength] = true
return this
}
}
module.exports.MockInterceptor = MockInterceptor
module.exports.MockScope = MockScope

59
node_modules/undici/lib/mock/mock-pool.js generated vendored Normal file
View File

@ -0,0 +1,59 @@
'use strict'
const { promisify } = require('util')
const Pool = require('../pool')
const { buildMockDispatch } = require('./mock-utils')
const {
kDispatches,
kMockAgent,
kClose,
kOriginalClose,
kOrigin,
kOriginalDispatch,
kConnected
} = require('./mock-symbols')
const { MockInterceptor } = require('./mock-interceptor')
const Symbols = require('../core/symbols')
const { InvalidArgumentError } = require('../core/errors')
/**
* MockPool provides an API that extends the Pool to influence the mockDispatches.
*/
class MockPool extends Pool {
constructor (origin, opts) {
super(origin, opts)
if (!opts || !opts.agent || typeof opts.agent.dispatch !== 'function') {
throw new InvalidArgumentError('Argument opts.agent must implement Agent')
}
this[kMockAgent] = opts.agent
this[kOrigin] = origin
this[kDispatches] = []
this[kConnected] = 1
this[kOriginalDispatch] = this.dispatch
this[kOriginalClose] = this.close.bind(this)
this.dispatch = buildMockDispatch.call(this)
this.close = this[kClose]
}
get [Symbols.kConnected] () {
return this[kConnected]
}
/**
* Sets up the base interceptor for mocking replies from undici.
*/
intercept (opts) {
return new MockInterceptor(opts, this[kDispatches])
}
async [kClose] () {
await promisify(this[kOriginalClose])()
this[kConnected] = 0
this[kMockAgent][Symbols.kClients].delete(this[kOrigin])
}
}
module.exports = MockPool

23
node_modules/undici/lib/mock/mock-symbols.js generated vendored Normal file
View File

@ -0,0 +1,23 @@
'use strict'
module.exports = {
kAgent: Symbol('agent'),
kOptions: Symbol('options'),
kFactory: Symbol('factory'),
kDispatches: Symbol('dispatches'),
kDispatchKey: Symbol('dispatch key'),
kDefaultHeaders: Symbol('default headers'),
kDefaultTrailers: Symbol('default trailers'),
kContentLength: Symbol('content length'),
kMockAgent: Symbol('mock agent'),
kMockAgentSet: Symbol('mock agent set'),
kMockAgentGet: Symbol('mock agent get'),
kMockDispatch: Symbol('mock dispatch'),
kClose: Symbol('close'),
kOriginalClose: Symbol('original agent close'),
kOrigin: Symbol('origin'),
kIsMockActive: Symbol('is mock active'),
kNetConnect: Symbol('net connect'),
kGetNetConnect: Symbol('get net connect'),
kConnected: Symbol('connected')
}

351
node_modules/undici/lib/mock/mock-utils.js generated vendored Normal file
View File

@ -0,0 +1,351 @@
'use strict'
const { MockNotMatchedError } = require('./mock-errors')
const {
kDispatches,
kMockAgent,
kOriginalDispatch,
kOrigin,
kGetNetConnect
} = require('./mock-symbols')
const { buildURL, nop } = require('../core/util')
const { STATUS_CODES } = require('http')
const {
types: {
isPromise
}
} = require('util')
function matchValue (match, value) {
if (typeof match === 'string') {
return match === value
}
if (match instanceof RegExp) {
return match.test(value)
}
if (typeof match === 'function') {
return match(value) === true
}
return false
}
function lowerCaseEntries (headers) {
return Object.fromEntries(
Object.entries(headers).map(([headerName, headerValue]) => {
return [headerName.toLocaleLowerCase(), headerValue]
})
)
}
/**
* @param {import('../../index').Headers|string[]|Record<string, string>} headers
* @param {string} key
*/
function getHeaderByName (headers, key) {
if (Array.isArray(headers)) {
for (let i = 0; i < headers.length; i += 2) {
if (headers[i].toLocaleLowerCase() === key.toLocaleLowerCase()) {
return headers[i + 1]
}
}
return undefined
} else if (typeof headers.get === 'function') {
return headers.get(key)
} else {
return lowerCaseEntries(headers)[key.toLocaleLowerCase()]
}
}
/** @param {string[]} headers */
function buildHeadersFromArray (headers) { // fetch HeadersList
const clone = headers.slice()
const entries = []
for (let index = 0; index < clone.length; index += 2) {
entries.push([clone[index], clone[index + 1]])
}
return Object.fromEntries(entries)
}
function matchHeaders (mockDispatch, headers) {
if (typeof mockDispatch.headers === 'function') {
if (Array.isArray(headers)) { // fetch HeadersList
headers = buildHeadersFromArray(headers)
}
return mockDispatch.headers(headers ? lowerCaseEntries(headers) : {})
}
if (typeof mockDispatch.headers === 'undefined') {
return true
}
if (typeof headers !== 'object' || typeof mockDispatch.headers !== 'object') {
return false
}
for (const [matchHeaderName, matchHeaderValue] of Object.entries(mockDispatch.headers)) {
const headerValue = getHeaderByName(headers, matchHeaderName)
if (!matchValue(matchHeaderValue, headerValue)) {
return false
}
}
return true
}
function safeUrl (path) {
if (typeof path !== 'string') {
return path
}
const pathSegments = path.split('?')
if (pathSegments.length !== 2) {
return path
}
const qp = new URLSearchParams(pathSegments.pop())
qp.sort()
return [...pathSegments, qp.toString()].join('?')
}
function matchKey (mockDispatch, { path, method, body, headers }) {
const pathMatch = matchValue(mockDispatch.path, path)
const methodMatch = matchValue(mockDispatch.method, method)
const bodyMatch = typeof mockDispatch.body !== 'undefined' ? matchValue(mockDispatch.body, body) : true
const headersMatch = matchHeaders(mockDispatch, headers)
return pathMatch && methodMatch && bodyMatch && headersMatch
}
function getResponseData (data) {
if (Buffer.isBuffer(data)) {
return data
} else if (typeof data === 'object') {
return JSON.stringify(data)
} else {
return data.toString()
}
}
function getMockDispatch (mockDispatches, key) {
const basePath = key.query ? buildURL(key.path, key.query) : key.path
const resolvedPath = typeof basePath === 'string' ? safeUrl(basePath) : basePath
// Match path
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path }) => matchValue(safeUrl(path), resolvedPath))
if (matchedMockDispatches.length === 0) {
throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`)
}
// Match method
matchedMockDispatches = matchedMockDispatches.filter(({ method }) => matchValue(method, key.method))
if (matchedMockDispatches.length === 0) {
throw new MockNotMatchedError(`Mock dispatch not matched for method '${key.method}'`)
}
// Match body
matchedMockDispatches = matchedMockDispatches.filter(({ body }) => typeof body !== 'undefined' ? matchValue(body, key.body) : true)
if (matchedMockDispatches.length === 0) {
throw new MockNotMatchedError(`Mock dispatch not matched for body '${key.body}'`)
}
// Match headers
matchedMockDispatches = matchedMockDispatches.filter((mockDispatch) => matchHeaders(mockDispatch, key.headers))
if (matchedMockDispatches.length === 0) {
throw new MockNotMatchedError(`Mock dispatch not matched for headers '${typeof key.headers === 'object' ? JSON.stringify(key.headers) : key.headers}'`)
}
return matchedMockDispatches[0]
}
function addMockDispatch (mockDispatches, key, data) {
const baseData = { timesInvoked: 0, times: 1, persist: false, consumed: false }
const replyData = typeof data === 'function' ? { callback: data } : { ...data }
const newMockDispatch = { ...baseData, ...key, pending: true, data: { error: null, ...replyData } }
mockDispatches.push(newMockDispatch)
return newMockDispatch
}
function deleteMockDispatch (mockDispatches, key) {
const index = mockDispatches.findIndex(dispatch => {
if (!dispatch.consumed) {
return false
}
return matchKey(dispatch, key)
})
if (index !== -1) {
mockDispatches.splice(index, 1)
}
}
function buildKey (opts) {
const { path, method, body, headers, query } = opts
return {
path,
method,
body,
headers,
query
}
}
function generateKeyValues (data) {
return Object.entries(data).reduce((keyValuePairs, [key, value]) => [
...keyValuePairs,
Buffer.from(`${key}`),
Array.isArray(value) ? value.map(x => Buffer.from(`${x}`)) : Buffer.from(`${value}`)
], [])
}
/**
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
* @param {number} statusCode
*/
function getStatusText (statusCode) {
return STATUS_CODES[statusCode] || 'unknown'
}
async function getResponse (body) {
const buffers = []
for await (const data of body) {
buffers.push(data)
}
return Buffer.concat(buffers).toString('utf8')
}
/**
* Mock dispatch function used to simulate undici dispatches
*/
function mockDispatch (opts, handler) {
// Get mock dispatch from built key
const key = buildKey(opts)
const mockDispatch = getMockDispatch(this[kDispatches], key)
mockDispatch.timesInvoked++
// Here's where we resolve a callback if a callback is present for the dispatch data.
if (mockDispatch.data.callback) {
mockDispatch.data = { ...mockDispatch.data, ...mockDispatch.data.callback(opts) }
}
// Parse mockDispatch data
const { data: { statusCode, data, headers, trailers, error }, delay, persist } = mockDispatch
const { timesInvoked, times } = mockDispatch
// If it's used up and not persistent, mark as consumed
mockDispatch.consumed = !persist && timesInvoked >= times
mockDispatch.pending = timesInvoked < times
// If specified, trigger dispatch error
if (error !== null) {
deleteMockDispatch(this[kDispatches], key)
handler.onError(error)
return true
}
// Handle the request with a delay if necessary
if (typeof delay === 'number' && delay > 0) {
setTimeout(() => {
handleReply(this[kDispatches])
}, delay)
} else {
handleReply(this[kDispatches])
}
function handleReply (mockDispatches, _data = data) {
// fetch's HeadersList is a 1D string array
const optsHeaders = Array.isArray(opts.headers)
? buildHeadersFromArray(opts.headers)
: opts.headers
const body = typeof _data === 'function'
? _data({ ...opts, headers: optsHeaders })
: _data
// util.types.isPromise is likely needed for jest.
if (isPromise(body)) {
// If handleReply is asynchronous, throwing an error
// in the callback will reject the promise, rather than
// synchronously throw the error, which breaks some tests.
// Rather, we wait for the callback to resolve if it is a
// promise, and then re-run handleReply with the new body.
body.then((newData) => handleReply(mockDispatches, newData))
return
}
const responseData = getResponseData(body)
const responseHeaders = generateKeyValues(headers)
const responseTrailers = generateKeyValues(trailers)
handler.abort = nop
handler.onHeaders(statusCode, responseHeaders, resume, getStatusText(statusCode))
handler.onData(Buffer.from(responseData))
handler.onComplete(responseTrailers)
deleteMockDispatch(mockDispatches, key)
}
function resume () {}
return true
}
function buildMockDispatch () {
const agent = this[kMockAgent]
const origin = this[kOrigin]
const originalDispatch = this[kOriginalDispatch]
return function dispatch (opts, handler) {
if (agent.isMockActive) {
try {
mockDispatch.call(this, opts, handler)
} catch (error) {
if (error instanceof MockNotMatchedError) {
const netConnect = agent[kGetNetConnect]()
if (netConnect === false) {
throw new MockNotMatchedError(`${error.message}: subsequent request to origin ${origin} was not allowed (net.connect disabled)`)
}
if (checkNetConnect(netConnect, origin)) {
originalDispatch.call(this, opts, handler)
} else {
throw new MockNotMatchedError(`${error.message}: subsequent request to origin ${origin} was not allowed (net.connect is not enabled for this origin)`)
}
} else {
throw error
}
}
} else {
originalDispatch.call(this, opts, handler)
}
}
}
function checkNetConnect (netConnect, origin) {
const url = new URL(origin)
if (netConnect === true) {
return true
} else if (Array.isArray(netConnect) && netConnect.some((matcher) => matchValue(matcher, url.host))) {
return true
}
return false
}
function buildMockOptions (opts) {
if (opts) {
const { agent, ...mockOptions } = opts
return mockOptions
}
}
module.exports = {
getResponseData,
getMockDispatch,
addMockDispatch,
deleteMockDispatch,
buildKey,
generateKeyValues,
matchValue,
getResponse,
getStatusText,
mockDispatch,
buildMockDispatch,
checkNetConnect,
buildMockOptions,
getHeaderByName
}

Some files were not shown because too many files have changed in this diff Show More