mirror of
https://github.com/oven-sh/setup-bun.git
synced 2025-02-23 10:40:10 +08:00
fix: use node fetch
This commit is contained in:
parent
8aad90088f
commit
6f1924d3c0
88
node_modules/data-uri-to-buffer/README.md
generated
vendored
Normal file
88
node_modules/data-uri-to-buffer/README.md
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
data-uri-to-buffer
|
||||
==================
|
||||
### Generate a Buffer instance from a [Data URI][rfc] string
|
||||
[](https://travis-ci.org/TooTallNate/node-data-uri-to-buffer)
|
||||
|
||||
This module accepts a ["data" URI][rfc] String of data, and returns a
|
||||
node.js `Buffer` instance with the decoded data.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install with `npm`:
|
||||
|
||||
``` bash
|
||||
$ npm install data-uri-to-buffer
|
||||
```
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
``` js
|
||||
var dataUriToBuffer = require('data-uri-to-buffer');
|
||||
|
||||
// plain-text data is supported
|
||||
var uri = 'data:,Hello%2C%20World!';
|
||||
var decoded = dataUriToBuffer(uri);
|
||||
console.log(decoded.toString());
|
||||
// 'Hello, World!'
|
||||
|
||||
// base64-encoded data is supported
|
||||
uri = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D';
|
||||
decoded = dataUriToBuffer(uri);
|
||||
console.log(decoded.toString());
|
||||
// 'Hello, World!'
|
||||
```
|
||||
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
### dataUriToBuffer(String uri) → Buffer
|
||||
|
||||
The `type` property on the Buffer instance gets set to the main type portion of
|
||||
the "mediatype" portion of the "data" URI, or defaults to `"text/plain"` if not
|
||||
specified.
|
||||
|
||||
The `typeFull` property on the Buffer instance gets set to the entire
|
||||
"mediatype" portion of the "data" URI (including all parameters), or defaults
|
||||
to `"text/plain;charset=US-ASCII"` if not specified.
|
||||
|
||||
The `charset` property on the Buffer instance gets set to the Charset portion of
|
||||
the "mediatype" portion of the "data" URI, or defaults to `"US-ASCII"` if the
|
||||
entire type is not specified, or defaults to `""` otherwise.
|
||||
|
||||
*Note*: If the only the main type is specified but not the charset, e.g.
|
||||
`"data:text/plain,abc"`, the charset is set to the empty string. The spec only
|
||||
defaults to US-ASCII as charset if the entire type is not specified.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014 Nathan Rajlich <nathan@tootallnate.net>
|
||||
|
||||
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.
|
||||
|
||||
[rfc]: http://tools.ietf.org/html/rfc2397
|
15
node_modules/data-uri-to-buffer/dist/index.d.ts
generated
vendored
Normal file
15
node_modules/data-uri-to-buffer/dist/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/// <reference types="node" />
|
||||
export interface MimeBuffer extends Buffer {
|
||||
type: string;
|
||||
typeFull: string;
|
||||
charset: string;
|
||||
}
|
||||
/**
|
||||
* Returns a `Buffer` instance from the given data URI `uri`.
|
||||
*
|
||||
* @param {String} uri Data URI to turn into a Buffer instance
|
||||
* @returns {Buffer} Buffer instance from Data URI
|
||||
* @api public
|
||||
*/
|
||||
export declare function dataUriToBuffer(uri: string): MimeBuffer;
|
||||
export default dataUriToBuffer;
|
53
node_modules/data-uri-to-buffer/dist/index.js
generated
vendored
Normal file
53
node_modules/data-uri-to-buffer/dist/index.js
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Returns a `Buffer` instance from the given data URI `uri`.
|
||||
*
|
||||
* @param {String} uri Data URI to turn into a Buffer instance
|
||||
* @returns {Buffer} Buffer instance from Data URI
|
||||
* @api public
|
||||
*/
|
||||
export function dataUriToBuffer(uri) {
|
||||
if (!/^data:/i.test(uri)) {
|
||||
throw new TypeError('`uri` does not appear to be a Data URI (must begin with "data:")');
|
||||
}
|
||||
// strip newlines
|
||||
uri = uri.replace(/\r?\n/g, '');
|
||||
// split the URI up into the "metadata" and the "data" portions
|
||||
const firstComma = uri.indexOf(',');
|
||||
if (firstComma === -1 || firstComma <= 4) {
|
||||
throw new TypeError('malformed data: URI');
|
||||
}
|
||||
// remove the "data:" scheme and parse the metadata
|
||||
const meta = uri.substring(5, firstComma).split(';');
|
||||
let charset = '';
|
||||
let base64 = false;
|
||||
const type = meta[0] || 'text/plain';
|
||||
let typeFull = type;
|
||||
for (let i = 1; i < meta.length; i++) {
|
||||
if (meta[i] === 'base64') {
|
||||
base64 = true;
|
||||
}
|
||||
else {
|
||||
typeFull += `;${meta[i]}`;
|
||||
if (meta[i].indexOf('charset=') === 0) {
|
||||
charset = meta[i].substring(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
// defaults to US-ASCII only if type is not provided
|
||||
if (!meta[0] && !charset.length) {
|
||||
typeFull += ';charset=US-ASCII';
|
||||
charset = 'US-ASCII';
|
||||
}
|
||||
// get the encoded data portion and decode URI-encoded chars
|
||||
const encoding = base64 ? 'base64' : 'ascii';
|
||||
const data = unescape(uri.substring(firstComma + 1));
|
||||
const buffer = Buffer.from(data, encoding);
|
||||
// set `.type` and `.typeFull` properties to MIME type
|
||||
buffer.type = type;
|
||||
buffer.typeFull = typeFull;
|
||||
// set the `.charset` property
|
||||
buffer.charset = charset;
|
||||
return buffer;
|
||||
}
|
||||
export default dataUriToBuffer;
|
||||
//# sourceMappingURL=index.js.map
|
1
node_modules/data-uri-to-buffer/dist/index.js.map
generated
vendored
Normal file
1
node_modules/data-uri-to-buffer/dist/index.js.map
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IAC1C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QACzB,MAAM,IAAI,SAAS,CAClB,kEAAkE,CAClE,CAAC;KACF;IAED,iBAAiB;IACjB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAEhC,+DAA+D;IAC/D,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,UAAU,KAAK,CAAC,CAAC,IAAI,UAAU,IAAI,CAAC,EAAE;QACzC,MAAM,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAC;KAC3C;IAED,mDAAmD;IACnD,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAErD,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC;IACrC,IAAI,QAAQ,GAAG,IAAI,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE;YACzB,MAAM,GAAG,IAAI,CAAC;SACd;aAAM;YACN,QAAQ,IAAI,IAAM,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;gBACtC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;aAC/B;SACD;KACD;IACD,oDAAoD;IACpD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;QAChC,QAAQ,IAAI,mBAAmB,CAAC;QAChC,OAAO,GAAG,UAAU,CAAC;KACrB;IAED,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;IAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAe,CAAC;IAEzD,sDAAsD;IACtD,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAE3B,8BAA8B;IAC9B,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IAEzB,OAAO,MAAM,CAAC;AACf,CAAC;AAED,eAAe,eAAe,CAAC"}
|
15
node_modules/data-uri-to-buffer/dist/src/index.d.ts
generated
vendored
Normal file
15
node_modules/data-uri-to-buffer/dist/src/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/// <reference types="node" />
|
||||
export interface MimeBuffer extends Buffer {
|
||||
type: string;
|
||||
typeFull: string;
|
||||
charset: string;
|
||||
}
|
||||
/**
|
||||
* Returns a `Buffer` instance from the given data URI `uri`.
|
||||
*
|
||||
* @param {String} uri Data URI to turn into a Buffer instance
|
||||
* @returns {Buffer} Buffer instance from Data URI
|
||||
* @api public
|
||||
*/
|
||||
export declare function dataUriToBuffer(uri: string): MimeBuffer;
|
||||
export default dataUriToBuffer;
|
53
node_modules/data-uri-to-buffer/dist/src/index.js
generated
vendored
Normal file
53
node_modules/data-uri-to-buffer/dist/src/index.js
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Returns a `Buffer` instance from the given data URI `uri`.
|
||||
*
|
||||
* @param {String} uri Data URI to turn into a Buffer instance
|
||||
* @returns {Buffer} Buffer instance from Data URI
|
||||
* @api public
|
||||
*/
|
||||
export function dataUriToBuffer(uri) {
|
||||
if (!/^data:/i.test(uri)) {
|
||||
throw new TypeError('`uri` does not appear to be a Data URI (must begin with "data:")');
|
||||
}
|
||||
// strip newlines
|
||||
uri = uri.replace(/\r?\n/g, '');
|
||||
// split the URI up into the "metadata" and the "data" portions
|
||||
const firstComma = uri.indexOf(',');
|
||||
if (firstComma === -1 || firstComma <= 4) {
|
||||
throw new TypeError('malformed data: URI');
|
||||
}
|
||||
// remove the "data:" scheme and parse the metadata
|
||||
const meta = uri.substring(5, firstComma).split(';');
|
||||
let charset = '';
|
||||
let base64 = false;
|
||||
const type = meta[0] || 'text/plain';
|
||||
let typeFull = type;
|
||||
for (let i = 1; i < meta.length; i++) {
|
||||
if (meta[i] === 'base64') {
|
||||
base64 = true;
|
||||
}
|
||||
else {
|
||||
typeFull += `;${meta[i]}`;
|
||||
if (meta[i].indexOf('charset=') === 0) {
|
||||
charset = meta[i].substring(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
// defaults to US-ASCII only if type is not provided
|
||||
if (!meta[0] && !charset.length) {
|
||||
typeFull += ';charset=US-ASCII';
|
||||
charset = 'US-ASCII';
|
||||
}
|
||||
// get the encoded data portion and decode URI-encoded chars
|
||||
const encoding = base64 ? 'base64' : 'ascii';
|
||||
const data = unescape(uri.substring(firstComma + 1));
|
||||
const buffer = Buffer.from(data, encoding);
|
||||
// set `.type` and `.typeFull` properties to MIME type
|
||||
buffer.type = type;
|
||||
buffer.typeFull = typeFull;
|
||||
// set the `.charset` property
|
||||
buffer.charset = charset;
|
||||
return buffer;
|
||||
}
|
||||
export default dataUriToBuffer;
|
||||
//# sourceMappingURL=index.js.map
|
1
node_modules/data-uri-to-buffer/dist/src/index.js.map
generated
vendored
Normal file
1
node_modules/data-uri-to-buffer/dist/src/index.js.map
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAMA;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IAC1C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QACzB,MAAM,IAAI,SAAS,CAClB,kEAAkE,CAClE,CAAC;KACF;IAED,iBAAiB;IACjB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAEhC,+DAA+D;IAC/D,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,UAAU,KAAK,CAAC,CAAC,IAAI,UAAU,IAAI,CAAC,EAAE;QACzC,MAAM,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAC;KAC3C;IAED,mDAAmD;IACnD,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAErD,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC;IACrC,IAAI,QAAQ,GAAG,IAAI,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE;YACzB,MAAM,GAAG,IAAI,CAAC;SACd;aAAM;YACN,QAAQ,IAAI,IAAM,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;gBACtC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;aAC/B;SACD;KACD;IACD,oDAAoD;IACpD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;QAChC,QAAQ,IAAI,mBAAmB,CAAC;QAChC,OAAO,GAAG,UAAU,CAAC;KACrB;IAED,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;IAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAe,CAAC;IAEzD,sDAAsD;IACtD,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAE3B,8BAA8B;IAC9B,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IAEzB,OAAO,MAAM,CAAC;AACf,CAAC;AAED,eAAe,eAAe,CAAC"}
|
4
node_modules/data-uri-to-buffer/dist/test/test.d.ts
generated
vendored
Normal file
4
node_modules/data-uri-to-buffer/dist/test/test.d.ts
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
export {};
|
144
node_modules/data-uri-to-buffer/dist/test/test.js
generated
vendored
Normal file
144
node_modules/data-uri-to-buffer/dist/test/test.js
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
import assert from 'assert';
|
||||
import dataUriToBuffer from '../src';
|
||||
describe('data-uri-to-buffer', function () {
|
||||
it('should decode bare-bones Data URIs', function () {
|
||||
var uri = 'data:,Hello%2C%20World!';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('text/plain;charset=US-ASCII', buf.typeFull);
|
||||
assert.equal('US-ASCII', buf.charset);
|
||||
assert.equal('Hello, World!', buf.toString());
|
||||
});
|
||||
it('should decode bare-bones "base64" Data URIs', function () {
|
||||
var uri = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('Hello, World!', buf.toString());
|
||||
});
|
||||
it('should decode plain-text Data URIs', function () {
|
||||
var html = '<!DOCTYPE html>' +
|
||||
'<html lang="en">' +
|
||||
'<head><title>Embedded Window</title></head>' +
|
||||
'<body><h1>42</h1></body>' +
|
||||
'</html>';
|
||||
// Escape the HTML for URL formatting
|
||||
var uri = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/html', buf.type);
|
||||
assert.equal('utf-8', buf.charset);
|
||||
assert.equal(html, buf.toString());
|
||||
});
|
||||
// the next 4 tests are from:
|
||||
// https://bug161965.bugzilla.mozilla.org/attachment.cgi?id=94670&action=view
|
||||
it('should decode "ISO-8859-8 in Base64" URIs', function () {
|
||||
var uri = 'data:text/plain;charset=iso-8859-8-i;base64,+ezl7Q==';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('iso-8859-8-i', buf.charset);
|
||||
assert.equal(4, buf.length);
|
||||
assert.equal(0xf9, buf[0]);
|
||||
assert.equal(0xec, buf[1]);
|
||||
assert.equal(0xe5, buf[2]);
|
||||
assert.equal(0xed, buf[3]);
|
||||
});
|
||||
it('should decode "ISO-8859-8 in URL-encoding" URIs', function () {
|
||||
var uri = 'data:text/plain;charset=iso-8859-8-i,%f9%ec%e5%ed';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('iso-8859-8-i', buf.charset);
|
||||
assert.equal(4, buf.length);
|
||||
assert.equal(0xf9, buf[0]);
|
||||
assert.equal(0xec, buf[1]);
|
||||
assert.equal(0xe5, buf[2]);
|
||||
assert.equal(0xed, buf[3]);
|
||||
});
|
||||
it('should decode "UTF-8 in Base64" URIs', function () {
|
||||
var uri = 'data:text/plain;charset=UTF-8;base64,16nXnNeV150=';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('UTF-8', buf.charset);
|
||||
assert.equal(8, buf.length);
|
||||
assert.equal('שלום', buf.toString('utf8'));
|
||||
});
|
||||
it('should decode "UTF-8 in URL-encoding" URIs', function () {
|
||||
var uri = 'data:text/plain;charset=UTF-8,%d7%a9%d7%9c%d7%95%d7%9d';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('UTF-8', buf.charset);
|
||||
assert.equal(8, buf.length);
|
||||
assert.equal('שלום', buf.toString('utf8'));
|
||||
});
|
||||
// this next one is from Wikipedia IIRC
|
||||
it('should decode "base64" Data URIs with newlines', function () {
|
||||
var uri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA\n' +
|
||||
'AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO\n' +
|
||||
'9TXL0Y4OHwAAAABJRU5ErkJggg==';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('image/png', buf.type);
|
||||
assert.equal('iVBORw0KGgoAAAANSUhEUgAAAAUA' +
|
||||
'AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO' +
|
||||
'9TXL0Y4OHwAAAABJRU5ErkJggg==', buf.toString('base64'));
|
||||
});
|
||||
it('should decode a plain-text URI with a space character in it', function () {
|
||||
var uri = 'data:,foo bar';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('foo bar', buf.toString());
|
||||
});
|
||||
it('should decode data with a "," comma char', function () {
|
||||
var uri = 'data:,a,b';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('a,b', buf.toString());
|
||||
});
|
||||
it('should decode data with traditionally reserved characters like ";"', function () {
|
||||
var uri = 'data:,;test';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal(';test', buf.toString());
|
||||
});
|
||||
it('should not default to US-ASCII if main type is provided', function () {
|
||||
var uri = 'data:text/plain,abc';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('text/plain', buf.typeFull);
|
||||
assert.equal('', buf.charset);
|
||||
assert.equal('abc', buf.toString());
|
||||
});
|
||||
it('should default to text/plain if main type is not provided', function () {
|
||||
var uri = 'data:;charset=UTF-8,abc';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('text/plain;charset=UTF-8', buf.typeFull);
|
||||
assert.equal('UTF-8', buf.charset);
|
||||
assert.equal('abc', buf.toString());
|
||||
});
|
||||
it('should not allow charset without a leading ;', function () {
|
||||
var uri = 'data:charset=UTF-8,abc';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('charset=UTF-8', buf.type);
|
||||
assert.equal('charset=UTF-8', buf.typeFull);
|
||||
assert.equal('', buf.charset);
|
||||
assert.equal('abc', buf.toString());
|
||||
});
|
||||
it('should allow custom media type parameters', function () {
|
||||
var uri = 'data:application/javascript;version=1.8;charset=UTF-8,abc';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('application/javascript', buf.type);
|
||||
assert.equal('application/javascript;version=1.8;charset=UTF-8', buf.typeFull);
|
||||
assert.equal('UTF-8', buf.charset);
|
||||
assert.equal('abc', buf.toString());
|
||||
});
|
||||
it('should allow base64 annotation anywhere', function () {
|
||||
var uri = 'data:text/plain;base64;charset=UTF-8,YWJj';
|
||||
var buf = dataUriToBuffer(uri);
|
||||
assert.equal('text/plain', buf.type);
|
||||
assert.equal('text/plain;charset=UTF-8', buf.typeFull);
|
||||
assert.equal('UTF-8', buf.charset);
|
||||
assert.equal('abc', buf.toString());
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=test.js.map
|
1
node_modules/data-uri-to-buffer/dist/test/test.js.map
generated
vendored
Normal file
1
node_modules/data-uri-to-buffer/dist/test/test.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
62
node_modules/data-uri-to-buffer/package.json
generated
vendored
Normal file
62
node_modules/data-uri-to-buffer/package.json
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "data-uri-to-buffer",
|
||||
"version": "4.0.0",
|
||||
"description": "Generate a Buffer instance from a Data URI string",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "jest",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/TooTallNate/node-data-uri-to-buffer.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
},
|
||||
"keywords": [
|
||||
"data",
|
||||
"uri",
|
||||
"datauri",
|
||||
"data-uri",
|
||||
"buffer",
|
||||
"convert",
|
||||
"rfc2397",
|
||||
"2397"
|
||||
],
|
||||
"author": "Nathan Rajlich <nathan@tootallnate.net> (http://n8.io/)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/TooTallNate/node-data-uri-to-buffer/issues"
|
||||
},
|
||||
"homepage": "https://github.com/TooTallNate/node-data-uri-to-buffer",
|
||||
"devDependencies": {
|
||||
"@types/es6-promisify": "^5.0.0",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^10.5.3",
|
||||
"jest": "^27.2.2",
|
||||
"ts-jest": "^27.0.5",
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest",
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"diagnostics": false,
|
||||
"isolatedModules": true
|
||||
}
|
||||
},
|
||||
"verbose": false,
|
||||
"testEnvironment": "node",
|
||||
"testMatch": [
|
||||
"<rootDir>/test/**/*.test.ts"
|
||||
]
|
||||
}
|
||||
}
|
68
node_modules/data-uri-to-buffer/src/index.ts
generated
vendored
Normal file
68
node_modules/data-uri-to-buffer/src/index.ts
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
export interface MimeBuffer extends Buffer {
|
||||
type: string;
|
||||
typeFull: string;
|
||||
charset: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a `Buffer` instance from the given data URI `uri`.
|
||||
*
|
||||
* @param {String} uri Data URI to turn into a Buffer instance
|
||||
* @returns {Buffer} Buffer instance from Data URI
|
||||
* @api public
|
||||
*/
|
||||
export function dataUriToBuffer(uri: string): MimeBuffer {
|
||||
if (!/^data:/i.test(uri)) {
|
||||
throw new TypeError(
|
||||
'`uri` does not appear to be a Data URI (must begin with "data:")'
|
||||
);
|
||||
}
|
||||
|
||||
// strip newlines
|
||||
uri = uri.replace(/\r?\n/g, '');
|
||||
|
||||
// split the URI up into the "metadata" and the "data" portions
|
||||
const firstComma = uri.indexOf(',');
|
||||
if (firstComma === -1 || firstComma <= 4) {
|
||||
throw new TypeError('malformed data: URI');
|
||||
}
|
||||
|
||||
// remove the "data:" scheme and parse the metadata
|
||||
const meta = uri.substring(5, firstComma).split(';');
|
||||
|
||||
let charset = '';
|
||||
let base64 = false;
|
||||
const type = meta[0] || 'text/plain';
|
||||
let typeFull = type;
|
||||
for (let i = 1; i < meta.length; i++) {
|
||||
if (meta[i] === 'base64') {
|
||||
base64 = true;
|
||||
} else {
|
||||
typeFull += `;${ meta[i]}`;
|
||||
if (meta[i].indexOf('charset=') === 0) {
|
||||
charset = meta[i].substring(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
// defaults to US-ASCII only if type is not provided
|
||||
if (!meta[0] && !charset.length) {
|
||||
typeFull += ';charset=US-ASCII';
|
||||
charset = 'US-ASCII';
|
||||
}
|
||||
|
||||
// get the encoded data portion and decode URI-encoded chars
|
||||
const encoding = base64 ? 'base64' : 'ascii';
|
||||
const data = unescape(uri.substring(firstComma + 1));
|
||||
const buffer = Buffer.from(data, encoding) as MimeBuffer;
|
||||
|
||||
// set `.type` and `.typeFull` properties to MIME type
|
||||
buffer.type = type;
|
||||
buffer.typeFull = typeFull;
|
||||
|
||||
// set the `.charset` property
|
||||
buffer.charset = charset;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export default dataUriToBuffer;
|
2
node_modules/undici/lib/fetch/LICENSE → node_modules/fetch-blob/LICENSE
generated
vendored
2
node_modules/undici/lib/fetch/LICENSE → node_modules/fetch-blob/LICENSE
generated
vendored
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Ethan Arrowood
|
||||
Copyright (c) 2019 David Frank
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
106
node_modules/fetch-blob/README.md
generated
vendored
Normal file
106
node_modules/fetch-blob/README.md
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
# fetch-blob
|
||||
|
||||
[![npm version][npm-image]][npm-url]
|
||||
[![build status][ci-image]][ci-url]
|
||||
[![coverage status][codecov-image]][codecov-url]
|
||||
[![install size][install-size-image]][install-size-url]
|
||||
|
||||
A Blob implementation in Node.js, originally from [node-fetch](https://github.com/node-fetch/node-fetch).
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
npm install fetch-blob
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Upgrading from 2x to 3x</summary>
|
||||
|
||||
Updating from 2 to 3 should be a breeze since there is not many changes to the blob specification.
|
||||
The major cause of a major release is coding standards.
|
||||
- internal WeakMaps was replaced with private fields
|
||||
- internal Buffer.from was replaced with TextEncoder/Decoder
|
||||
- internal buffers was replaced with Uint8Arrays
|
||||
- CommonJS was replaced with ESM
|
||||
- The node stream returned by calling `blob.stream()` was replaced with whatwg streams
|
||||
- (Read "Differences from other blobs" for more info.)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Differences from other Blobs</summary>
|
||||
|
||||
- Unlike NodeJS `buffer.Blob` (Added in: v15.7.0) and browser native Blob this polyfilled version can't be sent via PostMessage
|
||||
- This blob version is more arbitrary, it can be constructed with blob parts that isn't a instance of itself
|
||||
it has to look and behave as a blob to be accepted as a blob part.
|
||||
- The benefit of this is that you can create other types of blobs that don't contain any internal data that has to be read in other ways, such as the `BlobDataItem` created in `from.js` that wraps a file path into a blob-like item and read lazily (nodejs plans to [implement this][fs-blobs] as well)
|
||||
- The `blob.stream()` is the most noticeable differences. It returns a WHATWG stream now. to keep it as a node stream you would have to do:
|
||||
|
||||
```js
|
||||
import {Readable} from 'stream'
|
||||
const stream = Readable.from(blob.stream())
|
||||
```
|
||||
</details>
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
// Ways to import
|
||||
// (PS it's dependency free ESM package so regular http-import from CDN works too)
|
||||
import Blob from 'fetch-blob'
|
||||
import File from 'fetch-blob/file.js'
|
||||
|
||||
import {Blob} from 'fetch-blob'
|
||||
import {File} from 'fetch-blob/file.js'
|
||||
|
||||
const {Blob} = await import('fetch-blob')
|
||||
|
||||
|
||||
// Ways to read the blob:
|
||||
const blob = new Blob(['hello, world'])
|
||||
|
||||
await blob.text()
|
||||
await blob.arrayBuffer()
|
||||
for await (let chunk of blob.stream()) { ... }
|
||||
blob.stream().getReader().read()
|
||||
blob.stream().getReader({mode: 'byob'}).read(view)
|
||||
```
|
||||
|
||||
### Blob part backed up by filesystem
|
||||
|
||||
`fetch-blob/from.js` comes packed with tools to convert any filepath into either a Blob or a File
|
||||
It will not read the content into memory. It will only stat the file for last modified date and file size.
|
||||
|
||||
```js
|
||||
// The default export is sync and use fs.stat to retrieve size & last modified as a blob
|
||||
import blobFromSync from 'fetch-blob/from.js'
|
||||
import {File, Blob, blobFrom, blobFromSync, fileFrom, fileFromSync} from 'fetch-blob/from.js'
|
||||
|
||||
const fsFile = fileFromSync('./2-GiB-file.bin', 'application/octet-stream')
|
||||
const fsBlob = await blobFrom('./2-GiB-file.mp4')
|
||||
|
||||
// Not a 4 GiB memory snapshot, just holds references
|
||||
// points to where data is located on the disk
|
||||
const blob = new Blob([fsFile, fsBlob, 'memory', new Uint8Array(10)])
|
||||
console.log(blob.size) // ~4 GiB
|
||||
```
|
||||
|
||||
`blobFrom|blobFromSync|fileFrom|fileFromSync(path, [mimetype])`
|
||||
|
||||
### Creating Blobs backed up by other async sources
|
||||
Our Blob & File class are more generic then any other polyfills in the way that it can accept any blob look-a-like item
|
||||
An example of this is that our blob implementation can be constructed with parts coming from [BlobDataItem](https://github.com/node-fetch/fetch-blob/blob/8ef89adad40d255a3bbd55cf38b88597c1cd5480/from.js#L32) (aka a filepath) or from [buffer.Blob](https://nodejs.org/api/buffer.html#buffer_new_buffer_blob_sources_options), It dose not have to implement all the methods - just enough that it can be read/understood by our Blob implementation. The minium requirements is that it has `Symbol.toStringTag`, `size`, `slice()` and either a `stream()` or a `arrayBuffer()` method. If you then wrap it in our Blob or File `new Blob([blobDataItem])` then you get all of the other methods that should be implemented in a blob or file
|
||||
|
||||
An example of this could be to create a file or blob like item coming from a remote HTTP request. Or from a DataBase
|
||||
|
||||
See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [tests](https://github.com/node-fetch/fetch-blob/blob/master/test.js) for more details of how to use the Blob.
|
||||
|
||||
[npm-image]: https://flat.badgen.net/npm/v/fetch-blob
|
||||
[npm-url]: https://www.npmjs.com/package/fetch-blob
|
||||
[ci-image]: https://github.com/node-fetch/fetch-blob/workflows/CI/badge.svg
|
||||
[ci-url]: https://github.com/node-fetch/fetch-blob/actions
|
||||
[codecov-image]: https://flat.badgen.net/codecov/c/github/node-fetch/fetch-blob/master
|
||||
[codecov-url]: https://codecov.io/gh/node-fetch/fetch-blob
|
||||
[install-size-image]: https://flat.badgen.net/packagephobia/install/fetch-blob
|
||||
[install-size-url]: https://packagephobia.now.sh/result?p=fetch-blob
|
||||
[fs-blobs]: https://github.com/nodejs/node/issues/37340
|
2
node_modules/fetch-blob/file.d.ts
generated
vendored
Normal file
2
node_modules/fetch-blob/file.d.ts
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/** @type {typeof globalThis.File} */ export const File: typeof globalThis.File;
|
||||
export default File;
|
49
node_modules/fetch-blob/file.js
generated
vendored
Normal file
49
node_modules/fetch-blob/file.js
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
import Blob from './index.js'
|
||||
|
||||
const _File = class File extends Blob {
|
||||
#lastModified = 0
|
||||
#name = ''
|
||||
|
||||
/**
|
||||
* @param {*[]} fileBits
|
||||
* @param {string} fileName
|
||||
* @param {{lastModified?: number, type?: string}} options
|
||||
*/// @ts-ignore
|
||||
constructor (fileBits, fileName, options = {}) {
|
||||
if (arguments.length < 2) {
|
||||
throw new TypeError(`Failed to construct 'File': 2 arguments required, but only ${arguments.length} present.`)
|
||||
}
|
||||
super(fileBits, options)
|
||||
|
||||
if (options === null) options = {}
|
||||
|
||||
// Simulate WebIDL type casting for NaN value in lastModified option.
|
||||
const lastModified = options.lastModified === undefined ? Date.now() : Number(options.lastModified)
|
||||
if (!Number.isNaN(lastModified)) {
|
||||
this.#lastModified = lastModified
|
||||
}
|
||||
|
||||
this.#name = String(fileName)
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this.#name
|
||||
}
|
||||
|
||||
get lastModified () {
|
||||
return this.#lastModified
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag] () {
|
||||
return 'File'
|
||||
}
|
||||
|
||||
static [Symbol.hasInstance] (object) {
|
||||
return !!object && object instanceof Blob &&
|
||||
/^(File)$/.test(object[Symbol.toStringTag])
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {typeof globalThis.File} */// @ts-ignore
|
||||
export const File = _File
|
||||
export default File
|
26
node_modules/fetch-blob/from.d.ts
generated
vendored
Normal file
26
node_modules/fetch-blob/from.d.ts
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
export default blobFromSync;
|
||||
/**
|
||||
* @param {string} path filepath on the disk
|
||||
* @param {string} [type] mimetype to use
|
||||
*/
|
||||
export function blobFromSync(path: string, type?: string): Blob;
|
||||
import File from "./file.js";
|
||||
import Blob from "./index.js";
|
||||
/**
|
||||
* @param {string} path filepath on the disk
|
||||
* @param {string} [type] mimetype to use
|
||||
* @returns {Promise<Blob>}
|
||||
*/
|
||||
export function blobFrom(path: string, type?: string): Promise<Blob>;
|
||||
/**
|
||||
* @param {string} path filepath on the disk
|
||||
* @param {string} [type] mimetype to use
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
export function fileFrom(path: string, type?: string): Promise<File>;
|
||||
/**
|
||||
* @param {string} path filepath on the disk
|
||||
* @param {string} [type] mimetype to use
|
||||
*/
|
||||
export function fileFromSync(path: string, type?: string): File;
|
||||
export { File, Blob };
|
100
node_modules/fetch-blob/from.js
generated
vendored
Normal file
100
node_modules/fetch-blob/from.js
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
import { statSync, createReadStream, promises as fs } from 'node:fs'
|
||||
import { basename } from 'node:path'
|
||||
import DOMException from 'node-domexception'
|
||||
|
||||
import File from './file.js'
|
||||
import Blob from './index.js'
|
||||
|
||||
const { stat } = fs
|
||||
|
||||
/**
|
||||
* @param {string} path filepath on the disk
|
||||
* @param {string} [type] mimetype to use
|
||||
*/
|
||||
const blobFromSync = (path, type) => fromBlob(statSync(path), path, type)
|
||||
|
||||
/**
|
||||
* @param {string} path filepath on the disk
|
||||
* @param {string} [type] mimetype to use
|
||||
* @returns {Promise<Blob>}
|
||||
*/
|
||||
const blobFrom = (path, type) => stat(path).then(stat => fromBlob(stat, path, type))
|
||||
|
||||
/**
|
||||
* @param {string} path filepath on the disk
|
||||
* @param {string} [type] mimetype to use
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
const fileFrom = (path, type) => stat(path).then(stat => fromFile(stat, path, type))
|
||||
|
||||
/**
|
||||
* @param {string} path filepath on the disk
|
||||
* @param {string} [type] mimetype to use
|
||||
*/
|
||||
const fileFromSync = (path, type) => fromFile(statSync(path), path, type)
|
||||
|
||||
// @ts-ignore
|
||||
const fromBlob = (stat, path, type = '') => new Blob([new BlobDataItem({
|
||||
path,
|
||||
size: stat.size,
|
||||
lastModified: stat.mtimeMs,
|
||||
start: 0
|
||||
})], { type })
|
||||
|
||||
// @ts-ignore
|
||||
const fromFile = (stat, path, type = '') => new File([new BlobDataItem({
|
||||
path,
|
||||
size: stat.size,
|
||||
lastModified: stat.mtimeMs,
|
||||
start: 0
|
||||
})], basename(path), { type, lastModified: stat.mtimeMs })
|
||||
|
||||
/**
|
||||
* This is a blob backed up by a file on the disk
|
||||
* with minium requirement. Its wrapped around a Blob as a blobPart
|
||||
* so you have no direct access to this.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
class BlobDataItem {
|
||||
#path
|
||||
#start
|
||||
|
||||
constructor (options) {
|
||||
this.#path = options.path
|
||||
this.#start = options.start
|
||||
this.size = options.size
|
||||
this.lastModified = options.lastModified
|
||||
}
|
||||
|
||||
/**
|
||||
* Slicing arguments is first validated and formatted
|
||||
* to not be out of range by Blob.prototype.slice
|
||||
*/
|
||||
slice (start, end) {
|
||||
return new BlobDataItem({
|
||||
path: this.#path,
|
||||
lastModified: this.lastModified,
|
||||
size: end - start,
|
||||
start: this.#start + start
|
||||
})
|
||||
}
|
||||
|
||||
async * stream () {
|
||||
const { mtimeMs } = await stat(this.#path)
|
||||
if (mtimeMs > this.lastModified) {
|
||||
throw new DOMException('The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.', 'NotReadableError')
|
||||
}
|
||||
yield * createReadStream(this.#path, {
|
||||
start: this.#start,
|
||||
end: this.#start + this.size - 1
|
||||
})
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag] () {
|
||||
return 'Blob'
|
||||
}
|
||||
}
|
||||
|
||||
export default blobFromSync
|
||||
export { File, Blob, blobFrom, blobFromSync, fileFrom, fileFromSync }
|
3
node_modules/fetch-blob/index.d.ts
generated
vendored
Normal file
3
node_modules/fetch-blob/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/** @type {typeof globalThis.Blob} */
|
||||
export const Blob: typeof globalThis.Blob;
|
||||
export default Blob;
|
250
node_modules/fetch-blob/index.js
generated
vendored
Normal file
250
node_modules/fetch-blob/index.js
generated
vendored
Normal file
@ -0,0 +1,250 @@
|
||||
/*! fetch-blob. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
// TODO (jimmywarting): in the feature use conditional loading with top level await (requires 14.x)
|
||||
// Node has recently added whatwg stream into core
|
||||
|
||||
import './streams.cjs'
|
||||
|
||||
// 64 KiB (same size chrome slice theirs blob into Uint8array's)
|
||||
const POOL_SIZE = 65536
|
||||
|
||||
/** @param {(Blob | Uint8Array)[]} parts */
|
||||
async function * toIterator (parts, clone = true) {
|
||||
for (const part of parts) {
|
||||
if ('stream' in part) {
|
||||
yield * (/** @type {AsyncIterableIterator<Uint8Array>} */ (part.stream()))
|
||||
} else if (ArrayBuffer.isView(part)) {
|
||||
if (clone) {
|
||||
let position = part.byteOffset
|
||||
const end = part.byteOffset + part.byteLength
|
||||
while (position !== end) {
|
||||
const size = Math.min(end - position, POOL_SIZE)
|
||||
const chunk = part.buffer.slice(position, position + size)
|
||||
position += chunk.byteLength
|
||||
yield new Uint8Array(chunk)
|
||||
}
|
||||
} else {
|
||||
yield part
|
||||
}
|
||||
/* c8 ignore next 10 */
|
||||
} else {
|
||||
// For blobs that have arrayBuffer but no stream method (nodes buffer.Blob)
|
||||
let position = 0, b = (/** @type {Blob} */ (part))
|
||||
while (position !== b.size) {
|
||||
const chunk = b.slice(position, Math.min(b.size, position + POOL_SIZE))
|
||||
const buffer = await chunk.arrayBuffer()
|
||||
position += buffer.byteLength
|
||||
yield new Uint8Array(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _Blob = class Blob {
|
||||
/** @type {Array.<(Blob|Uint8Array)>} */
|
||||
#parts = []
|
||||
#type = ''
|
||||
#size = 0
|
||||
#endings = 'transparent'
|
||||
|
||||
/**
|
||||
* The Blob() constructor returns a new Blob object. The content
|
||||
* of the blob consists of the concatenation of the values given
|
||||
* in the parameter array.
|
||||
*
|
||||
* @param {*} blobParts
|
||||
* @param {{ type?: string, endings?: string }} [options]
|
||||
*/
|
||||
constructor (blobParts = [], options = {}) {
|
||||
if (typeof blobParts !== 'object' || blobParts === null) {
|
||||
throw new TypeError('Failed to construct \'Blob\': The provided value cannot be converted to a sequence.')
|
||||
}
|
||||
|
||||
if (typeof blobParts[Symbol.iterator] !== 'function') {
|
||||
throw new TypeError('Failed to construct \'Blob\': The object must have a callable @@iterator property.')
|
||||
}
|
||||
|
||||
if (typeof options !== 'object' && typeof options !== 'function') {
|
||||
throw new TypeError('Failed to construct \'Blob\': parameter 2 cannot convert to dictionary.')
|
||||
}
|
||||
|
||||
if (options === null) options = {}
|
||||
|
||||
const encoder = new TextEncoder()
|
||||
for (const element of blobParts) {
|
||||
let part
|
||||
if (ArrayBuffer.isView(element)) {
|
||||
part = new Uint8Array(element.buffer.slice(element.byteOffset, element.byteOffset + element.byteLength))
|
||||
} else if (element instanceof ArrayBuffer) {
|
||||
part = new Uint8Array(element.slice(0))
|
||||
} else if (element instanceof Blob) {
|
||||
part = element
|
||||
} else {
|
||||
part = encoder.encode(`${element}`)
|
||||
}
|
||||
|
||||
this.#size += ArrayBuffer.isView(part) ? part.byteLength : part.size
|
||||
this.#parts.push(part)
|
||||
}
|
||||
|
||||
this.#endings = `${options.endings === undefined ? 'transparent' : options.endings}`
|
||||
const type = options.type === undefined ? '' : String(options.type)
|
||||
this.#type = /^[\x20-\x7E]*$/.test(type) ? type : ''
|
||||
}
|
||||
|
||||
/**
|
||||
* The Blob interface's size property returns the
|
||||
* size of the Blob in bytes.
|
||||
*/
|
||||
get size () {
|
||||
return this.#size
|
||||
}
|
||||
|
||||
/**
|
||||
* The type property of a Blob object returns the MIME type of the file.
|
||||
*/
|
||||
get type () {
|
||||
return this.#type
|
||||
}
|
||||
|
||||
/**
|
||||
* The text() method in the Blob interface returns a Promise
|
||||
* that resolves with a string containing the contents of
|
||||
* the blob, interpreted as UTF-8.
|
||||
*
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
async text () {
|
||||
// More optimized than using this.arrayBuffer()
|
||||
// that requires twice as much ram
|
||||
const decoder = new TextDecoder()
|
||||
let str = ''
|
||||
for await (const part of toIterator(this.#parts, false)) {
|
||||
str += decoder.decode(part, { stream: true })
|
||||
}
|
||||
// Remaining
|
||||
str += decoder.decode()
|
||||
return str
|
||||
}
|
||||
|
||||
/**
|
||||
* The arrayBuffer() method in the Blob interface returns a
|
||||
* Promise that resolves with the contents of the blob as
|
||||
* binary data contained in an ArrayBuffer.
|
||||
*
|
||||
* @return {Promise<ArrayBuffer>}
|
||||
*/
|
||||
async arrayBuffer () {
|
||||
// Easier way... Just a unnecessary overhead
|
||||
// const view = new Uint8Array(this.size);
|
||||
// await this.stream().getReader({mode: 'byob'}).read(view);
|
||||
// return view.buffer;
|
||||
|
||||
const data = new Uint8Array(this.size)
|
||||
let offset = 0
|
||||
for await (const chunk of toIterator(this.#parts, false)) {
|
||||
data.set(chunk, offset)
|
||||
offset += chunk.length
|
||||
}
|
||||
|
||||
return data.buffer
|
||||
}
|
||||
|
||||
stream () {
|
||||
const it = toIterator(this.#parts, true)
|
||||
|
||||
return new globalThis.ReadableStream({
|
||||
// @ts-ignore
|
||||
type: 'bytes',
|
||||
async pull (ctrl) {
|
||||
const chunk = await it.next()
|
||||
chunk.done ? ctrl.close() : ctrl.enqueue(chunk.value)
|
||||
},
|
||||
|
||||
async cancel () {
|
||||
await it.return()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* The Blob interface's slice() method creates and returns a
|
||||
* new Blob object which contains data from a subset of the
|
||||
* blob on which it's called.
|
||||
*
|
||||
* @param {number} [start]
|
||||
* @param {number} [end]
|
||||
* @param {string} [type]
|
||||
*/
|
||||
slice (start = 0, end = this.size, type = '') {
|
||||
const { size } = this
|
||||
|
||||
let relativeStart = start < 0 ? Math.max(size + start, 0) : Math.min(start, size)
|
||||
let relativeEnd = end < 0 ? Math.max(size + end, 0) : Math.min(end, size)
|
||||
|
||||
const span = Math.max(relativeEnd - relativeStart, 0)
|
||||
const parts = this.#parts
|
||||
const blobParts = []
|
||||
let added = 0
|
||||
|
||||
for (const part of parts) {
|
||||
// don't add the overflow to new blobParts
|
||||
if (added >= span) {
|
||||
break
|
||||
}
|
||||
|
||||
const size = ArrayBuffer.isView(part) ? part.byteLength : part.size
|
||||
if (relativeStart && size <= relativeStart) {
|
||||
// Skip the beginning and change the relative
|
||||
// start & end position as we skip the unwanted parts
|
||||
relativeStart -= size
|
||||
relativeEnd -= size
|
||||
} else {
|
||||
let chunk
|
||||
if (ArrayBuffer.isView(part)) {
|
||||
chunk = part.subarray(relativeStart, Math.min(size, relativeEnd))
|
||||
added += chunk.byteLength
|
||||
} else {
|
||||
chunk = part.slice(relativeStart, Math.min(size, relativeEnd))
|
||||
added += chunk.size
|
||||
}
|
||||
relativeEnd -= size
|
||||
blobParts.push(chunk)
|
||||
relativeStart = 0 // All next sequential parts should start at 0
|
||||
}
|
||||
}
|
||||
|
||||
const blob = new Blob([], { type: String(type).toLowerCase() })
|
||||
blob.#size = span
|
||||
blob.#parts = blobParts
|
||||
|
||||
return blob
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag] () {
|
||||
return 'Blob'
|
||||
}
|
||||
|
||||
static [Symbol.hasInstance] (object) {
|
||||
return (
|
||||
object &&
|
||||
typeof object === 'object' &&
|
||||
typeof object.constructor === 'function' &&
|
||||
(
|
||||
typeof object.stream === 'function' ||
|
||||
typeof object.arrayBuffer === 'function'
|
||||
) &&
|
||||
/^(Blob|File)$/.test(object[Symbol.toStringTag])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperties(_Blob.prototype, {
|
||||
size: { enumerable: true },
|
||||
type: { enumerable: true },
|
||||
slice: { enumerable: true }
|
||||
})
|
||||
|
||||
/** @type {typeof globalThis.Blob} */
|
||||
export const Blob = _Blob
|
||||
export default Blob
|
56
node_modules/fetch-blob/package.json
generated
vendored
Normal file
56
node_modules/fetch-blob/package.json
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "fetch-blob",
|
||||
"version": "3.2.0",
|
||||
"description": "Blob & File implementation in Node.js, originally from node-fetch.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"from.js",
|
||||
"file.js",
|
||||
"file.d.ts",
|
||||
"index.js",
|
||||
"index.d.ts",
|
||||
"from.d.ts",
|
||||
"streams.cjs"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "node --experimental-loader ./test/http-loader.js ./test/test-wpt-in-node.js",
|
||||
"report": "c8 --reporter json --reporter text npm run test",
|
||||
"coverage": "npm run report && codecov -f coverage/coverage-final.json",
|
||||
"prepublishOnly": "tsc --declaration --emitDeclarationOnly --allowJs index.js from.js"
|
||||
},
|
||||
"repository": "https://github.com/node-fetch/fetch-blob.git",
|
||||
"keywords": [
|
||||
"blob",
|
||||
"file",
|
||||
"node-fetch"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^12.20 || >= 14.13"
|
||||
},
|
||||
"author": "Jimmy Wärting <jimmy@warting.se> (https://jimmy.warting.se)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/node-fetch/fetch-blob/issues"
|
||||
},
|
||||
"homepage": "https://github.com/node-fetch/fetch-blob#readme",
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.9",
|
||||
"c8": "^7.11.0",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
}
|
||||
}
|
51
node_modules/fetch-blob/streams.cjs
generated
vendored
Normal file
51
node_modules/fetch-blob/streams.cjs
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/* c8 ignore start */
|
||||
// 64 KiB (same size chrome slice theirs blob into Uint8array's)
|
||||
const POOL_SIZE = 65536
|
||||
|
||||
if (!globalThis.ReadableStream) {
|
||||
// `node:stream/web` got introduced in v16.5.0 as experimental
|
||||
// and it's preferred over the polyfilled version. So we also
|
||||
// suppress the warning that gets emitted by NodeJS for using it.
|
||||
try {
|
||||
const process = require('node:process')
|
||||
const { emitWarning } = process
|
||||
try {
|
||||
process.emitWarning = () => {}
|
||||
Object.assign(globalThis, require('node:stream/web'))
|
||||
process.emitWarning = emitWarning
|
||||
} catch (error) {
|
||||
process.emitWarning = emitWarning
|
||||
throw error
|
||||
}
|
||||
} catch (error) {
|
||||
// fallback to polyfill implementation
|
||||
Object.assign(globalThis, require('web-streams-polyfill/dist/ponyfill.es2018.js'))
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Don't use node: prefix for this, require+node: is not supported until node v14.14
|
||||
// Only `import()` can use prefix in 12.20 and later
|
||||
const { Blob } = require('buffer')
|
||||
if (Blob && !Blob.prototype.stream) {
|
||||
Blob.prototype.stream = function name (params) {
|
||||
let position = 0
|
||||
const blob = this
|
||||
|
||||
return new ReadableStream({
|
||||
type: 'bytes',
|
||||
async pull (ctrl) {
|
||||
const chunk = blob.slice(position, Math.min(blob.size, position + POOL_SIZE))
|
||||
const buffer = await chunk.arrayBuffer()
|
||||
position += buffer.byteLength
|
||||
ctrl.enqueue(new Uint8Array(buffer))
|
||||
|
||||
if (position === blob.size) {
|
||||
ctrl.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
/* c8 ignore end */
|
441
node_modules/formdata-polyfill/FormData.js
generated
vendored
Normal file
441
node_modules/formdata-polyfill/FormData.js
generated
vendored
Normal file
@ -0,0 +1,441 @@
|
||||
/* formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
/* global FormData self Blob File */
|
||||
/* eslint-disable no-inner-declarations */
|
||||
|
||||
if (typeof Blob !== 'undefined' && (typeof FormData === 'undefined' || !FormData.prototype.keys)) {
|
||||
const global = typeof globalThis === 'object'
|
||||
? globalThis
|
||||
: typeof window === 'object'
|
||||
? window
|
||||
: typeof self === 'object' ? self : this
|
||||
|
||||
// keep a reference to native implementation
|
||||
const _FormData = global.FormData
|
||||
|
||||
// To be monkey patched
|
||||
const _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send
|
||||
const _fetch = global.Request && global.fetch
|
||||
const _sendBeacon = global.navigator && global.navigator.sendBeacon
|
||||
// Might be a worker thread...
|
||||
const _match = global.Element && global.Element.prototype
|
||||
|
||||
// Unable to patch Request/Response constructor correctly #109
|
||||
// only way is to use ES6 class extend
|
||||
// https://github.com/babel/babel/issues/1966
|
||||
|
||||
const stringTag = global.Symbol && Symbol.toStringTag
|
||||
|
||||
// Add missing stringTags to blob and files
|
||||
if (stringTag) {
|
||||
if (!Blob.prototype[stringTag]) {
|
||||
Blob.prototype[stringTag] = 'Blob'
|
||||
}
|
||||
|
||||
if ('File' in global && !File.prototype[stringTag]) {
|
||||
File.prototype[stringTag] = 'File'
|
||||
}
|
||||
}
|
||||
|
||||
// Fix so you can construct your own File
|
||||
try {
|
||||
new File([], '') // eslint-disable-line
|
||||
} catch (a) {
|
||||
global.File = function File (b, d, c) {
|
||||
const blob = new Blob(b, c || {})
|
||||
const t = c && void 0 !== c.lastModified ? new Date(c.lastModified) : new Date()
|
||||
|
||||
Object.defineProperties(blob, {
|
||||
name: {
|
||||
value: d
|
||||
},
|
||||
lastModified: {
|
||||
value: +t
|
||||
},
|
||||
toString: {
|
||||
value () {
|
||||
return '[object File]'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (stringTag) {
|
||||
Object.defineProperty(blob, stringTag, {
|
||||
value: 'File'
|
||||
})
|
||||
}
|
||||
|
||||
return blob
|
||||
}
|
||||
}
|
||||
|
||||
function ensureArgs (args, expected) {
|
||||
if (args.length < expected) {
|
||||
throw new TypeError(`${expected} argument required, but only ${args.length} present.`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string | undefined} filename
|
||||
* @returns {[string, File|string]}
|
||||
*/
|
||||
function normalizeArgs (name, value, filename) {
|
||||
if (value instanceof Blob) {
|
||||
filename = filename !== undefined
|
||||
? String(filename + '')
|
||||
: typeof value.name === 'string'
|
||||
? value.name
|
||||
: 'blob'
|
||||
|
||||
if (value.name !== filename || Object.prototype.toString.call(value) === '[object Blob]') {
|
||||
value = new File([value], filename)
|
||||
}
|
||||
return [String(name), value]
|
||||
}
|
||||
return [String(name), String(value)]
|
||||
}
|
||||
|
||||
// normalize line feeds for textarea
|
||||
// https://html.spec.whatwg.org/multipage/form-elements.html#textarea-line-break-normalisation-transformation
|
||||
function normalizeLinefeeds (value) {
|
||||
return value.replace(/\r?\n|\r/g, '\r\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {ArrayLike<T>} arr
|
||||
* @param {{ (elm: T): void; }} cb
|
||||
*/
|
||||
function each (arr, cb) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
cb(arr[i])
|
||||
}
|
||||
}
|
||||
|
||||
const escape = str => str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22')
|
||||
|
||||
/**
|
||||
* @implements {Iterable}
|
||||
*/
|
||||
class FormDataPolyfill {
|
||||
/**
|
||||
* FormData class
|
||||
*
|
||||
* @param {HTMLFormElement=} form
|
||||
*/
|
||||
constructor (form) {
|
||||
/** @type {[string, string|File][]} */
|
||||
this._data = []
|
||||
|
||||
const self = this
|
||||
form && each(form.elements, (/** @type {HTMLInputElement} */ elm) => {
|
||||
if (
|
||||
!elm.name ||
|
||||
elm.disabled ||
|
||||
elm.type === 'submit' ||
|
||||
elm.type === 'button' ||
|
||||
elm.matches('form fieldset[disabled] *')
|
||||
) return
|
||||
|
||||
if (elm.type === 'file') {
|
||||
const files = elm.files && elm.files.length
|
||||
? elm.files
|
||||
: [new File([], '', { type: 'application/octet-stream' })] // #78
|
||||
|
||||
each(files, file => {
|
||||
self.append(elm.name, file)
|
||||
})
|
||||
} else if (elm.type === 'select-multiple' || elm.type === 'select-one') {
|
||||
each(elm.options, opt => {
|
||||
!opt.disabled && opt.selected && self.append(elm.name, opt.value)
|
||||
})
|
||||
} else if (elm.type === 'checkbox' || elm.type === 'radio') {
|
||||
if (elm.checked) self.append(elm.name, elm.value)
|
||||
} else {
|
||||
const value = elm.type === 'textarea' ? normalizeLinefeeds(elm.value) : elm.value
|
||||
self.append(elm.name, value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a field
|
||||
*
|
||||
* @param {string} name field name
|
||||
* @param {string|Blob|File} value string / blob / file
|
||||
* @param {string=} filename filename to use with blob
|
||||
* @return {undefined}
|
||||
*/
|
||||
append (name, value, filename) {
|
||||
ensureArgs(arguments, 2)
|
||||
this._data.push(normalizeArgs(name, value, filename))
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all fields values given name
|
||||
*
|
||||
* @param {string} name Field name
|
||||
* @return {undefined}
|
||||
*/
|
||||
delete (name) {
|
||||
ensureArgs(arguments, 1)
|
||||
const result = []
|
||||
name = String(name)
|
||||
|
||||
each(this._data, entry => {
|
||||
entry[0] !== name && result.push(entry)
|
||||
})
|
||||
|
||||
this._data = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all fields as [name, value]
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
* entries () {
|
||||
for (var i = 0; i < this._data.length; i++) {
|
||||
yield this._data[i]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all fields
|
||||
*
|
||||
* @param {Function} callback Executed for each item with parameters (value, name, thisArg)
|
||||
* @param {Object=} thisArg `this` context for callback function
|
||||
*/
|
||||
forEach (callback, thisArg) {
|
||||
ensureArgs(arguments, 1)
|
||||
for (const [name, value] of this) {
|
||||
callback.call(thisArg, value, name, this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first field value given name
|
||||
* or null if non existent
|
||||
*
|
||||
* @param {string} name Field name
|
||||
* @return {string|File|null} value Fields value
|
||||
*/
|
||||
get (name) {
|
||||
ensureArgs(arguments, 1)
|
||||
const entries = this._data
|
||||
name = String(name)
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
if (entries[i][0] === name) {
|
||||
return entries[i][1]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all fields values given name
|
||||
*
|
||||
* @param {string} name Fields name
|
||||
* @return {Array} [{String|File}]
|
||||
*/
|
||||
getAll (name) {
|
||||
ensureArgs(arguments, 1)
|
||||
const result = []
|
||||
name = String(name)
|
||||
each(this._data, data => {
|
||||
data[0] === name && result.push(data[1])
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for field name existence
|
||||
*
|
||||
* @param {string} name Field name
|
||||
* @return {boolean}
|
||||
*/
|
||||
has (name) {
|
||||
ensureArgs(arguments, 1)
|
||||
name = String(name)
|
||||
for (let i = 0; i < this._data.length; i++) {
|
||||
if (this._data[i][0] === name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all fields name
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
* keys () {
|
||||
for (const [name] of this) {
|
||||
yield name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite all values given name
|
||||
*
|
||||
* @param {string} name Filed name
|
||||
* @param {string} value Field value
|
||||
* @param {string=} filename Filename (optional)
|
||||
*/
|
||||
set (name, value, filename) {
|
||||
ensureArgs(arguments, 2)
|
||||
name = String(name)
|
||||
/** @type {[string, string|File][]} */
|
||||
const result = []
|
||||
const args = normalizeArgs(name, value, filename)
|
||||
let replace = true
|
||||
|
||||
// - replace the first occurrence with same name
|
||||
// - discards the remaining with same name
|
||||
// - while keeping the same order items where added
|
||||
each(this._data, data => {
|
||||
data[0] === name
|
||||
? replace && (replace = !result.push(args))
|
||||
: result.push(data)
|
||||
})
|
||||
|
||||
replace && result.push(args)
|
||||
|
||||
this._data = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all fields
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
* values () {
|
||||
for (const [, value] of this) {
|
||||
yield value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a native (perhaps degraded) FormData with only a `append` method
|
||||
* Can throw if it's not supported
|
||||
*
|
||||
* @return {FormData}
|
||||
*/
|
||||
['_asNative'] () {
|
||||
const fd = new _FormData()
|
||||
|
||||
for (const [name, value] of this) {
|
||||
fd.append(name, value)
|
||||
}
|
||||
|
||||
return fd
|
||||
}
|
||||
|
||||
/**
|
||||
* [_blob description]
|
||||
*
|
||||
* @return {Blob} [description]
|
||||
*/
|
||||
['_blob'] () {
|
||||
const boundary = '----formdata-polyfill-' + Math.random(),
|
||||
chunks = [],
|
||||
p = `--${boundary}\r\nContent-Disposition: form-data; name="`
|
||||
this.forEach((value, name) => typeof value == 'string'
|
||||
? chunks.push(p + escape(normalizeLinefeeds(name)) + `"\r\n\r\n${normalizeLinefeeds(value)}\r\n`)
|
||||
: chunks.push(p + escape(normalizeLinefeeds(name)) + `"; filename="${escape(value.name)}"\r\nContent-Type: ${value.type||"application/octet-stream"}\r\n\r\n`, value, `\r\n`))
|
||||
chunks.push(`--${boundary}--`)
|
||||
return new Blob(chunks, {
|
||||
type: "multipart/form-data; boundary=" + boundary
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* The class itself is iterable
|
||||
* alias for formdata.entries()
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
[Symbol.iterator] () {
|
||||
return this.entries()
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the default string description.
|
||||
*
|
||||
* @return {string} [object FormData]
|
||||
*/
|
||||
toString () {
|
||||
return '[object FormData]'
|
||||
}
|
||||
}
|
||||
|
||||
if (_match && !_match.matches) {
|
||||
_match.matches =
|
||||
_match.matchesSelector ||
|
||||
_match.mozMatchesSelector ||
|
||||
_match.msMatchesSelector ||
|
||||
_match.oMatchesSelector ||
|
||||
_match.webkitMatchesSelector ||
|
||||
function (s) {
|
||||
var matches = (this.document || this.ownerDocument).querySelectorAll(s)
|
||||
var i = matches.length
|
||||
while (--i >= 0 && matches.item(i) !== this) {}
|
||||
return i > -1
|
||||
}
|
||||
}
|
||||
|
||||
if (stringTag) {
|
||||
/**
|
||||
* Create the default string description.
|
||||
* It is accessed internally by the Object.prototype.toString().
|
||||
*/
|
||||
FormDataPolyfill.prototype[stringTag] = 'FormData'
|
||||
}
|
||||
|
||||
// Patch xhr's send method to call _blob transparently
|
||||
if (_send) {
|
||||
const setRequestHeader = global.XMLHttpRequest.prototype.setRequestHeader
|
||||
|
||||
global.XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
|
||||
setRequestHeader.call(this, name, value)
|
||||
if (name.toLowerCase() === 'content-type') this._hasContentType = true
|
||||
}
|
||||
|
||||
global.XMLHttpRequest.prototype.send = function (data) {
|
||||
// need to patch send b/c old IE don't send blob's type (#44)
|
||||
if (data instanceof FormDataPolyfill) {
|
||||
const blob = data['_blob']()
|
||||
if (!this._hasContentType) this.setRequestHeader('Content-Type', blob.type)
|
||||
_send.call(this, blob)
|
||||
} else {
|
||||
_send.call(this, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Patch fetch's function to call _blob transparently
|
||||
if (_fetch) {
|
||||
global.fetch = function (input, init) {
|
||||
if (init && init.body && init.body instanceof FormDataPolyfill) {
|
||||
init.body = init.body['_blob']()
|
||||
}
|
||||
|
||||
return _fetch.call(this, input, init)
|
||||
}
|
||||
}
|
||||
|
||||
// Patch navigator.sendBeacon to use native FormData
|
||||
if (_sendBeacon) {
|
||||
global.navigator.sendBeacon = function (url, data) {
|
||||
if (data instanceof FormDataPolyfill) {
|
||||
data = data['_asNative']()
|
||||
}
|
||||
return _sendBeacon.call(this, url, data)
|
||||
}
|
||||
}
|
||||
|
||||
global['FormData'] = FormDataPolyfill
|
||||
}
|
2
node_modules/undici/LICENSE → node_modules/formdata-polyfill/LICENSE
generated
vendored
2
node_modules/undici/LICENSE → node_modules/formdata-polyfill/LICENSE
generated
vendored
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Matteo Collina and Undici contributors
|
||||
Copyright (c) 2016 Jimmy Karl Roland Wärting
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
145
node_modules/formdata-polyfill/README.md
generated
vendored
Normal file
145
node_modules/formdata-polyfill/README.md
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
### A `FormData` polyfill for the browser ...and a module for NodeJS (`New!`)
|
||||
|
||||
```bash
|
||||
npm install formdata-polyfill
|
||||
```
|
||||
|
||||
The browser polyfill will likely have done its part already, and i hope you stop supporting old browsers c",)<br>
|
||||
But NodeJS still laks a proper FormData<br>The good old form-data package is a very old and isn't spec compatible and dose some abnormal stuff to construct and read FormData instances that other http libraries are not happy about when it comes to follow the spec.
|
||||
|
||||
### The NodeJS / ESM version
|
||||
- The modular (~2.3 KiB minified uncompressed) version of this package is independent of any browser stuff and don't patch anything
|
||||
- It's as pure/spec compatible as it possible gets the test are run by WPT.
|
||||
- It's compatible with [node-fetch](https://github.com/node-fetch/node-fetch).
|
||||
- It have higher platform dependencies as it uses classes, symbols, ESM & private fields
|
||||
- Only dependency it has is [fetch-blob](https://github.com/node-fetch/fetch-blob)
|
||||
|
||||
```js
|
||||
// Node example
|
||||
import fetch from 'node-fetch'
|
||||
import File from 'fetch-blob/file.js'
|
||||
import { fileFromSync } from 'fetch-blob/from.js'
|
||||
import { FormData } from 'formdata-polyfill/esm.min.js'
|
||||
|
||||
const file = fileFromSync('./README.md')
|
||||
const fd = new FormData()
|
||||
|
||||
fd.append('file-upload', new File(['abc'], 'hello-world.txt'))
|
||||
fd.append('file-upload', file)
|
||||
|
||||
// it's also possible to append file/blob look-a-like items
|
||||
// if you have streams coming from other destinations
|
||||
fd.append('file-upload', {
|
||||
size: 123,
|
||||
type: '',
|
||||
name: 'cat-video.mp4',
|
||||
stream() { return stream },
|
||||
[Symbol.toStringTag]: 'File'
|
||||
})
|
||||
|
||||
fetch('https://httpbin.org/post', { method: 'POST', body: fd })
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
It also comes with way to convert FormData into Blobs - it's not something that every developer should have to deal with.
|
||||
It's mainly for [node-fetch](https://github.com/node-fetch/node-fetch) and other http library to ease the process of serializing a FormData into a blob and just wish to deal with Blobs instead (Both Deno and Undici adapted a version of this [formDataToBlob](https://github.com/jimmywarting/FormData/blob/5ddea9e0de2fc5e246ab1b2f9d404dee0c319c02/formdata-to-blob.js) to the core and passes all WPT tests run by the browser itself)
|
||||
```js
|
||||
import { Readable } from 'node:stream'
|
||||
import { FormData, formDataToBlob } from 'formdata-polyfill/esm.min.js'
|
||||
|
||||
const blob = formDataToBlob(new FormData())
|
||||
fetch('https://httpbin.org/post', { method: 'POST', body: blob })
|
||||
|
||||
// node built in http and other similar http library have to do:
|
||||
const stream = Readable.from(blob.stream())
|
||||
const req = http.request('http://httpbin.org/post', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Length': blob.size,
|
||||
'Content-Type': blob.type
|
||||
}
|
||||
})
|
||||
stream.pipe(req)
|
||||
```
|
||||
|
||||
PS: blob & file that are appended to the FormData will not be read until any of the serialized blob read-methods gets called
|
||||
...so uploading very large files is no biggie
|
||||
|
||||
### Browser polyfill
|
||||
|
||||
usage:
|
||||
|
||||
```js
|
||||
import 'formdata-polyfill' // that's it
|
||||
```
|
||||
|
||||
The browser polyfill conditionally replaces the native implementation rather than fixing the missing functions,
|
||||
since otherwise there is no way to get or delete existing values in the FormData object.
|
||||
Therefore this also patches `XMLHttpRequest.prototype.send` and `fetch` to send the `FormData` as a blob,
|
||||
and `navigator.sendBeacon` to send native `FormData`.
|
||||
|
||||
I was unable to patch the Response/Request constructor
|
||||
so if you are constructing them with FormData then you need to call `fd._blob()` manually.
|
||||
|
||||
```js
|
||||
new Request(url, {
|
||||
method: 'post',
|
||||
body: fd._blob ? fd._blob() : fd
|
||||
})
|
||||
```
|
||||
|
||||
Dependencies
|
||||
---
|
||||
|
||||
If you need to support IE <= 9 then I recommend you to include eligrey's [blob.js]
|
||||
(which i hope you don't - since IE is now dead)
|
||||
|
||||
<details>
|
||||
<summary>Updating from 2.x to 3.x</summary>
|
||||
|
||||
Previously you had to import the polyfill and use that,
|
||||
since it didn't replace the global (existing) FormData implementation.
|
||||
But now it transparently calls `_blob()` for you when you are sending something with fetch or XHR,
|
||||
by way of monkey-patching the `XMLHttpRequest.prototype.send` and `fetch` functions.
|
||||
|
||||
So you maybe had something like this:
|
||||
|
||||
```javascript
|
||||
var FormData = require('formdata-polyfill')
|
||||
var fd = new FormData(form)
|
||||
xhr.send(fd._blob())
|
||||
```
|
||||
|
||||
There is no longer anything exported from the module
|
||||
(though you of course still need to import it to install the polyfill),
|
||||
so you can now use the FormData object as normal:
|
||||
|
||||
```javascript
|
||||
require('formdata-polyfill')
|
||||
var fd = new FormData(form)
|
||||
xhr.send(fd)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
Native Browser compatibility (as of 2021-05-08)
|
||||
---
|
||||
Based on this you can decide for yourself if you need this polyfill.
|
||||
|
||||
[](https://developer.mozilla.org/en-US/docs/Web/API/FormData#Browser_compatibility)
|
||||
|
||||
|
||||
|
||||
This normalizes support for the FormData API:
|
||||
|
||||
- `append` with filename
|
||||
- `delete()`, `get()`, `getAll()`, `has()`, `set()`
|
||||
- `entries()`, `keys()`, `values()`, and support for `for...of`
|
||||
- Available in web workers (just include the polyfill)
|
||||
|
||||
[npm-image]: https://img.shields.io/npm/v/formdata-polyfill.svg
|
||||
[npm-url]: https://www.npmjs.com/package/formdata-polyfill
|
||||
[blob.js]: https://github.com/eligrey/Blob.js
|
5
node_modules/formdata-polyfill/esm.min.d.ts
generated
vendored
Normal file
5
node_modules/formdata-polyfill/esm.min.d.ts
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
export declare const FormData: {
|
||||
new (): FormData;
|
||||
prototype: FormData;
|
||||
};
|
||||
export declare function formDataToBlob(formData: FormData): Blob;
|
40
node_modules/formdata-polyfill/esm.min.js
generated
vendored
Normal file
40
node_modules/formdata-polyfill/esm.min.js
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
import C from 'fetch-blob'
|
||||
import F from 'fetch-blob/file.js'
|
||||
|
||||
var {toStringTag:t,iterator:i,hasInstance:h}=Symbol,
|
||||
r=Math.random,
|
||||
m='append,set,get,getAll,delete,keys,values,entries,forEach,constructor'.split(','),
|
||||
f=(a,b,c)=>(a+='',/^(Blob|File)$/.test(b && b[t])?[(c=c!==void 0?c+'':b[t]=='File'?b.name:'blob',a),b.name!==c||b[t]=='blob'?new F([b],c,b):b]:[a,b+'']),
|
||||
e=(c,f)=>(f?c:c.replace(/\r?\n|\r/g,'\r\n')).replace(/\n/g,'%0A').replace(/\r/g,'%0D').replace(/"/g,'%22'),
|
||||
x=(n, a, e)=>{if(a.length<e){throw new TypeError(`Failed to execute '${n}' on 'FormData': ${e} arguments required, but only ${a.length} present.`)}}
|
||||
|
||||
export const File = F
|
||||
|
||||
/** @type {typeof globalThis.FormData} */
|
||||
export const FormData = class FormData {
|
||||
#d=[];
|
||||
constructor(...a){if(a.length)throw new TypeError(`Failed to construct 'FormData': parameter 1 is not of type 'HTMLFormElement'.`)}
|
||||
get [t]() {return 'FormData'}
|
||||
[i](){return this.entries()}
|
||||
static [h](o) {return o&&typeof o==='object'&&o[t]==='FormData'&&!m.some(m=>typeof o[m]!='function')}
|
||||
append(...a){x('append',arguments,2);this.#d.push(f(...a))}
|
||||
delete(a){x('delete',arguments,1);a+='';this.#d=this.#d.filter(([b])=>b!==a)}
|
||||
get(a){x('get',arguments,1);a+='';for(var b=this.#d,l=b.length,c=0;c<l;c++)if(b[c][0]===a)return b[c][1];return null}
|
||||
getAll(a,b){x('getAll',arguments,1);b=[];a+='';this.#d.forEach(c=>c[0]===a&&b.push(c[1]));return b}
|
||||
has(a){x('has',arguments,1);a+='';return this.#d.some(b=>b[0]===a)}
|
||||
forEach(a,b){x('forEach',arguments,1);for(var [c,d]of this)a.call(b,d,c,this)}
|
||||
set(...a){x('set',arguments,2);var b=[],c=!0;a=f(...a);this.#d.forEach(d=>{d[0]===a[0]?c&&(c=!b.push(a)):b.push(d)});c&&b.push(a);this.#d=b}
|
||||
*entries(){yield*this.#d}
|
||||
*keys(){for(var[a]of this)yield a}
|
||||
*values(){for(var[,a]of this)yield a}}
|
||||
|
||||
/** @param {FormData} F */
|
||||
export function formDataToBlob (F,B=C){
|
||||
var b=`${r()}${r()}`.replace(/\./g, '').slice(-28).padStart(32, '-'),c=[],p=`--${b}\r\nContent-Disposition: form-data; name="`
|
||||
F.forEach((v,n)=>typeof v=='string'
|
||||
?c.push(p+e(n)+`"\r\n\r\n${v.replace(/\r(?!\n)|(?<!\r)\n/g, '\r\n')}\r\n`)
|
||||
:c.push(p+e(n)+`"; filename="${e(v.name, 1)}"\r\nContent-Type: ${v.type||"application/octet-stream"}\r\n\r\n`, v, '\r\n'))
|
||||
c.push(`--${b}--`)
|
||||
return new B(c,{type:"multipart/form-data; boundary="+b})}
|
39
node_modules/formdata-polyfill/formdata-to-blob.js
generated
vendored
Normal file
39
node_modules/formdata-polyfill/formdata-to-blob.js
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
/*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
const escape = (str, filename) =>
|
||||
(filename ? str : str.replace(/\r?\n|\r/g, '\r\n'))
|
||||
.replace(/\n/g, '%0A')
|
||||
.replace(/\r/g, '%0D')
|
||||
.replace(/"/g, '%22')
|
||||
|
||||
/**
|
||||
* pure function to convert any formData instance to a Blob
|
||||
* instances synchronous without reading all of the files
|
||||
*
|
||||
* @param {FormData|*} formData an instance of a formData Class
|
||||
* @param {Blob|*} [BlobClass=Blob] the Blob class to use when constructing it
|
||||
*/
|
||||
export function formDataToBlob (formData, BlobClass = Blob) {
|
||||
const boundary = ('----formdata-polyfill-' + Math.random())
|
||||
const chunks = []
|
||||
const prefix = `--${boundary}\r\nContent-Disposition: form-data; name="`
|
||||
|
||||
for (let [name, value] of formData) {
|
||||
if (typeof value === 'string') {
|
||||
chunks.push(prefix + escape(name) + `"\r\n\r\n${value.replace(/\r(?!\n)|(?<!\r)\n/g, '\r\n')}\r\n`)
|
||||
} else {
|
||||
chunks.push(
|
||||
prefix + escape(name) + `"; filename="${escape(value.name, 1)}"\r\n` +
|
||||
`Content-Type: ${value.type || 'application/octet-stream'}\r\n\r\n`,
|
||||
value,
|
||||
'\r\n'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
chunks.push(`--${boundary}--`)
|
||||
|
||||
return new BlobClass(chunks, {
|
||||
type: 'multipart/form-data; boundary=' + boundary
|
||||
})
|
||||
}
|
21
node_modules/formdata-polyfill/formdata.min.js
generated
vendored
Normal file
21
node_modules/formdata-polyfill/formdata.min.js
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
/*! formdata-polyfill. MIT License. Jimmy W?rting <https://jimmy.warting.se/opensource> */
|
||||
;(function(){var h;function l(a){var b=0;return function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}}}var m="function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){if(a==Array.prototype||a==Object.prototype)return a;a[b]=c.value;return a};
|
||||
function n(a){a=["object"==typeof globalThis&&globalThis,a,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");}var q=n(this);function r(a,b){if(b)a:{var c=q;a=a.split(".");for(var d=0;d<a.length-1;d++){var e=a[d];if(!(e in c))break a;c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&m(c,a,{configurable:!0,writable:!0,value:b})}}
|
||||
r("Symbol",function(a){function b(f){if(this instanceof b)throw new TypeError("Symbol is not a constructor");return new c(d+(f||"")+"_"+e++,f)}function c(f,g){this.A=f;m(this,"description",{configurable:!0,writable:!0,value:g})}if(a)return a;c.prototype.toString=function(){return this.A};var d="jscomp_symbol_"+(1E9*Math.random()>>>0)+"_",e=0;return b});
|
||||
r("Symbol.iterator",function(a){if(a)return a;a=Symbol("Symbol.iterator");for(var b="Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array".split(" "),c=0;c<b.length;c++){var d=q[b[c]];"function"===typeof d&&"function"!=typeof d.prototype[a]&&m(d.prototype,a,{configurable:!0,writable:!0,value:function(){return u(l(this))}})}return a});function u(a){a={next:a};a[Symbol.iterator]=function(){return this};return a}
|
||||
function v(a){var b="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];return b?b.call(a):{next:l(a)}}var w;if("function"==typeof Object.setPrototypeOf)w=Object.setPrototypeOf;else{var y;a:{var z={a:!0},A={};try{A.__proto__=z;y=A.a;break a}catch(a){}y=!1}w=y?function(a,b){a.__proto__=b;if(a.__proto__!==b)throw new TypeError(a+" is not extensible");return a}:null}var B=w;function C(){this.m=!1;this.j=null;this.v=void 0;this.h=1;this.u=this.C=0;this.l=null}
|
||||
function D(a){if(a.m)throw new TypeError("Generator is already running");a.m=!0}C.prototype.o=function(a){this.v=a};C.prototype.s=function(a){this.l={D:a,F:!0};this.h=this.C||this.u};C.prototype.return=function(a){this.l={return:a};this.h=this.u};function E(a,b){a.h=3;return{value:b}}function F(a){this.g=new C;this.G=a}F.prototype.o=function(a){D(this.g);if(this.g.j)return G(this,this.g.j.next,a,this.g.o);this.g.o(a);return H(this)};
|
||||
function I(a,b){D(a.g);var c=a.g.j;if(c)return G(a,"return"in c?c["return"]:function(d){return{value:d,done:!0}},b,a.g.return);a.g.return(b);return H(a)}F.prototype.s=function(a){D(this.g);if(this.g.j)return G(this,this.g.j["throw"],a,this.g.o);this.g.s(a);return H(this)};
|
||||
function G(a,b,c,d){try{var e=b.call(a.g.j,c);if(!(e instanceof Object))throw new TypeError("Iterator result "+e+" is not an object");if(!e.done)return a.g.m=!1,e;var f=e.value}catch(g){return a.g.j=null,a.g.s(g),H(a)}a.g.j=null;d.call(a.g,f);return H(a)}function H(a){for(;a.g.h;)try{var b=a.G(a.g);if(b)return a.g.m=!1,{value:b.value,done:!1}}catch(c){a.g.v=void 0,a.g.s(c)}a.g.m=!1;if(a.g.l){b=a.g.l;a.g.l=null;if(b.F)throw b.D;return{value:b.return,done:!0}}return{value:void 0,done:!0}}
|
||||
function J(a){this.next=function(b){return a.o(b)};this.throw=function(b){return a.s(b)};this.return=function(b){return I(a,b)};this[Symbol.iterator]=function(){return this}}function K(a,b){b=new J(new F(b));B&&a.prototype&&B(b,a.prototype);return b}function L(a,b){a instanceof String&&(a+="");var c=0,d=!1,e={next:function(){if(!d&&c<a.length){var f=c++;return{value:b(f,a[f]),done:!1}}d=!0;return{done:!0,value:void 0}}};e[Symbol.iterator]=function(){return e};return e}
|
||||
r("Array.prototype.entries",function(a){return a?a:function(){return L(this,function(b,c){return[b,c]})}});
|
||||
if("undefined"!==typeof Blob&&("undefined"===typeof FormData||!FormData.prototype.keys)){var M=function(a,b){for(var c=0;c<a.length;c++)b(a[c])},N=function(a){return a.replace(/\r?\n|\r/g,"\r\n")},O=function(a,b,c){if(b instanceof Blob){c=void 0!==c?String(c+""):"string"===typeof b.name?b.name:"blob";if(b.name!==c||"[object Blob]"===Object.prototype.toString.call(b))b=new File([b],c);return[String(a),b]}return[String(a),String(b)]},P=function(a,b){if(a.length<b)throw new TypeError(b+" argument required, but only "+
|
||||
a.length+" present.");},Q="object"===typeof globalThis?globalThis:"object"===typeof window?window:"object"===typeof self?self:this,R=Q.FormData,S=Q.XMLHttpRequest&&Q.XMLHttpRequest.prototype.send,T=Q.Request&&Q.fetch,U=Q.navigator&&Q.navigator.sendBeacon,V=Q.Element&&Q.Element.prototype,W=Q.Symbol&&Symbol.toStringTag;W&&(Blob.prototype[W]||(Blob.prototype[W]="Blob"),"File"in Q&&!File.prototype[W]&&(File.prototype[W]="File"));try{new File([],"")}catch(a){Q.File=function(b,c,d){b=new Blob(b,d||{});
|
||||
Object.defineProperties(b,{name:{value:c},lastModified:{value:+(d&&void 0!==d.lastModified?new Date(d.lastModified):new Date)},toString:{value:function(){return"[object File]"}}});W&&Object.defineProperty(b,W,{value:"File"});return b}}var escape=function(a){return a.replace(/\n/g,"%0A").replace(/\r/g,"%0D").replace(/"/g,"%22")},X=function(a){this.i=[];var b=this;a&&M(a.elements,function(c){if(c.name&&!c.disabled&&"submit"!==c.type&&"button"!==c.type&&!c.matches("form fieldset[disabled] *"))if("file"===
|
||||
c.type){var d=c.files&&c.files.length?c.files:[new File([],"",{type:"application/octet-stream"})];M(d,function(e){b.append(c.name,e)})}else"select-multiple"===c.type||"select-one"===c.type?M(c.options,function(e){!e.disabled&&e.selected&&b.append(c.name,e.value)}):"checkbox"===c.type||"radio"===c.type?c.checked&&b.append(c.name,c.value):(d="textarea"===c.type?N(c.value):c.value,b.append(c.name,d))})};h=X.prototype;h.append=function(a,b,c){P(arguments,2);this.i.push(O(a,b,c))};h.delete=function(a){P(arguments,
|
||||
1);var b=[];a=String(a);M(this.i,function(c){c[0]!==a&&b.push(c)});this.i=b};h.entries=function b(){var c,d=this;return K(b,function(e){1==e.h&&(c=0);if(3!=e.h)return c<d.i.length?e=E(e,d.i[c]):(e.h=0,e=void 0),e;c++;e.h=2})};h.forEach=function(b,c){P(arguments,1);for(var d=v(this),e=d.next();!e.done;e=d.next()){var f=v(e.value);e=f.next().value;f=f.next().value;b.call(c,f,e,this)}};h.get=function(b){P(arguments,1);var c=this.i;b=String(b);for(var d=0;d<c.length;d++)if(c[d][0]===b)return c[d][1];
|
||||
return null};h.getAll=function(b){P(arguments,1);var c=[];b=String(b);M(this.i,function(d){d[0]===b&&c.push(d[1])});return c};h.has=function(b){P(arguments,1);b=String(b);for(var c=0;c<this.i.length;c++)if(this.i[c][0]===b)return!0;return!1};h.keys=function c(){var d=this,e,f,g,k,p;return K(c,function(t){1==t.h&&(e=v(d),f=e.next());if(3!=t.h){if(f.done){t.h=0;return}g=f.value;k=v(g);p=k.next().value;return E(t,p)}f=e.next();t.h=2})};h.set=function(c,d,e){P(arguments,2);c=String(c);var f=[],g=O(c,
|
||||
d,e),k=!0;M(this.i,function(p){p[0]===c?k&&(k=!f.push(g)):f.push(p)});k&&f.push(g);this.i=f};h.values=function d(){var e=this,f,g,k,p,t;return K(d,function(x){1==x.h&&(f=v(e),g=f.next());if(3!=x.h){if(g.done){x.h=0;return}k=g.value;p=v(k);p.next();t=p.next().value;return E(x,t)}g=f.next();x.h=2})};X.prototype._asNative=function(){for(var d=new R,e=v(this),f=e.next();!f.done;f=e.next()){var g=v(f.value);f=g.next().value;g=g.next().value;d.append(f,g)}return d};X.prototype._blob=function(){var d="----formdata-polyfill-"+
|
||||
Math.random(),e=[],f="--"+d+'\r\nContent-Disposition: form-data; name="';this.forEach(function(g,k){return"string"==typeof g?e.push(f+escape(N(k))+('"\r\n\r\n'+N(g)+"\r\n")):e.push(f+escape(N(k))+('"; filename="'+escape(g.name)+'"\r\nContent-Type: '+(g.type||"application/octet-stream")+"\r\n\r\n"),g,"\r\n")});e.push("--"+d+"--");return new Blob(e,{type:"multipart/form-data; boundary="+d})};X.prototype[Symbol.iterator]=function(){return this.entries()};X.prototype.toString=function(){return"[object FormData]"};
|
||||
V&&!V.matches&&(V.matches=V.matchesSelector||V.mozMatchesSelector||V.msMatchesSelector||V.oMatchesSelector||V.webkitMatchesSelector||function(d){d=(this.document||this.ownerDocument).querySelectorAll(d);for(var e=d.length;0<=--e&&d.item(e)!==this;);return-1<e});W&&(X.prototype[W]="FormData");if(S){var Y=Q.XMLHttpRequest.prototype.setRequestHeader;Q.XMLHttpRequest.prototype.setRequestHeader=function(d,e){Y.call(this,d,e);"content-type"===d.toLowerCase()&&(this.B=!0)};Q.XMLHttpRequest.prototype.send=
|
||||
function(d){d instanceof X?(d=d._blob(),this.B||this.setRequestHeader("Content-Type",d.type),S.call(this,d)):S.call(this,d)}}T&&(Q.fetch=function(d,e){e&&e.body&&e.body instanceof X&&(e.body=e.body._blob());return T.call(this,d,e)});U&&(Q.navigator.sendBeacon=function(d,e){e instanceof X&&(e=e._asNative());return U.call(this,d,e)});Q.FormData=X};})();
|
50
node_modules/formdata-polyfill/package.json
generated
vendored
Normal file
50
node_modules/formdata-polyfill/package.json
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "formdata-polyfill",
|
||||
"version": "4.0.10",
|
||||
"description": "HTML5 `FormData` for Browsers and Node.",
|
||||
"type": "module",
|
||||
"main": "formdata.min.js",
|
||||
"scripts": {
|
||||
"build": "node build.js",
|
||||
"test": "node test/test-esm.js",
|
||||
"test-wpt": "node --experimental-loader ./test/http-loader.js ./test/test-wpt-in-node.js",
|
||||
"test-polyfill": "php -S localhost:4445 & open http://localhost:4445/test/test-polyfill.html"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://jimmywarting@github.com/jimmywarting/FormData.git"
|
||||
},
|
||||
"files": [
|
||||
"esm.min.js",
|
||||
"esm.min.d.ts",
|
||||
"FormData.js",
|
||||
"formdata-to-blob.js",
|
||||
"formdata.min.js",
|
||||
"README.md"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"keywords": [
|
||||
"formdata",
|
||||
"fetch",
|
||||
"node-fetch",
|
||||
"html5",
|
||||
"browser",
|
||||
"polyfill"
|
||||
],
|
||||
"author": "Jimmy Wärting",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jimmywarting/FormData/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jimmywarting/FormData#readme",
|
||||
"dependencies": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/google-closure-compiler": "^0.0.19",
|
||||
"@types/node": "^16.7.10",
|
||||
"google-closure-compiler": "^20210808.0.0"
|
||||
}
|
||||
}
|
2
node_modules/node-domexception/.history/README_20210527203617.md
generated
vendored
Normal file
2
node_modules/node-domexception/.history/README_20210527203617.md
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# node-domexception
|
||||
An implementation of the DOMException class from NodeJS
|
41
node_modules/node-domexception/.history/README_20210527212714.md
generated
vendored
Normal file
41
node_modules/node-domexception/.history/README_20210527212714.md
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# DOMException
|
||||
An implementation of the DOMException class from NodeJS
|
||||
|
||||
This package implements the [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException) class, from NodeJS itself.
|
||||
NodeJS has DOMException built in, but it's not globally available, and you can't require/import it from somewhere.
|
||||
|
||||
The only possible way is to use some web-ish tools that have been introduced into NodeJS that throws an error and catch the constructor.
|
||||
This way you will have the same class that NodeJS has and you can check if the error is a instance of DOMException.
|
||||
The instanceof check would not have worked with a custom class such as the DOMexception provided by domenic which also is much larger in size.
|
||||
|
||||
```js
|
||||
import DOMException from 'node-domexception'
|
||||
|
||||
hello().catch(err => {
|
||||
if (err instanceof DOMException) {
|
||||
...
|
||||
}
|
||||
})
|
||||
|
||||
const e1 = new DOMException("Something went wrong", "BadThingsError");
|
||||
console.assert(e1.name === "BadThingsError");
|
||||
console.assert(e1.code === 0);
|
||||
|
||||
const e2 = new DOMException("Another exciting error message", "NoModificationAllowedError");
|
||||
console.assert(e2.name === "NoModificationAllowedError");
|
||||
console.assert(e2.code === 7);
|
||||
|
||||
console.assert(DOMException.INUSE_ATTRIBUTE_ERR === 10);
|
||||
```
|
||||
|
||||
## APIs
|
||||
|
||||
This package exposes two flavors of the `DOMException` interface depending on the imported module.
|
||||
|
||||
### `domexception` module
|
||||
|
||||
This module default-exports the `DOMException` interface constructor.
|
||||
|
||||
### `domexception/webidl2js-wrapper` module
|
||||
|
||||
This module exports the `DOMException` [interface wrapper API](https://github.com/jsdom/webidl2js#for-interfaces) generated by [webidl2js](https://github.com/jsdom/webidl2js).
|
36
node_modules/node-domexception/.history/README_20210527213345.md
generated
vendored
Normal file
36
node_modules/node-domexception/.history/README_20210527213345.md
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# DOMException
|
||||
An implementation of the DOMException class from NodeJS
|
||||
|
||||
This package implements the [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException) class, from NodeJS itself. (including the legacy codes)
|
||||
NodeJS has DOMException built in, but it's not globally available, and you can't require/import it from somewhere.
|
||||
|
||||
The only possible way is to use some web-ish tools that have been introduced into NodeJS that throws an error and catch the constructor.
|
||||
This way you will have the same class that NodeJS has and you can check if the error is a instance of DOMException.
|
||||
The instanceof check would not have worked with a custom class such as the DOMException provided by domenic which also is much larger in size.
|
||||
|
||||
```js
|
||||
import DOMException from 'node-domexception'
|
||||
import { MessageChannel } from 'worker_threads'
|
||||
|
||||
async function hello() {
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
}
|
||||
|
||||
hello().catch(err => {
|
||||
console.assert(err.name === 'DataCloneError')
|
||||
console.assert(err.code === 25)
|
||||
console.assert(err instanceof DOMException)
|
||||
})
|
||||
|
||||
const e1 = new DOMException('Something went wrong', 'BadThingsError')
|
||||
console.assert(e1.name === 'BadThingsError')
|
||||
console.assert(e1.code === 0)
|
||||
|
||||
const e2 = new DOMException('Another exciting error message', 'NoModificationAllowedError')
|
||||
console.assert(e2.name === 'NoModificationAllowedError')
|
||||
console.assert(e2.code === 7)
|
||||
|
||||
console.assert(DOMException.INUSE_ATTRIBUTE_ERR === 10)
|
||||
```
|
36
node_modules/node-domexception/.history/README_20210527213411.md
generated
vendored
Normal file
36
node_modules/node-domexception/.history/README_20210527213411.md
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# DOMException
|
||||
An implementation of the DOMException class from NodeJS
|
||||
|
||||
This package implements the [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException) class that comes from NodeJS itself. (including the legacy codes)
|
||||
NodeJS has DOMException built in, but it's not globally available, and you can't require/import it from somewhere.
|
||||
|
||||
The only possible way is to use some web-ish tools that have been introduced into NodeJS that throws an error and catch the constructor.
|
||||
This way you will have the same class that NodeJS has and you can check if the error is a instance of DOMException.
|
||||
The instanceof check would not have worked with a custom class such as the DOMException provided by domenic which also is much larger in size.
|
||||
|
||||
```js
|
||||
import DOMException from 'node-domexception'
|
||||
import { MessageChannel } from 'worker_threads'
|
||||
|
||||
async function hello() {
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
}
|
||||
|
||||
hello().catch(err => {
|
||||
console.assert(err.name === 'DataCloneError')
|
||||
console.assert(err.code === 25)
|
||||
console.assert(err instanceof DOMException)
|
||||
})
|
||||
|
||||
const e1 = new DOMException('Something went wrong', 'BadThingsError')
|
||||
console.assert(e1.name === 'BadThingsError')
|
||||
console.assert(e1.code === 0)
|
||||
|
||||
const e2 = new DOMException('Another exciting error message', 'NoModificationAllowedError')
|
||||
console.assert(e2.name === 'NoModificationAllowedError')
|
||||
console.assert(e2.code === 7)
|
||||
|
||||
console.assert(DOMException.INUSE_ATTRIBUTE_ERR === 10)
|
||||
```
|
36
node_modules/node-domexception/.history/README_20210527213803.md
generated
vendored
Normal file
36
node_modules/node-domexception/.history/README_20210527213803.md
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# DOMException
|
||||
An implementation of the DOMException class from NodeJS
|
||||
|
||||
This package exposes the [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException) class that comes from NodeJS itself. (including all of the deprecated legacy codes)
|
||||
NodeJS has it built in, but it's not globally available, and you can't require/import it from somewhere.
|
||||
|
||||
The only possible way is to use some web-ish tools that have been introduced into NodeJS that throws an error and catch the constructor.
|
||||
This way you will have the same class that NodeJS has and you can check if the error is a instance of DOMException.
|
||||
The instanceof check would not have worked with a custom class such as the DOMException provided by domenic which also is much larger in size since it has to re-construct the hole class from the ground up.
|
||||
|
||||
```js
|
||||
import DOMException from 'node-domexception'
|
||||
import { MessageChannel } from 'worker_threads'
|
||||
|
||||
async function hello() {
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
}
|
||||
|
||||
hello().catch(err => {
|
||||
console.assert(err.name === 'DataCloneError')
|
||||
console.assert(err.code === 25)
|
||||
console.assert(err instanceof DOMException)
|
||||
})
|
||||
|
||||
const e1 = new DOMException('Something went wrong', 'BadThingsError')
|
||||
console.assert(e1.name === 'BadThingsError')
|
||||
console.assert(e1.code === 0)
|
||||
|
||||
const e2 = new DOMException('Another exciting error message', 'NoModificationAllowedError')
|
||||
console.assert(e2.name === 'NoModificationAllowedError')
|
||||
console.assert(e2.code === 7)
|
||||
|
||||
console.assert(DOMException.INUSE_ATTRIBUTE_ERR === 10)
|
||||
```
|
38
node_modules/node-domexception/.history/README_20210527214323.md
generated
vendored
Normal file
38
node_modules/node-domexception/.history/README_20210527214323.md
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
# DOMException
|
||||
An implementation of the DOMException class from NodeJS
|
||||
|
||||
This package exposes the [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException) class that comes from NodeJS itself. (including all of the deprecated legacy codes)
|
||||
NodeJS has it built in, but it's not globally available, and you can't require/import it from somewhere.
|
||||
|
||||
The only possible way is to use some web-ish tools that have been introduced into NodeJS that throws an error and catch the constructor.
|
||||
This way you will have the same class that NodeJS has and you can check if the error is a instance of DOMException.
|
||||
The instanceof check would not have worked with a custom class such as the DOMException provided by domenic which also is much larger in size since it has to re-construct the hole class from the ground up.
|
||||
|
||||
(plz don't depend on this package in any other environment other than node >=10.5)
|
||||
|
||||
```js
|
||||
import DOMException from 'node-domexception'
|
||||
import { MessageChannel } from 'worker_threads'
|
||||
|
||||
async function hello() {
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
}
|
||||
|
||||
hello().catch(err => {
|
||||
console.assert(err.name === 'DataCloneError')
|
||||
console.assert(err.code === 25)
|
||||
console.assert(err instanceof DOMException)
|
||||
})
|
||||
|
||||
const e1 = new DOMException('Something went wrong', 'BadThingsError')
|
||||
console.assert(e1.name === 'BadThingsError')
|
||||
console.assert(e1.code === 0)
|
||||
|
||||
const e2 = new DOMException('Another exciting error message', 'NoModificationAllowedError')
|
||||
console.assert(e2.name === 'NoModificationAllowedError')
|
||||
console.assert(e2.code === 7)
|
||||
|
||||
console.assert(DOMException.INUSE_ATTRIBUTE_ERR === 10)
|
||||
```
|
38
node_modules/node-domexception/.history/README_20210527214408.md
generated
vendored
Normal file
38
node_modules/node-domexception/.history/README_20210527214408.md
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
# DOMException
|
||||
An implementation of the DOMException class from NodeJS
|
||||
|
||||
This package exposes the [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException) class that comes from NodeJS itself. (including all of the deprecated legacy codes)
|
||||
NodeJS has it built in, but it's not globally available, and you can't require/import it from somewhere.
|
||||
|
||||
The only possible way is to use some web-ish tools that have been introduced into NodeJS that throws an error and catch the constructor.
|
||||
This way you will have the same class that NodeJS has and you can check if the error is a instance of DOMException.
|
||||
The instanceof check would not have worked with a custom class such as the DOMException provided by domenic which also is much larger in size since it has to re-construct the hole class from the ground up.
|
||||
|
||||
(plz don't depend on this package in any other environment other than node >=10.5)
|
||||
|
||||
```js
|
||||
import DOMException from 'node-domexception'
|
||||
import { MessageChannel } from 'worker_threads'
|
||||
|
||||
async function hello() {
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
}
|
||||
|
||||
hello().catch(err => {
|
||||
console.assert(err.name === 'DataCloneError')
|
||||
console.assert(err.code === 25)
|
||||
console.assert(err instanceof DOMException)
|
||||
})
|
||||
|
||||
const e1 = new DOMException('Something went wrong', 'BadThingsError')
|
||||
console.assert(e1.name === 'BadThingsError')
|
||||
console.assert(e1.code === 0)
|
||||
|
||||
const e2 = new DOMException('Another exciting error message', 'NoModificationAllowedError')
|
||||
console.assert(e2.name === 'NoModificationAllowedError')
|
||||
console.assert(e2.code === 7)
|
||||
|
||||
console.assert(DOMException.INUSE_ATTRIBUTE_ERR === 10)
|
||||
```
|
0
node_modules/node-domexception/.history/index_20210527203842.js
generated
vendored
Normal file
0
node_modules/node-domexception/.history/index_20210527203842.js
generated
vendored
Normal file
8
node_modules/node-domexception/.history/index_20210527203947.js
generated
vendored
Normal file
8
node_modules/node-domexception/.history/index_20210527203947.js
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
const { MessageChannel } = require('worker_threads')
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) { globalThis.DOMException = err.constructor }
|
||||
}
|
9
node_modules/node-domexception/.history/index_20210527204259.js
generated
vendored
Normal file
9
node_modules/node-domexception/.history/index_20210527204259.js
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads')
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) { globalThis.DOMException = err.constructor }
|
||||
}
|
||||
|
||||
module.exports
|
9
node_modules/node-domexception/.history/index_20210527204418.js
generated
vendored
Normal file
9
node_modules/node-domexception/.history/index_20210527204418.js
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads')
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) { globalThis.DOMException = err.constructor }
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
11
node_modules/node-domexception/.history/index_20210527204756.js
generated
vendored
Normal file
11
node_modules/node-domexception/.history/index_20210527204756.js
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/*! blob-to-buffer. MIT License. Jimmy Wärting <https://jimmy.warting.se> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads')
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) { globalThis.DOMException = err.constructor }
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
11
node_modules/node-domexception/.history/index_20210527204833.js
generated
vendored
Normal file
11
node_modules/node-domexception/.history/index_20210527204833.js
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/*! blob-to-buffer. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads')
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) { globalThis.DOMException = err.constructor }
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
15
node_modules/node-domexception/.history/index_20210527211208.js
generated
vendored
Normal file
15
node_modules/node-domexception/.history/index_20210527211208.js
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/*! blob-to-buffer. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
var { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
15
node_modules/node-domexception/.history/index_20210527211248.js
generated
vendored
Normal file
15
node_modules/node-domexception/.history/index_20210527211248.js
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/*! blob-to-buffer. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
23
node_modules/node-domexception/.history/index_20210527212722.js
generated
vendored
Normal file
23
node_modules/node-domexception/.history/index_20210527212722.js
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/*! blob-to-buffer. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
||||
|
||||
const e1 = new DOMException("Something went wrong", "BadThingsError");
|
||||
console.assert(e1.name === "BadThingsError");
|
||||
console.assert(e1.code === 0);
|
||||
|
||||
const e2 = new DOMException("Another exciting error message", "NoModificationAllowedError");
|
||||
console.assert(e2.name === "NoModificationAllowedError");
|
||||
console.assert(e2.code === 7);
|
23
node_modules/node-domexception/.history/index_20210527212731.js
generated
vendored
Normal file
23
node_modules/node-domexception/.history/index_20210527212731.js
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/*! blob-to-buffer. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
||||
|
||||
const e1 = new DOMException("Something went wrong", "BadThingsError");
|
||||
console.assert(e1.name === "BadThingsError");
|
||||
console.assert(e1.code === 0);
|
||||
|
||||
const e2 = new DOMException("Another exciting error message", "NoModificationAllowedError");
|
||||
console.assert(e2.name === "NoModificationAllowedError");
|
||||
console.assert(e2.code === 2);
|
15
node_modules/node-domexception/.history/index_20210527212746.js
generated
vendored
Normal file
15
node_modules/node-domexception/.history/index_20210527212746.js
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/*! blob-to-buffer. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
16
node_modules/node-domexception/.history/index_20210527212900.js
generated
vendored
Normal file
16
node_modules/node-domexception/.history/index_20210527212900.js
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/*! blob-to-buffer. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) {
|
||||
console.log(err.code)
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
16
node_modules/node-domexception/.history/index_20210527213022.js
generated
vendored
Normal file
16
node_modules/node-domexception/.history/index_20210527213022.js
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/*! blob-to-buffer. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) {
|
||||
console.log(err.code, err.name, err.message)
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
16
node_modules/node-domexception/.history/index_20210527213822.js
generated
vendored
Normal file
16
node_modules/node-domexception/.history/index_20210527213822.js
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/*! node-DOMException. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
try { port.postMessage(ab, [ab, ab]) }
|
||||
catch (err) {
|
||||
console.log(err.code, err.name, err.message)
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
17
node_modules/node-domexception/.history/index_20210527213843.js
generated
vendored
Normal file
17
node_modules/node-domexception/.history/index_20210527213843.js
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
/*! node-DOMException. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
try {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
catch (err) {
|
||||
console.log(err.code, err.name, err.message)
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
17
node_modules/node-domexception/.history/index_20210527213852.js
generated
vendored
Normal file
17
node_modules/node-domexception/.history/index_20210527213852.js
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
/*! node-DOMException. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
try {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
} catch (err) {
|
||||
console.log(err.code, err.name, err.message)
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
16
node_modules/node-domexception/.history/index_20210527213910.js
generated
vendored
Normal file
16
node_modules/node-domexception/.history/index_20210527213910.js
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/*! node-DOMException. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
try {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
} catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
16
node_modules/node-domexception/.history/index_20210527214034.js
generated
vendored
Normal file
16
node_modules/node-domexception/.history/index_20210527214034.js
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
try {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
} catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
41
node_modules/node-domexception/.history/index_20210527214643.js
generated
vendored
Normal file
41
node_modules/node-domexception/.history/index_20210527214643.js
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
try {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
} catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
||||
|
||||
|
||||
const { MessageChannel } = require('worker_threads')
|
||||
|
||||
async function hello() {
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
}
|
||||
|
||||
hello().catch(err => {
|
||||
console.assert(err.name === 'DataCloneError')
|
||||
console.assert(err.code === 25)
|
||||
console.assert(err instanceof DOMException)
|
||||
})
|
||||
|
||||
const e1 = new DOMException('Something went wrong', 'BadThingsError')
|
||||
console.assert(e1.name === 'BadThingsError')
|
||||
console.assert(e1.code === 0)
|
||||
|
||||
const e2 = new DOMException('Another exciting error message', 'NoModificationAllowedError')
|
||||
console.assert(e2.name === 'NoModificationAllowedError')
|
||||
console.assert(e2.code === 7)
|
||||
|
||||
console.assert(DOMException.INUSE_ATTRIBUTE_ERR === 10)
|
41
node_modules/node-domexception/.history/index_20210527214654.js
generated
vendored
Normal file
41
node_modules/node-domexception/.history/index_20210527214654.js
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
try {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
} catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
||||
|
||||
|
||||
const { MessageChannel } = require('worker_threads')
|
||||
|
||||
async function hello() {
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
}
|
||||
|
||||
hello().catch(err => {
|
||||
console.assert(err.name === 'DataCloneError')
|
||||
console.assert(err.code === 21)
|
||||
console.assert(err instanceof DOMException)
|
||||
})
|
||||
|
||||
const e1 = new DOMException('Something went wrong', 'BadThingsError')
|
||||
console.assert(e1.name === 'BadThingsError')
|
||||
console.assert(e1.code === 0)
|
||||
|
||||
const e2 = new DOMException('Another exciting error message', 'NoModificationAllowedError')
|
||||
console.assert(e2.name === 'NoModificationAllowedError')
|
||||
console.assert(e2.code === 7)
|
||||
|
||||
console.assert(DOMException.INUSE_ATTRIBUTE_ERR === 10)
|
16
node_modules/node-domexception/.history/index_20210527214700.js
generated
vendored
Normal file
16
node_modules/node-domexception/.history/index_20210527214700.js
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
try {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
} catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
19
node_modules/node-domexception/.history/package_20210527203733.json
generated
vendored
Normal file
19
node_modules/node-domexception/.history/package_20210527203733.json
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "domexception",
|
||||
"version": "1.0.0",
|
||||
"description": "An implementation of the DOMException class from NodeJS",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jimmywarting/node-domexception.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jimmywarting/node-domexception/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jimmywarting/node-domexception#readme"
|
||||
}
|
16
node_modules/node-domexception/.history/package_20210527203825.json
generated
vendored
Normal file
16
node_modules/node-domexception/.history/package_20210527203825.json
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "node-domexception",
|
||||
"version": "1.0.0",
|
||||
"description": "An implementation of the DOMException class from NodeJS",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jimmywarting/node-domexception.git"
|
||||
},
|
||||
"author": "Jimmy Wärting",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jimmywarting/node-domexception/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jimmywarting/node-domexception#readme"
|
||||
}
|
19
node_modules/node-domexception/.history/package_20210527204621.json
generated
vendored
Normal file
19
node_modules/node-domexception/.history/package_20210527204621.json
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "node-domexception",
|
||||
"version": "1.0.0",
|
||||
"description": "An implementation of the DOMException class from NodeJS",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jimmywarting/node-domexception.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
},
|
||||
"author": "Jimmy Wärting",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jimmywarting/node-domexception/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jimmywarting/node-domexception#readme"
|
||||
}
|
25
node_modules/node-domexception/.history/package_20210527204913.json
generated
vendored
Normal file
25
node_modules/node-domexception/.history/package_20210527204913.json
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "node-domexception",
|
||||
"version": "1.0.0",
|
||||
"description": "An implementation of the DOMException class from NodeJS",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jimmywarting/node-domexception.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
},
|
||||
"author": "Jimmy Wärting",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jimmywarting/node-domexception/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jimmywarting/node-domexception#readme",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
}
|
||||
]
|
||||
}
|
25
node_modules/node-domexception/.history/package_20210527204925.json
generated
vendored
Normal file
25
node_modules/node-domexception/.history/package_20210527204925.json
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "node-domexception",
|
||||
"version": "1.0.0",
|
||||
"description": "An implementation of the DOMException class from NodeJS",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jimmywarting/node-domexception.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
},
|
||||
"author": "Jimmy Wärting",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jimmywarting/node-domexception/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jimmywarting/node-domexception#readme",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
}
|
||||
]
|
||||
}
|
29
node_modules/node-domexception/.history/package_20210527205145.json
generated
vendored
Normal file
29
node_modules/node-domexception/.history/package_20210527205145.json
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "node-domexception",
|
||||
"version": "1.0.0",
|
||||
"description": "An implementation of the DOMException class from NodeJS",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jimmywarting/node-domexception.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
},
|
||||
"author": "Jimmy Wärting",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jimmywarting/node-domexception/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jimmywarting/node-domexception#readme",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
]
|
||||
}
|
29
node_modules/node-domexception/.history/package_20210527205156.json
generated
vendored
Normal file
29
node_modules/node-domexception/.history/package_20210527205156.json
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "node-domexception",
|
||||
"version": "1.0.0",
|
||||
"description": "An implementation of the DOMException class from NodeJS",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jimmywarting/node-domexception.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
},
|
||||
"author": "Jimmy Wärting",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jimmywarting/node-domexception/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jimmywarting/node-domexception#readme",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
]
|
||||
}
|
0
node_modules/node-domexception/.history/test_20210527205603.js
generated
vendored
Normal file
0
node_modules/node-domexception/.history/test_20210527205603.js
generated
vendored
Normal file
3
node_modules/node-domexception/.history/test_20210527205957.js
generated
vendored
Normal file
3
node_modules/node-domexception/.history/test_20210527205957.js
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
require('./index.js')
|
||||
|
||||
console.log(DOMException.INDEX_SIZE_ERR)
|
3
node_modules/node-domexception/.history/test_20210527210021.js
generated
vendored
Normal file
3
node_modules/node-domexception/.history/test_20210527210021.js
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
const e = require('./index.js')
|
||||
|
||||
console.log(e.INDEX_SIZE_ERR)
|
21
node_modules/node-domexception/LICENSE
generated
vendored
Normal file
21
node_modules/node-domexception/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Jimmy Wärting
|
||||
|
||||
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.
|
46
node_modules/node-domexception/README.md
generated
vendored
Normal file
46
node_modules/node-domexception/README.md
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
# DOMException
|
||||
An implementation of the DOMException class from NodeJS
|
||||
|
||||
NodeJS has DOMException built in, but it's not globally available, and you can't require/import it from somewhere.
|
||||
|
||||
This package exposes the [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException) class that comes from NodeJS itself. (including all of the legacy codes)
|
||||
|
||||
<sub>(plz don't depend on this package in any other environment other than node >=10.5)</sub>
|
||||
|
||||
```js
|
||||
import DOMException from 'node-domexception'
|
||||
import { MessageChannel } from 'worker_threads'
|
||||
|
||||
async function hello() {
|
||||
const port = new MessageChannel().port1
|
||||
const ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
}
|
||||
|
||||
hello().catch(err => {
|
||||
console.assert(err.name === 'DataCloneError')
|
||||
console.assert(err.code === 25)
|
||||
console.assert(err instanceof DOMException)
|
||||
})
|
||||
|
||||
const e1 = new DOMException('Something went wrong', 'BadThingsError')
|
||||
console.assert(e1.name === 'BadThingsError')
|
||||
console.assert(e1.code === 0)
|
||||
|
||||
const e2 = new DOMException('Another exciting error message', 'NoModificationAllowedError')
|
||||
console.assert(e2.name === 'NoModificationAllowedError')
|
||||
console.assert(e2.code === 7)
|
||||
|
||||
console.assert(DOMException.INUSE_ATTRIBUTE_ERR === 10)
|
||||
```
|
||||
|
||||
# Background
|
||||
|
||||
The only possible way is to use some web-ish tools that have been introduced into NodeJS that throws a DOMException and catch the constructor. This is exactly what this package dose for you and exposes it.<br>
|
||||
This way you will have the same class that NodeJS has and you can check if the error is a instance of DOMException.<br>
|
||||
The instanceof check would not have worked with a custom class such as the DOMException provided by domenic which also is much larger in size since it has to re-construct the hole class from the ground up.
|
||||
|
||||
The DOMException is used in many places such as the Fetch API, File & Blobs, PostMessaging and more. <br>
|
||||
Why they decided to call it **DOM**, I don't know
|
||||
|
||||
Please consider sponsoring if you find this helpful
|
16
node_modules/node-domexception/index.js
generated
vendored
Normal file
16
node_modules/node-domexception/index.js
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
if (!globalThis.DOMException) {
|
||||
try {
|
||||
const { MessageChannel } = require('worker_threads'),
|
||||
port = new MessageChannel().port1,
|
||||
ab = new ArrayBuffer()
|
||||
port.postMessage(ab, [ab, ab])
|
||||
} catch (err) {
|
||||
err.constructor.name === 'DOMException' && (
|
||||
globalThis.DOMException = err.constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalThis.DOMException
|
29
node_modules/node-domexception/package.json
generated
vendored
Normal file
29
node_modules/node-domexception/package.json
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "node-domexception",
|
||||
"version": "1.0.0",
|
||||
"description": "An implementation of the DOMException class from NodeJS",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jimmywarting/node-domexception.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
},
|
||||
"author": "Jimmy Wärting",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jimmywarting/node-domexception/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jimmywarting/node-domexception#readme",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
]
|
||||
}
|
219
node_modules/node-fetch/@types/index.d.ts
generated
vendored
Normal file
219
node_modules/node-fetch/@types/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,219 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference lib="dom" />
|
||||
|
||||
import {RequestOptions} from 'http';
|
||||
import {FormData} from 'formdata-polyfill/esm.min.js';
|
||||
import {
|
||||
Blob,
|
||||
blobFrom,
|
||||
blobFromSync,
|
||||
File,
|
||||
fileFrom,
|
||||
fileFromSync
|
||||
} from 'fetch-blob/from.js';
|
||||
|
||||
type AbortSignal = {
|
||||
readonly aborted: boolean;
|
||||
|
||||
addEventListener: (type: 'abort', listener: (this: AbortSignal) => void) => void;
|
||||
removeEventListener: (type: 'abort', listener: (this: AbortSignal) => void) => void;
|
||||
};
|
||||
|
||||
export type HeadersInit = Headers | Record<string, string> | Iterable<readonly [string, string]> | Iterable<Iterable<string>>;
|
||||
|
||||
export {
|
||||
FormData,
|
||||
Blob,
|
||||
blobFrom,
|
||||
blobFromSync,
|
||||
File,
|
||||
fileFrom,
|
||||
fileFromSync
|
||||
};
|
||||
|
||||
/**
|
||||
* This Fetch API interface allows you to perform various actions on HTTP request and response headers.
|
||||
* These actions include retrieving, setting, adding to, and removing.
|
||||
* A Headers object has an associated header list, which is initially empty and consists of zero or more name and value pairs.
|
||||
* You can add to this using methods like append() (see Examples.)
|
||||
* In all methods of this interface, header names are matched by case-insensitive byte sequence.
|
||||
* */
|
||||
export class Headers {
|
||||
constructor(init?: HeadersInit);
|
||||
|
||||
append(name: string, value: string): void;
|
||||
delete(name: string): void;
|
||||
get(name: string): string | null;
|
||||
has(name: string): boolean;
|
||||
set(name: string, value: string): void;
|
||||
forEach(
|
||||
callbackfn: (value: string, key: string, parent: Headers) => void,
|
||||
thisArg?: any
|
||||
): void;
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[string, string]>;
|
||||
/**
|
||||
* Returns an iterator allowing to go through all key/value pairs contained in this object.
|
||||
*/
|
||||
entries(): IterableIterator<[string, string]>;
|
||||
/**
|
||||
* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object.
|
||||
*/
|
||||
keys(): IterableIterator<string>;
|
||||
/**
|
||||
* Returns an iterator allowing to go through all values of the key/value pairs contained in this object.
|
||||
*/
|
||||
values(): IterableIterator<string>;
|
||||
|
||||
/** Node-fetch extension */
|
||||
raw(): Record<string, string[]>;
|
||||
}
|
||||
|
||||
export interface RequestInit {
|
||||
/**
|
||||
* A BodyInit object or null to set request's body.
|
||||
*/
|
||||
body?: BodyInit | null;
|
||||
/**
|
||||
* A Headers object, an object literal, or an array of two-item arrays to set request's headers.
|
||||
*/
|
||||
headers?: HeadersInit;
|
||||
/**
|
||||
* A string to set request's method.
|
||||
*/
|
||||
method?: string;
|
||||
/**
|
||||
* A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect.
|
||||
*/
|
||||
redirect?: RequestRedirect;
|
||||
/**
|
||||
* An AbortSignal to set request's signal.
|
||||
*/
|
||||
signal?: AbortSignal | null;
|
||||
/**
|
||||
* A string whose value is a same-origin URL, "about:client", or the empty string, to set request’s referrer.
|
||||
*/
|
||||
referrer?: string;
|
||||
/**
|
||||
* A referrer policy to set request’s referrerPolicy.
|
||||
*/
|
||||
referrerPolicy?: ReferrerPolicy;
|
||||
|
||||
// Node-fetch extensions to the whatwg/fetch spec
|
||||
agent?: RequestOptions['agent'] | ((parsedUrl: URL) => RequestOptions['agent']);
|
||||
compress?: boolean;
|
||||
counter?: number;
|
||||
follow?: number;
|
||||
hostname?: string;
|
||||
port?: number;
|
||||
protocol?: string;
|
||||
size?: number;
|
||||
highWaterMark?: number;
|
||||
insecureHTTPParser?: boolean;
|
||||
}
|
||||
|
||||
export interface ResponseInit {
|
||||
headers?: HeadersInit;
|
||||
status?: number;
|
||||
statusText?: string;
|
||||
}
|
||||
|
||||
export type BodyInit =
|
||||
| Blob
|
||||
| Buffer
|
||||
| URLSearchParams
|
||||
| FormData
|
||||
| NodeJS.ReadableStream
|
||||
| string;
|
||||
declare class BodyMixin {
|
||||
constructor(body?: BodyInit, options?: {size?: number});
|
||||
|
||||
readonly body: NodeJS.ReadableStream | null;
|
||||
readonly bodyUsed: boolean;
|
||||
readonly size: number;
|
||||
|
||||
/** @deprecated Use `body.arrayBuffer()` instead. */
|
||||
buffer(): Promise<Buffer>;
|
||||
arrayBuffer(): Promise<ArrayBuffer>;
|
||||
formData(): Promise<FormData>;
|
||||
blob(): Promise<Blob>;
|
||||
json(): Promise<unknown>;
|
||||
text(): Promise<string>;
|
||||
}
|
||||
|
||||
// `Body` must not be exported as a class since it's not exported from the JavaScript code.
|
||||
export interface Body extends Pick<BodyMixin, keyof BodyMixin> {}
|
||||
|
||||
export type RequestRedirect = 'error' | 'follow' | 'manual';
|
||||
export type ReferrerPolicy = '' | 'no-referrer' | 'no-referrer-when-downgrade' | 'same-origin' | 'origin' | 'strict-origin' | 'origin-when-cross-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
|
||||
export type RequestInfo = string | Request;
|
||||
export class Request extends BodyMixin {
|
||||
constructor(input: RequestInfo, init?: RequestInit);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
readonly headers: Headers;
|
||||
/**
|
||||
* Returns request's HTTP method, which is "GET" by default.
|
||||
*/
|
||||
readonly method: string;
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
readonly redirect: RequestRedirect;
|
||||
/**
|
||||
* Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler.
|
||||
*/
|
||||
readonly signal: AbortSignal;
|
||||
/**
|
||||
* Returns the URL of request as a string.
|
||||
*/
|
||||
readonly url: string;
|
||||
/**
|
||||
* A string whose value is a same-origin URL, "about:client", or the empty string, to set request’s referrer.
|
||||
*/
|
||||
readonly referrer: string;
|
||||
/**
|
||||
* A referrer policy to set request’s referrerPolicy.
|
||||
*/
|
||||
readonly referrerPolicy: ReferrerPolicy;
|
||||
clone(): Request;
|
||||
}
|
||||
|
||||
type ResponseType = 'basic' | 'cors' | 'default' | 'error' | 'opaque' | 'opaqueredirect';
|
||||
|
||||
export class Response extends BodyMixin {
|
||||
constructor(body?: BodyInit | null, init?: ResponseInit);
|
||||
|
||||
readonly headers: Headers;
|
||||
readonly ok: boolean;
|
||||
readonly redirected: boolean;
|
||||
readonly status: number;
|
||||
readonly statusText: string;
|
||||
readonly type: ResponseType;
|
||||
readonly url: string;
|
||||
clone(): Response;
|
||||
|
||||
static error(): Response;
|
||||
static redirect(url: string, status?: number): Response;
|
||||
}
|
||||
|
||||
export class FetchError extends Error {
|
||||
constructor(message: string, type: string, systemError?: Record<string, unknown>);
|
||||
|
||||
name: 'FetchError';
|
||||
[Symbol.toStringTag]: 'FetchError';
|
||||
type: string;
|
||||
code?: string;
|
||||
errno?: string;
|
||||
}
|
||||
|
||||
export class AbortError extends Error {
|
||||
type: string;
|
||||
name: 'AbortError';
|
||||
[Symbol.toStringTag]: 'AbortError';
|
||||
}
|
||||
|
||||
export function isRedirect(code: number): boolean;
|
||||
export default function fetch(url: RequestInfo, init?: RequestInit): Promise<Response>;
|
22
node_modules/node-fetch/LICENSE.md
generated
vendored
Normal file
22
node_modules/node-fetch/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 - 2020 Node Fetch Team
|
||||
|
||||
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.
|
||||
|
871
node_modules/node-fetch/README.md
generated
vendored
Normal file
871
node_modules/node-fetch/README.md
generated
vendored
Normal file
@ -0,0 +1,871 @@
|
||||
<div align="center">
|
||||
<img src="docs/media/Banner.svg" alt="Node Fetch"/>
|
||||
<br>
|
||||
<p>A light-weight module that brings <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch API</a> to Node.js.</p>
|
||||
<a href="https://github.com/node-fetch/node-fetch/actions"><img src="https://github.com/node-fetch/node-fetch/workflows/CI/badge.svg?branch=master" alt="Build status"></a>
|
||||
<a href="https://coveralls.io/github/node-fetch/node-fetch"><img src="https://img.shields.io/coveralls/github/node-fetch/node-fetch" alt="Coverage status"></a>
|
||||
<a href="https://packagephobia.now.sh/result?p=node-fetch"><img src="https://badgen.net/packagephobia/install/node-fetch" alt="Current version"></a>
|
||||
<a href="https://www.npmjs.com/package/node-fetch"><img src="https://img.shields.io/npm/v/node-fetch" alt="Install size"></a>
|
||||
<a href="https://github.com/sindresorhus/awesome-nodejs"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Node.js"></a>
|
||||
<a href="https://discord.gg/Zxbndcm"><img src="https://img.shields.io/discord/619915844268326952?color=%237289DA&label=Discord" alt="Discord"></a>
|
||||
<br>
|
||||
<br>
|
||||
<b>Consider supporting us on our Open Collective:</b>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://opencollective.com/node-fetch"><img src="https://opencollective.com/node-fetch/donate/button.png?color=blue" alt="Open Collective"></a>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
**You might be looking for the [v2 docs](https://github.com/node-fetch/node-fetch/tree/2.x#readme)**
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [Motivation](#motivation)
|
||||
- [Features](#features)
|
||||
- [Difference from client-side fetch](#difference-from-client-side-fetch)
|
||||
- [Installation](#installation)
|
||||
- [Loading and configuring the module](#loading-and-configuring-the-module)
|
||||
- [Upgrading](#upgrading)
|
||||
- [Common Usage](#common-usage)
|
||||
- [Plain text or HTML](#plain-text-or-html)
|
||||
- [JSON](#json)
|
||||
- [Simple Post](#simple-post)
|
||||
- [Post with JSON](#post-with-json)
|
||||
- [Post with form parameters](#post-with-form-parameters)
|
||||
- [Handling exceptions](#handling-exceptions)
|
||||
- [Handling client and server errors](#handling-client-and-server-errors)
|
||||
- [Handling cookies](#handling-cookies)
|
||||
- [Advanced Usage](#advanced-usage)
|
||||
- [Streams](#streams)
|
||||
- [Accessing Headers and other Metadata](#accessing-headers-and-other-metadata)
|
||||
- [Extract Set-Cookie Header](#extract-set-cookie-header)
|
||||
- [Post data using a file](#post-data-using-a-file)
|
||||
- [Request cancellation with AbortSignal](#request-cancellation-with-abortsignal)
|
||||
- [API](#api)
|
||||
- [fetch(url[, options])](#fetchurl-options)
|
||||
- [Options](#options)
|
||||
- [Default Headers](#default-headers)
|
||||
- [Custom Agent](#custom-agent)
|
||||
- [Custom highWaterMark](#custom-highwatermark)
|
||||
- [Insecure HTTP Parser](#insecure-http-parser)
|
||||
- [Class: Request](#class-request)
|
||||
- [new Request(input[, options])](#new-requestinput-options)
|
||||
- [Class: Response](#class-response)
|
||||
- [new Response([body[, options]])](#new-responsebody-options)
|
||||
- [response.ok](#responseok)
|
||||
- [response.redirected](#responseredirected)
|
||||
- [response.type](#responsetype)
|
||||
- [Class: Headers](#class-headers)
|
||||
- [new Headers([init])](#new-headersinit)
|
||||
- [Interface: Body](#interface-body)
|
||||
- [body.body](#bodybody)
|
||||
- [body.bodyUsed](#bodybodyused)
|
||||
- [body.arrayBuffer()](#bodyarraybuffer)
|
||||
- [body.blob()](#bodyblob)
|
||||
- [body.formData()](#formdata)
|
||||
- [body.json()](#bodyjson)
|
||||
- [body.text()](#bodytext)
|
||||
- [Class: FetchError](#class-fetcherror)
|
||||
- [Class: AbortError](#class-aborterror)
|
||||
- [TypeScript](#typescript)
|
||||
- [Acknowledgement](#acknowledgement)
|
||||
- [Team](#team)
|
||||
- [Former](#former)
|
||||
- [License](#license)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
## Motivation
|
||||
|
||||
Instead of implementing `XMLHttpRequest` in Node.js to run browser-specific [Fetch polyfill](https://github.com/github/fetch), why not go from native `http` to `fetch` API directly? Hence, `node-fetch`, minimal code for a `window.fetch` compatible API on Node.js runtime.
|
||||
|
||||
See Jason Miller's [isomorphic-unfetch](https://www.npmjs.com/package/isomorphic-unfetch) or Leonardo Quixada's [cross-fetch](https://github.com/lquixada/cross-fetch) for isomorphic usage (exports `node-fetch` for server-side, `whatwg-fetch` for client-side).
|
||||
|
||||
## Features
|
||||
|
||||
- Stay consistent with `window.fetch` API.
|
||||
- Make conscious trade-off when following [WHATWG fetch spec][whatwg-fetch] and [stream spec](https://streams.spec.whatwg.org/) implementation details, document known differences.
|
||||
- Use native promise and async functions.
|
||||
- Use native Node streams for body, on both request and response.
|
||||
- Decode content encoding (gzip/deflate/brotli) properly, and convert string output (such as `res.text()` and `res.json()`) to UTF-8 automatically.
|
||||
- Useful extensions such as redirect limit, response size limit, [explicit errors][error-handling.md] for troubleshooting.
|
||||
|
||||
## Difference from client-side fetch
|
||||
|
||||
- See known differences:
|
||||
- [As of v3.x](docs/v3-LIMITS.md)
|
||||
- [As of v2.x](docs/v2-LIMITS.md)
|
||||
- If you happen to use a missing feature that `window.fetch` offers, feel free to open an issue.
|
||||
- Pull requests are welcomed too!
|
||||
|
||||
## Installation
|
||||
|
||||
Current stable release (`3.x`) requires at least Node.js 12.20.0.
|
||||
|
||||
```sh
|
||||
npm install node-fetch
|
||||
```
|
||||
|
||||
## Loading and configuring the module
|
||||
|
||||
### ES Modules (ESM)
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
```
|
||||
|
||||
### CommonJS
|
||||
|
||||
`node-fetch` from v3 is an ESM-only module - you are not able to import it with `require()`.
|
||||
|
||||
If you cannot switch to ESM, please use v2 which remains compatible with CommonJS. Critical bug fixes will continue to be published for v2.
|
||||
|
||||
```sh
|
||||
npm install node-fetch@2
|
||||
```
|
||||
|
||||
Alternatively, you can use the async `import()` function from CommonJS to load `node-fetch` asynchronously:
|
||||
|
||||
```js
|
||||
// mod.cjs
|
||||
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
|
||||
```
|
||||
|
||||
### Providing global access
|
||||
|
||||
To use `fetch()` without importing it, you can patch the `global` object in node:
|
||||
|
||||
```js
|
||||
// fetch-polyfill.js
|
||||
import fetch, {
|
||||
Blob,
|
||||
blobFrom,
|
||||
blobFromSync,
|
||||
File,
|
||||
fileFrom,
|
||||
fileFromSync,
|
||||
FormData,
|
||||
Headers,
|
||||
Request,
|
||||
Response,
|
||||
} from 'node-fetch'
|
||||
|
||||
if (!globalThis.fetch) {
|
||||
globalThis.fetch = fetch
|
||||
globalThis.Headers = Headers
|
||||
globalThis.Request = Request
|
||||
globalThis.Response = Response
|
||||
}
|
||||
|
||||
// index.js
|
||||
import './fetch-polyfill'
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
Using an old version of node-fetch? Check out the following files:
|
||||
|
||||
- [2.x to 3.x upgrade guide](docs/v3-UPGRADE-GUIDE.md)
|
||||
- [1.x to 2.x upgrade guide](docs/v2-UPGRADE-GUIDE.md)
|
||||
- [Changelog](https://github.com/node-fetch/node-fetch/releases)
|
||||
|
||||
## Common Usage
|
||||
|
||||
NOTE: The documentation below is up-to-date with `3.x` releases, if you are using an older version, please check how to [upgrade](#upgrading).
|
||||
|
||||
### Plain text or HTML
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const response = await fetch('https://github.com/');
|
||||
const body = await response.text();
|
||||
|
||||
console.log(body);
|
||||
```
|
||||
|
||||
### JSON
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const response = await fetch('https://api.github.com/users/github');
|
||||
const data = await response.json();
|
||||
|
||||
console.log(data);
|
||||
```
|
||||
|
||||
### Simple Post
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const response = await fetch('https://httpbin.org/post', {method: 'POST', body: 'a=1'});
|
||||
const data = await response.json();
|
||||
|
||||
console.log(data);
|
||||
```
|
||||
|
||||
### Post with JSON
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const body = {a: 1};
|
||||
|
||||
const response = await fetch('https://httpbin.org/post', {
|
||||
method: 'post',
|
||||
body: JSON.stringify(body),
|
||||
headers: {'Content-Type': 'application/json'}
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
console.log(data);
|
||||
```
|
||||
|
||||
### Post with form parameters
|
||||
|
||||
`URLSearchParams` is available on the global object in Node.js as of v10.0.0. See [official documentation](https://nodejs.org/api/url.html#url_class_urlsearchparams) for more usage methods.
|
||||
|
||||
NOTE: The `Content-Type` header is only set automatically to `x-www-form-urlencoded` when an instance of `URLSearchParams` is given as such:
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('a', 1);
|
||||
|
||||
const response = await fetch('https://httpbin.org/post', {method: 'POST', body: params});
|
||||
const data = await response.json();
|
||||
|
||||
console.log(data);
|
||||
```
|
||||
|
||||
### Handling exceptions
|
||||
|
||||
NOTE: 3xx-5xx responses are _NOT_ exceptions, and should be handled in `then()`, see the next section.
|
||||
|
||||
Wrapping the fetch function into a `try/catch` block will catch _all_ exceptions, such as errors originating from node core libraries, like network errors, and operational errors which are instances of FetchError. See the [error handling document][error-handling.md] for more details.
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
try {
|
||||
await fetch('https://domain.invalid/');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
```
|
||||
|
||||
### Handling client and server errors
|
||||
|
||||
It is common to create a helper function to check that the response contains no client (4xx) or server (5xx) error responses:
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
class HTTPResponseError extends Error {
|
||||
constructor(response, ...args) {
|
||||
super(`HTTP Error Response: ${response.status} ${response.statusText}`, ...args);
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
|
||||
const checkStatus = response => {
|
||||
if (response.ok) {
|
||||
// response.status >= 200 && response.status < 300
|
||||
return response;
|
||||
} else {
|
||||
throw new HTTPResponseError(response);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch('https://httpbin.org/status/400');
|
||||
|
||||
try {
|
||||
checkStatus(response);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
const errorBody = await error.response.text();
|
||||
console.error(`Error body: ${errorBody}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Handling cookies
|
||||
|
||||
Cookies are not stored by default. However, cookies can be extracted and passed by manipulating request and response headers. See [Extract Set-Cookie Header](#extract-set-cookie-header) for details.
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Streams
|
||||
|
||||
The "Node.js way" is to use streams when possible. You can pipe `res.body` to another stream. This example uses [stream.pipeline](https://nodejs.org/api/stream.html#stream_stream_pipeline_streams_callback) to attach stream error handlers and wait for the download to complete.
|
||||
|
||||
```js
|
||||
import {createWriteStream} from 'node:fs';
|
||||
import {pipeline} from 'node:stream';
|
||||
import {promisify} from 'node:util'
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const streamPipeline = promisify(pipeline);
|
||||
|
||||
const response = await fetch('https://github.githubassets.com/images/modules/logos_page/Octocat.png');
|
||||
|
||||
if (!response.ok) throw new Error(`unexpected response ${response.statusText}`);
|
||||
|
||||
await streamPipeline(response.body, createWriteStream('./octocat.png'));
|
||||
```
|
||||
|
||||
In Node.js 14 you can also use async iterators to read `body`; however, be careful to catch
|
||||
errors -- the longer a response runs, the more likely it is to encounter an error.
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const response = await fetch('https://httpbin.org/stream/3');
|
||||
|
||||
try {
|
||||
for await (const chunk of response.body) {
|
||||
console.dir(JSON.parse(chunk.toString()));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
```
|
||||
|
||||
In Node.js 12 you can also use async iterators to read `body`; however, async iterators with streams
|
||||
did not mature until Node.js 14, so you need to do some extra work to ensure you handle errors
|
||||
directly from the stream and wait on it response to fully close.
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const read = async body => {
|
||||
let error;
|
||||
body.on('error', err => {
|
||||
error = err;
|
||||
});
|
||||
|
||||
for await (const chunk of body) {
|
||||
console.dir(JSON.parse(chunk.toString()));
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
body.on('close', () => {
|
||||
error ? reject(error) : resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('https://httpbin.org/stream/3');
|
||||
await read(response.body);
|
||||
} catch (err) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
```
|
||||
|
||||
### Accessing Headers and other Metadata
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const response = await fetch('https://github.com/');
|
||||
|
||||
console.log(response.ok);
|
||||
console.log(response.status);
|
||||
console.log(response.statusText);
|
||||
console.log(response.headers.raw());
|
||||
console.log(response.headers.get('content-type'));
|
||||
```
|
||||
|
||||
### Extract Set-Cookie Header
|
||||
|
||||
Unlike browsers, you can access raw `Set-Cookie` headers manually using `Headers.raw()`. This is a `node-fetch` only API.
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const response = await fetch('https://example.com');
|
||||
|
||||
// Returns an array of values, instead of a string of comma-separated values
|
||||
console.log(response.headers.raw()['set-cookie']);
|
||||
```
|
||||
|
||||
### Post data using a file
|
||||
|
||||
```js
|
||||
import fetch {
|
||||
Blob,
|
||||
blobFrom,
|
||||
blobFromSync,
|
||||
File,
|
||||
fileFrom,
|
||||
fileFromSync,
|
||||
} from 'node-fetch'
|
||||
|
||||
const mimetype = 'text/plain'
|
||||
const blob = fileFromSync('./input.txt', mimetype)
|
||||
const url = 'https://httpbin.org/post'
|
||||
|
||||
const response = await fetch(url, { method: 'POST', body: blob })
|
||||
const data = await response.json()
|
||||
|
||||
console.log(data)
|
||||
```
|
||||
|
||||
node-fetch comes with a spec-compliant [FormData] implementations for posting
|
||||
multipart/form-data payloads
|
||||
|
||||
```js
|
||||
import fetch, { FormData, File, fileFrom } from 'node-fetch'
|
||||
|
||||
const httpbin = 'https://httpbin.org/post'
|
||||
const formData = new FormData()
|
||||
const binary = new Uint8Array([ 97, 98, 99 ])
|
||||
const abc = new File([binary], 'abc.txt', { type: 'text/plain' })
|
||||
|
||||
formData.set('greeting', 'Hello, world!')
|
||||
formData.set('file-upload', abc, 'new name.txt')
|
||||
|
||||
const response = await fetch(httpbin, { method: 'POST', body: formData })
|
||||
const data = await response.json()
|
||||
|
||||
console.log(data)
|
||||
```
|
||||
|
||||
If you for some reason need to post a stream coming from any arbitrary place,
|
||||
then you can append a [Blob] or a [File] look-a-like item.
|
||||
|
||||
The minium requirement is that it has:
|
||||
1. A `Symbol.toStringTag` getter or property that is either `Blob` or `File`
|
||||
2. A known size.
|
||||
3. And either a `stream()` method or a `arrayBuffer()` method that returns a ArrayBuffer.
|
||||
|
||||
The `stream()` must return any async iterable object as long as it yields Uint8Array (or Buffer)
|
||||
so Node.Readable streams and whatwg streams works just fine.
|
||||
|
||||
```js
|
||||
formData.append('upload', {
|
||||
[Symbol.toStringTag]: 'Blob',
|
||||
size: 3,
|
||||
*stream() {
|
||||
yield new Uint8Array([97, 98, 99])
|
||||
},
|
||||
arrayBuffer() {
|
||||
return new Uint8Array([97, 98, 99]).buffer
|
||||
}
|
||||
}, 'abc.txt')
|
||||
```
|
||||
|
||||
### Request cancellation with AbortSignal
|
||||
|
||||
You may cancel requests with `AbortController`. A suggested implementation is [`abort-controller`](https://www.npmjs.com/package/abort-controller).
|
||||
|
||||
An example of timing out a request after 150ms could be achieved as the following:
|
||||
|
||||
```js
|
||||
import fetch, { AbortError } from 'node-fetch';
|
||||
|
||||
// AbortController was added in node v14.17.0 globally
|
||||
const AbortController = globalThis.AbortController || await import('abort-controller')
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => {
|
||||
controller.abort();
|
||||
}, 150);
|
||||
|
||||
try {
|
||||
const response = await fetch('https://example.com', {signal: controller.signal});
|
||||
const data = await response.json();
|
||||
} catch (error) {
|
||||
if (error instanceof AbortError) {
|
||||
console.log('request was aborted');
|
||||
}
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
```
|
||||
|
||||
See [test cases](https://github.com/node-fetch/node-fetch/blob/master/test/) for more examples.
|
||||
|
||||
## API
|
||||
|
||||
### fetch(url[, options])
|
||||
|
||||
- `url` A string representing the URL for fetching
|
||||
- `options` [Options](#fetch-options) for the HTTP(S) request
|
||||
- Returns: <code>Promise<[Response](#class-response)></code>
|
||||
|
||||
Perform an HTTP(S) fetch.
|
||||
|
||||
`url` should be an absolute URL, such as `https://example.com/`. A path-relative URL (`/file/under/root`) or protocol-relative URL (`//can-be-http-or-https.com/`) will result in a rejected `Promise`.
|
||||
|
||||
<a id="fetch-options"></a>
|
||||
|
||||
### Options
|
||||
|
||||
The default values are shown after each option key.
|
||||
|
||||
```js
|
||||
{
|
||||
// These properties are part of the Fetch Standard
|
||||
method: 'GET',
|
||||
headers: {}, // Request headers. format is the identical to that accepted by the Headers constructor (see below)
|
||||
body: null, // Request body. can be null, or a Node.js Readable stream
|
||||
redirect: 'follow', // Set to `manual` to extract redirect headers, `error` to reject redirect
|
||||
signal: null, // Pass an instance of AbortSignal to optionally abort requests
|
||||
|
||||
// The following properties are node-fetch extensions
|
||||
follow: 20, // maximum redirect count. 0 to not follow redirect
|
||||
compress: true, // support gzip/deflate content encoding. false to disable
|
||||
size: 0, // maximum response body size in bytes. 0 to disable
|
||||
agent: null, // http(s).Agent instance or function that returns an instance (see below)
|
||||
highWaterMark: 16384, // the maximum number of bytes to store in the internal buffer before ceasing to read from the underlying resource.
|
||||
insecureHTTPParser: false // Use an insecure HTTP parser that accepts invalid HTTP headers when `true`.
|
||||
}
|
||||
```
|
||||
|
||||
#### Default Headers
|
||||
|
||||
If no values are set, the following request headers will be sent automatically:
|
||||
|
||||
| Header | Value |
|
||||
| ------------------- | ------------------------------------------------------ |
|
||||
| `Accept-Encoding` | `gzip, deflate, br` (when `options.compress === true`) |
|
||||
| `Accept` | `*/*` |
|
||||
| `Connection` | `close` _(when no `options.agent` is present)_ |
|
||||
| `Content-Length` | _(automatically calculated, if possible)_ |
|
||||
| `Host` | _(host and port information from the target URI)_ |
|
||||
| `Transfer-Encoding` | `chunked` _(when `req.body` is a stream)_ |
|
||||
| `User-Agent` | `node-fetch` |
|
||||
|
||||
|
||||
Note: when `body` is a `Stream`, `Content-Length` is not set automatically.
|
||||
|
||||
#### Custom Agent
|
||||
|
||||
The `agent` option allows you to specify networking related options which are out of the scope of Fetch, including and not limited to the following:
|
||||
|
||||
- Support self-signed certificate
|
||||
- Use only IPv4 or IPv6
|
||||
- Custom DNS Lookup
|
||||
|
||||
See [`http.Agent`](https://nodejs.org/api/http.html#http_new_agent_options) for more information.
|
||||
|
||||
In addition, the `agent` option accepts a function that returns `http`(s)`.Agent` instance given current [URL](https://nodejs.org/api/url.html), this is useful during a redirection chain across HTTP and HTTPS protocol.
|
||||
|
||||
```js
|
||||
import http from 'node:http';
|
||||
import https from 'node:https';
|
||||
|
||||
const httpAgent = new http.Agent({
|
||||
keepAlive: true
|
||||
});
|
||||
const httpsAgent = new https.Agent({
|
||||
keepAlive: true
|
||||
});
|
||||
|
||||
const options = {
|
||||
agent: function(_parsedURL) {
|
||||
if (_parsedURL.protocol == 'http:') {
|
||||
return httpAgent;
|
||||
} else {
|
||||
return httpsAgent;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<a id="custom-highWaterMark"></a>
|
||||
|
||||
#### Custom highWaterMark
|
||||
|
||||
Stream on Node.js have a smaller internal buffer size (16kB, aka `highWaterMark`) from client-side browsers (>1MB, not consistent across browsers). Because of that, when you are writing an isomorphic app and using `res.clone()`, it will hang with large response in Node.
|
||||
|
||||
The recommended way to fix this problem is to resolve cloned response in parallel:
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const response = await fetch('https://example.com');
|
||||
const r1 = response.clone();
|
||||
|
||||
const results = await Promise.all([response.json(), r1.text()]);
|
||||
|
||||
console.log(results[0]);
|
||||
console.log(results[1]);
|
||||
```
|
||||
|
||||
If for some reason you don't like the solution above, since `3.x` you are able to modify the `highWaterMark` option:
|
||||
|
||||
```js
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const response = await fetch('https://example.com', {
|
||||
// About 1MB
|
||||
highWaterMark: 1024 * 1024
|
||||
});
|
||||
|
||||
const result = await res.clone().arrayBuffer();
|
||||
console.dir(result);
|
||||
```
|
||||
|
||||
#### Insecure HTTP Parser
|
||||
|
||||
Passed through to the `insecureHTTPParser` option on http(s).request. See [`http.request`](https://nodejs.org/api/http.html#http_http_request_url_options_callback) for more information.
|
||||
|
||||
#### Manual Redirect
|
||||
|
||||
The `redirect: 'manual'` option for node-fetch is different from the browser & specification, which
|
||||
results in an [opaque-redirect filtered response](https://fetch.spec.whatwg.org/#concept-filtered-response-opaque-redirect).
|
||||
node-fetch gives you the typical [basic filtered response](https://fetch.spec.whatwg.org/#concept-filtered-response-basic) instead.
|
||||
|
||||
```js
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
const response = await fetch('https://httpbin.org/status/301', { redirect: 'manual' });
|
||||
|
||||
if (response.status === 301 || response.status === 302) {
|
||||
const locationURL = new URL(response.headers.get('location'), response.url);
|
||||
const response2 = await fetch(locationURL, { redirect: 'manual' });
|
||||
console.dir(response2);
|
||||
}
|
||||
```
|
||||
|
||||
<a id="class-request"></a>
|
||||
|
||||
### Class: Request
|
||||
|
||||
An HTTP(S) request containing information about URL, method, headers, and the body. This class implements the [Body](#iface-body) interface.
|
||||
|
||||
Due to the nature of Node.js, the following properties are not implemented at this moment:
|
||||
|
||||
- `type`
|
||||
- `destination`
|
||||
- `mode`
|
||||
- `credentials`
|
||||
- `cache`
|
||||
- `integrity`
|
||||
- `keepalive`
|
||||
|
||||
The following node-fetch extension properties are provided:
|
||||
|
||||
- `follow`
|
||||
- `compress`
|
||||
- `counter`
|
||||
- `agent`
|
||||
- `highWaterMark`
|
||||
|
||||
See [options](#fetch-options) for exact meaning of these extensions.
|
||||
|
||||
#### new Request(input[, options])
|
||||
|
||||
<small>_(spec-compliant)_</small>
|
||||
|
||||
- `input` A string representing a URL, or another `Request` (which will be cloned)
|
||||
- `options` [Options](#fetch-options) for the HTTP(S) request
|
||||
|
||||
Constructs a new `Request` object. The constructor is identical to that in the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request).
|
||||
|
||||
In most cases, directly `fetch(url, options)` is simpler than creating a `Request` object.
|
||||
|
||||
<a id="class-response"></a>
|
||||
|
||||
### Class: Response
|
||||
|
||||
An HTTP(S) response. This class implements the [Body](#iface-body) interface.
|
||||
|
||||
The following properties are not implemented in node-fetch at this moment:
|
||||
|
||||
- `trailer`
|
||||
|
||||
#### new Response([body[, options]])
|
||||
|
||||
<small>_(spec-compliant)_</small>
|
||||
|
||||
- `body` A `String` or [`Readable` stream][node-readable]
|
||||
- `options` A [`ResponseInit`][response-init] options dictionary
|
||||
|
||||
Constructs a new `Response` object. The constructor is identical to that in the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response).
|
||||
|
||||
Because Node.js does not implement service workers (for which this class was designed), one rarely has to construct a `Response` directly.
|
||||
|
||||
#### response.ok
|
||||
|
||||
<small>_(spec-compliant)_</small>
|
||||
|
||||
Convenience property representing if the request ended normally. Will evaluate to true if the response status was greater than or equal to 200 but smaller than 300.
|
||||
|
||||
#### response.redirected
|
||||
|
||||
<small>_(spec-compliant)_</small>
|
||||
|
||||
Convenience property representing if the request has been redirected at least once. Will evaluate to true if the internal redirect counter is greater than 0.
|
||||
|
||||
#### response.type
|
||||
|
||||
<small>_(deviation from spec)_</small>
|
||||
|
||||
Convenience property representing the response's type. node-fetch only supports `'default'` and `'error'` and does not make use of [filtered responses](https://fetch.spec.whatwg.org/#concept-filtered-response).
|
||||
|
||||
<a id="class-headers"></a>
|
||||
|
||||
### Class: Headers
|
||||
|
||||
This class allows manipulating and iterating over a set of HTTP headers. All methods specified in the [Fetch Standard][whatwg-fetch] are implemented.
|
||||
|
||||
#### new Headers([init])
|
||||
|
||||
<small>_(spec-compliant)_</small>
|
||||
|
||||
- `init` Optional argument to pre-fill the `Headers` object
|
||||
|
||||
Construct a new `Headers` object. `init` can be either `null`, a `Headers` object, an key-value map object or any iterable object.
|
||||
|
||||
```js
|
||||
// Example adapted from https://fetch.spec.whatwg.org/#example-headers-class
|
||||
import {Headers} from 'node-fetch';
|
||||
|
||||
const meta = {
|
||||
'Content-Type': 'text/xml'
|
||||
};
|
||||
const headers = new Headers(meta);
|
||||
|
||||
// The above is equivalent to
|
||||
const meta = [['Content-Type', 'text/xml']];
|
||||
const headers = new Headers(meta);
|
||||
|
||||
// You can in fact use any iterable objects, like a Map or even another Headers
|
||||
const meta = new Map();
|
||||
meta.set('Content-Type', 'text/xml');
|
||||
const headers = new Headers(meta);
|
||||
const copyOfHeaders = new Headers(headers);
|
||||
```
|
||||
|
||||
<a id="iface-body"></a>
|
||||
|
||||
### Interface: Body
|
||||
|
||||
`Body` is an abstract interface with methods that are applicable to both `Request` and `Response` classes.
|
||||
|
||||
#### body.body
|
||||
|
||||
<small>_(deviation from spec)_</small>
|
||||
|
||||
- Node.js [`Readable` stream][node-readable]
|
||||
|
||||
Data are encapsulated in the `Body` object. Note that while the [Fetch Standard][whatwg-fetch] requires the property to always be a WHATWG `ReadableStream`, in node-fetch it is a Node.js [`Readable` stream][node-readable].
|
||||
|
||||
#### body.bodyUsed
|
||||
|
||||
<small>_(spec-compliant)_</small>
|
||||
|
||||
- `Boolean`
|
||||
|
||||
A boolean property for if this body has been consumed. Per the specs, a consumed body cannot be used again.
|
||||
|
||||
#### body.arrayBuffer()
|
||||
|
||||
#### body.formData()
|
||||
|
||||
#### body.blob()
|
||||
|
||||
#### body.json()
|
||||
|
||||
#### body.text()
|
||||
|
||||
`fetch` comes with methods to parse `multipart/form-data` payloads as well as
|
||||
`x-www-form-urlencoded` bodies using `.formData()` this comes from the idea that
|
||||
Service Worker can intercept such messages before it's sent to the server to
|
||||
alter them. This is useful for anybody building a server so you can use it to
|
||||
parse & consume payloads.
|
||||
|
||||
<details>
|
||||
<summary>Code example</summary>
|
||||
|
||||
```js
|
||||
import http from 'node:http'
|
||||
import { Response } from 'node-fetch'
|
||||
|
||||
http.createServer(async function (req, res) {
|
||||
const formData = await new Response(req, {
|
||||
headers: req.headers // Pass along the boundary value
|
||||
}).formData()
|
||||
const allFields = [...formData]
|
||||
|
||||
const file = formData.get('uploaded-files')
|
||||
const arrayBuffer = await file.arrayBuffer()
|
||||
const text = await file.text()
|
||||
const whatwgReadableStream = file.stream()
|
||||
|
||||
// other was to consume the request could be to do:
|
||||
const json = await new Response(req).json()
|
||||
const text = await new Response(req).text()
|
||||
const arrayBuffer = await new Response(req).arrayBuffer()
|
||||
const blob = await new Response(req, {
|
||||
headers: req.headers // So that `type` inherits `Content-Type`
|
||||
}.blob()
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<a id="class-fetcherror"></a>
|
||||
|
||||
### Class: FetchError
|
||||
|
||||
<small>_(node-fetch extension)_</small>
|
||||
|
||||
An operational error in the fetching process. See [ERROR-HANDLING.md][] for more info.
|
||||
|
||||
<a id="class-aborterror"></a>
|
||||
|
||||
### Class: AbortError
|
||||
|
||||
<small>_(node-fetch extension)_</small>
|
||||
|
||||
An Error thrown when the request is aborted in response to an `AbortSignal`'s `abort` event. It has a `name` property of `AbortError`. See [ERROR-HANDLING.MD][] for more info.
|
||||
|
||||
## TypeScript
|
||||
|
||||
**Since `3.x` types are bundled with `node-fetch`, so you don't need to install any additional packages.**
|
||||
|
||||
For older versions please use the type definitions from [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped):
|
||||
|
||||
```sh
|
||||
npm install --save-dev @types/node-fetch@2.x
|
||||
```
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
Thanks to [github/fetch](https://github.com/github/fetch) for providing a solid implementation reference.
|
||||
|
||||
## Team
|
||||
|
||||
| [](https://github.com/bitinn) | [](https://github.com/jimmywarting) | [](https://github.com/xxczaki) | [](https://github.com/Richienb) | [](https://github.com/gr2m) |
|
||||
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
|
||||
| [David Frank](https://bitinn.net/) | [Jimmy Wärting](https://jimmy.warting.se/) | [Antoni Kepinski](https://kepinski.ch) | [Richie Bendall](https://www.richie-bendall.ml/) | [Gregor Martynus](https://twitter.com/gr2m) |
|
||||
|
||||
###### Former
|
||||
|
||||
- [Timothy Gu](https://github.com/timothygu)
|
||||
- [Jared Kantrowitz](https://github.com/jkantr)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE.md)
|
||||
|
||||
[whatwg-fetch]: https://fetch.spec.whatwg.org/
|
||||
[response-init]: https://fetch.spec.whatwg.org/#responseinit
|
||||
[node-readable]: https://nodejs.org/api/stream.html#stream_readable_streams
|
||||
[mdn-headers]: https://developer.mozilla.org/en-US/docs/Web/API/Headers
|
||||
[error-handling.md]: https://github.com/node-fetch/node-fetch/blob/master/docs/ERROR-HANDLING.md
|
||||
[FormData]: https://developer.mozilla.org/en-US/docs/Web/API/FormData
|
||||
[Blob]: https://developer.mozilla.org/en-US/docs/Web/API/Blob
|
||||
[File]: https://developer.mozilla.org/en-US/docs/Web/API/File
|
131
node_modules/node-fetch/package.json
generated
vendored
Normal file
131
node_modules/node-fetch/package.json
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
{
|
||||
"name": "node-fetch",
|
||||
"version": "3.2.6",
|
||||
"description": "A light-weight module that brings Fetch API to node.js",
|
||||
"main": "./src/index.js",
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"files": [
|
||||
"src",
|
||||
"@types/index.d.ts"
|
||||
],
|
||||
"types": "./@types/index.d.ts",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"coverage": "c8 report --reporter=text-lcov | coveralls",
|
||||
"test-types": "tsd",
|
||||
"lint": "xo"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/node-fetch/node-fetch.git"
|
||||
},
|
||||
"keywords": [
|
||||
"fetch",
|
||||
"http",
|
||||
"promise",
|
||||
"request",
|
||||
"curl",
|
||||
"wget",
|
||||
"xhr",
|
||||
"whatwg"
|
||||
],
|
||||
"author": "David Frank",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/node-fetch/node-fetch/issues"
|
||||
},
|
||||
"homepage": "https://github.com/node-fetch/node-fetch",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"abortcontroller-polyfill": "^1.7.1",
|
||||
"busboy": "^1.4.0",
|
||||
"c8": "^7.7.2",
|
||||
"chai": "^4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-iterator": "^3.0.2",
|
||||
"chai-string": "^1.5.0",
|
||||
"coveralls": "^3.1.0",
|
||||
"form-data": "^4.0.0",
|
||||
"formdata-node": "^4.2.4",
|
||||
"mocha": "^9.1.3",
|
||||
"p-timeout": "^5.0.0",
|
||||
"stream-consumers": "^1.0.1",
|
||||
"tsd": "^0.14.0",
|
||||
"xo": "^0.39.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"tsd": {
|
||||
"cwd": "@types",
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true
|
||||
}
|
||||
},
|
||||
"xo": {
|
||||
"envs": [
|
||||
"node",
|
||||
"browser"
|
||||
],
|
||||
"ignores": [
|
||||
"example.js"
|
||||
],
|
||||
"rules": {
|
||||
"complexity": 0,
|
||||
"import/extensions": 0,
|
||||
"import/no-useless-path-segments": 0,
|
||||
"import/no-anonymous-default-export": 0,
|
||||
"import/no-named-as-default": 0,
|
||||
"unicorn/import-index": 0,
|
||||
"unicorn/no-array-reduce": 0,
|
||||
"unicorn/prefer-node-protocol": 0,
|
||||
"unicorn/numeric-separators-style": 0,
|
||||
"unicorn/explicit-length-check": 0,
|
||||
"capitalized-comments": 0,
|
||||
"node/no-unsupported-features/es-syntax": 0,
|
||||
"@typescript-eslint/member-ordering": 0
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": "test/**/*.js",
|
||||
"envs": [
|
||||
"node",
|
||||
"mocha"
|
||||
],
|
||||
"rules": {
|
||||
"max-nested-callbacks": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"no-warning-comments": 0,
|
||||
"new-cap": 0,
|
||||
"guard-for-in": 0,
|
||||
"unicorn/no-array-for-each": 0,
|
||||
"unicorn/prevent-abbreviations": 0,
|
||||
"promise/prefer-await-to-then": 0,
|
||||
"ava/no-import-test-files": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"runkitExampleFilename": "example.js",
|
||||
"release": {
|
||||
"branches": [
|
||||
"+([0-9]).x",
|
||||
"main",
|
||||
"next",
|
||||
{
|
||||
"name": "beta",
|
||||
"prerelease": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
397
node_modules/node-fetch/src/body.js
generated
vendored
Normal file
397
node_modules/node-fetch/src/body.js
generated
vendored
Normal file
@ -0,0 +1,397 @@
|
||||
|
||||
/**
|
||||
* Body.js
|
||||
*
|
||||
* Body interface provides common methods for Request and Response
|
||||
*/
|
||||
|
||||
import Stream, {PassThrough} from 'node:stream';
|
||||
import {types, deprecate, promisify} from 'node:util';
|
||||
import {Buffer} from 'node:buffer';
|
||||
|
||||
import Blob from 'fetch-blob';
|
||||
import {FormData, formDataToBlob} from 'formdata-polyfill/esm.min.js';
|
||||
|
||||
import {FetchError} from './errors/fetch-error.js';
|
||||
import {FetchBaseError} from './errors/base.js';
|
||||
import {isBlob, isURLSearchParameters} from './utils/is.js';
|
||||
|
||||
const pipeline = promisify(Stream.pipeline);
|
||||
const INTERNALS = Symbol('Body internals');
|
||||
|
||||
/**
|
||||
* Body mixin
|
||||
*
|
||||
* Ref: https://fetch.spec.whatwg.org/#body
|
||||
*
|
||||
* @param Stream body Readable stream
|
||||
* @param Object opts Response options
|
||||
* @return Void
|
||||
*/
|
||||
export default class Body {
|
||||
constructor(body, {
|
||||
size = 0
|
||||
} = {}) {
|
||||
let boundary = null;
|
||||
|
||||
if (body === null) {
|
||||
// Body is undefined or null
|
||||
body = null;
|
||||
} else if (isURLSearchParameters(body)) {
|
||||
// Body is a URLSearchParams
|
||||
body = Buffer.from(body.toString());
|
||||
} else if (isBlob(body)) {
|
||||
// Body is blob
|
||||
} else if (Buffer.isBuffer(body)) {
|
||||
// Body is Buffer
|
||||
} else if (types.isAnyArrayBuffer(body)) {
|
||||
// Body is ArrayBuffer
|
||||
body = Buffer.from(body);
|
||||
} else if (ArrayBuffer.isView(body)) {
|
||||
// Body is ArrayBufferView
|
||||
body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
|
||||
} else if (body instanceof Stream) {
|
||||
// Body is stream
|
||||
} else if (body instanceof FormData) {
|
||||
// Body is FormData
|
||||
body = formDataToBlob(body);
|
||||
boundary = body.type.split('=')[1];
|
||||
} else {
|
||||
// None of the above
|
||||
// coerce to string then buffer
|
||||
body = Buffer.from(String(body));
|
||||
}
|
||||
|
||||
let stream = body;
|
||||
|
||||
if (Buffer.isBuffer(body)) {
|
||||
stream = Stream.Readable.from(body);
|
||||
} else if (isBlob(body)) {
|
||||
stream = Stream.Readable.from(body.stream());
|
||||
}
|
||||
|
||||
this[INTERNALS] = {
|
||||
body,
|
||||
stream,
|
||||
boundary,
|
||||
disturbed: false,
|
||||
error: null
|
||||
};
|
||||
this.size = size;
|
||||
|
||||
if (body instanceof Stream) {
|
||||
body.on('error', error_ => {
|
||||
const error = error_ instanceof FetchBaseError ?
|
||||
error_ :
|
||||
new FetchError(`Invalid response body while trying to fetch ${this.url}: ${error_.message}`, 'system', error_);
|
||||
this[INTERNALS].error = error;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get body() {
|
||||
return this[INTERNALS].stream;
|
||||
}
|
||||
|
||||
get bodyUsed() {
|
||||
return this[INTERNALS].disturbed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode response as ArrayBuffer
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
async arrayBuffer() {
|
||||
const {buffer, byteOffset, byteLength} = await consumeBody(this);
|
||||
return buffer.slice(byteOffset, byteOffset + byteLength);
|
||||
}
|
||||
|
||||
async formData() {
|
||||
const ct = this.headers.get('content-type');
|
||||
|
||||
if (ct.startsWith('application/x-www-form-urlencoded')) {
|
||||
const formData = new FormData();
|
||||
const parameters = new URLSearchParams(await this.text());
|
||||
|
||||
for (const [name, value] of parameters) {
|
||||
formData.append(name, value);
|
||||
}
|
||||
|
||||
return formData;
|
||||
}
|
||||
|
||||
const {toFormData} = await import('./utils/multipart-parser.js');
|
||||
return toFormData(this.body, ct);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return raw response as Blob
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
async blob() {
|
||||
const ct = (this.headers && this.headers.get('content-type')) || (this[INTERNALS].body && this[INTERNALS].body.type) || '';
|
||||
const buf = await this.arrayBuffer();
|
||||
|
||||
return new Blob([buf], {
|
||||
type: ct
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode response as json
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
async json() {
|
||||
const text = await this.text();
|
||||
return JSON.parse(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode response as text
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
async text() {
|
||||
const buffer = await consumeBody(this);
|
||||
return new TextDecoder().decode(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode response as buffer (non-spec api)
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
buffer() {
|
||||
return consumeBody(this);
|
||||
}
|
||||
}
|
||||
|
||||
Body.prototype.buffer = deprecate(Body.prototype.buffer, 'Please use \'response.arrayBuffer()\' instead of \'response.buffer()\'', 'node-fetch#buffer');
|
||||
|
||||
// In browsers, all properties are enumerable.
|
||||
Object.defineProperties(Body.prototype, {
|
||||
body: {enumerable: true},
|
||||
bodyUsed: {enumerable: true},
|
||||
arrayBuffer: {enumerable: true},
|
||||
blob: {enumerable: true},
|
||||
json: {enumerable: true},
|
||||
text: {enumerable: true},
|
||||
data: {get: deprecate(() => {},
|
||||
'data doesn\'t exist, use json(), text(), arrayBuffer(), or body instead',
|
||||
'https://github.com/node-fetch/node-fetch/issues/1000 (response)')}
|
||||
});
|
||||
|
||||
/**
|
||||
* Consume and convert an entire Body to a Buffer.
|
||||
*
|
||||
* Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
async function consumeBody(data) {
|
||||
if (data[INTERNALS].disturbed) {
|
||||
throw new TypeError(`body used already for: ${data.url}`);
|
||||
}
|
||||
|
||||
data[INTERNALS].disturbed = true;
|
||||
|
||||
if (data[INTERNALS].error) {
|
||||
throw data[INTERNALS].error;
|
||||
}
|
||||
|
||||
const {body} = data;
|
||||
|
||||
// Body is null
|
||||
if (body === null) {
|
||||
return Buffer.alloc(0);
|
||||
}
|
||||
|
||||
/* c8 ignore next 3 */
|
||||
if (!(body instanceof Stream)) {
|
||||
return Buffer.alloc(0);
|
||||
}
|
||||
|
||||
// Body is stream
|
||||
// get ready to actually consume the body
|
||||
const accum = [];
|
||||
let accumBytes = 0;
|
||||
|
||||
try {
|
||||
for await (const chunk of body) {
|
||||
if (data.size > 0 && accumBytes + chunk.length > data.size) {
|
||||
const error = new FetchError(`content size at ${data.url} over limit: ${data.size}`, 'max-size');
|
||||
body.destroy(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
accumBytes += chunk.length;
|
||||
accum.push(chunk);
|
||||
}
|
||||
} catch (error) {
|
||||
const error_ = error instanceof FetchBaseError ? error : new FetchError(`Invalid response body while trying to fetch ${data.url}: ${error.message}`, 'system', error);
|
||||
throw error_;
|
||||
}
|
||||
|
||||
if (body.readableEnded === true || body._readableState.ended === true) {
|
||||
try {
|
||||
if (accum.every(c => typeof c === 'string')) {
|
||||
return Buffer.from(accum.join(''));
|
||||
}
|
||||
|
||||
return Buffer.concat(accum, accumBytes);
|
||||
} catch (error) {
|
||||
throw new FetchError(`Could not create Buffer from response body for ${data.url}: ${error.message}`, 'system', error);
|
||||
}
|
||||
} else {
|
||||
throw new FetchError(`Premature close of server response while trying to fetch ${data.url}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone body given Res/Req instance
|
||||
*
|
||||
* @param Mixed instance Response or Request instance
|
||||
* @param String highWaterMark highWaterMark for both PassThrough body streams
|
||||
* @return Mixed
|
||||
*/
|
||||
export const clone = (instance, highWaterMark) => {
|
||||
let p1;
|
||||
let p2;
|
||||
let {body} = instance[INTERNALS];
|
||||
|
||||
// Don't allow cloning a used body
|
||||
if (instance.bodyUsed) {
|
||||
throw new Error('cannot clone body after it is used');
|
||||
}
|
||||
|
||||
// Check that body is a stream and not form-data object
|
||||
// note: we can't clone the form-data object without having it as a dependency
|
||||
if ((body instanceof Stream) && (typeof body.getBoundary !== 'function')) {
|
||||
// Tee instance body
|
||||
p1 = new PassThrough({highWaterMark});
|
||||
p2 = new PassThrough({highWaterMark});
|
||||
body.pipe(p1);
|
||||
body.pipe(p2);
|
||||
// Set instance body to teed body and return the other teed body
|
||||
instance[INTERNALS].stream = p1;
|
||||
body = p2;
|
||||
}
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
const getNonSpecFormDataBoundary = deprecate(
|
||||
body => body.getBoundary(),
|
||||
'form-data doesn\'t follow the spec and requires special treatment. Use alternative package',
|
||||
'https://github.com/node-fetch/node-fetch/issues/1167'
|
||||
);
|
||||
|
||||
/**
|
||||
* Performs the operation "extract a `Content-Type` value from |object|" as
|
||||
* specified in the specification:
|
||||
* https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
||||
*
|
||||
* This function assumes that instance.body is present.
|
||||
*
|
||||
* @param {any} body Any options.body input
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export const extractContentType = (body, request) => {
|
||||
// Body is null or undefined
|
||||
if (body === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Body is string
|
||||
if (typeof body === 'string') {
|
||||
return 'text/plain;charset=UTF-8';
|
||||
}
|
||||
|
||||
// Body is a URLSearchParams
|
||||
if (isURLSearchParameters(body)) {
|
||||
return 'application/x-www-form-urlencoded;charset=UTF-8';
|
||||
}
|
||||
|
||||
// Body is blob
|
||||
if (isBlob(body)) {
|
||||
return body.type || null;
|
||||
}
|
||||
|
||||
// Body is a Buffer (Buffer, ArrayBuffer or ArrayBufferView)
|
||||
if (Buffer.isBuffer(body) || types.isAnyArrayBuffer(body) || ArrayBuffer.isView(body)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (body instanceof FormData) {
|
||||
return `multipart/form-data; boundary=${request[INTERNALS].boundary}`;
|
||||
}
|
||||
|
||||
// Detect form data input from form-data module
|
||||
if (body && typeof body.getBoundary === 'function') {
|
||||
return `multipart/form-data;boundary=${getNonSpecFormDataBoundary(body)}`;
|
||||
}
|
||||
|
||||
// Body is stream - can't really do much about this
|
||||
if (body instanceof Stream) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Body constructor defaults other things to string
|
||||
return 'text/plain;charset=UTF-8';
|
||||
};
|
||||
|
||||
/**
|
||||
* The Fetch Standard treats this as if "total bytes" is a property on the body.
|
||||
* For us, we have to explicitly get it with a function.
|
||||
*
|
||||
* ref: https://fetch.spec.whatwg.org/#concept-body-total-bytes
|
||||
*
|
||||
* @param {any} obj.body Body object from the Body instance.
|
||||
* @returns {number | null}
|
||||
*/
|
||||
export const getTotalBytes = request => {
|
||||
const {body} = request[INTERNALS];
|
||||
|
||||
// Body is null or undefined
|
||||
if (body === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Body is Blob
|
||||
if (isBlob(body)) {
|
||||
return body.size;
|
||||
}
|
||||
|
||||
// Body is Buffer
|
||||
if (Buffer.isBuffer(body)) {
|
||||
return body.length;
|
||||
}
|
||||
|
||||
// Detect form data input from form-data module
|
||||
if (body && typeof body.getLengthSync === 'function') {
|
||||
return body.hasKnownLength && body.hasKnownLength() ? body.getLengthSync() : null;
|
||||
}
|
||||
|
||||
// Body is stream
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a Body to a Node.js WritableStream (e.g. http.Request) object.
|
||||
*
|
||||
* @param {Stream.Writable} dest The stream to write to.
|
||||
* @param obj.body Body object from the Body instance.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const writeToStream = async (dest, {body}) => {
|
||||
if (body === null) {
|
||||
// Body is null
|
||||
dest.end();
|
||||
} else {
|
||||
// Body is stream
|
||||
await pipeline(body, dest);
|
||||
}
|
||||
};
|
10
node_modules/node-fetch/src/errors/abort-error.js
generated
vendored
Normal file
10
node_modules/node-fetch/src/errors/abort-error.js
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
import {FetchBaseError} from './base.js';
|
||||
|
||||
/**
|
||||
* AbortError interface for cancelled requests
|
||||
*/
|
||||
export class AbortError extends FetchBaseError {
|
||||
constructor(message, type = 'aborted') {
|
||||
super(message, type);
|
||||
}
|
||||
}
|
17
node_modules/node-fetch/src/errors/base.js
generated
vendored
Normal file
17
node_modules/node-fetch/src/errors/base.js
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
export class FetchBaseError extends Error {
|
||||
constructor(message, type) {
|
||||
super(message);
|
||||
// Hide custom error implementation details from end-users
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.constructor.name;
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return this.constructor.name;
|
||||
}
|
||||
}
|
26
node_modules/node-fetch/src/errors/fetch-error.js
generated
vendored
Normal file
26
node_modules/node-fetch/src/errors/fetch-error.js
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
import {FetchBaseError} from './base.js';
|
||||
|
||||
/**
|
||||
* @typedef {{ address?: string, code: string, dest?: string, errno: number, info?: object, message: string, path?: string, port?: number, syscall: string}} SystemError
|
||||
*/
|
||||
|
||||
/**
|
||||
* FetchError interface for operational errors
|
||||
*/
|
||||
export class FetchError extends FetchBaseError {
|
||||
/**
|
||||
* @param {string} message - Error message for human
|
||||
* @param {string} [type] - Error type for machine
|
||||
* @param {SystemError} [systemError] - For Node.js system error
|
||||
*/
|
||||
constructor(message, type, systemError) {
|
||||
super(message, type);
|
||||
// When err.type is `system`, err.erroredSysCall contains system error and err.code contains system error code
|
||||
if (systemError) {
|
||||
// eslint-disable-next-line no-multi-assign
|
||||
this.code = this.errno = systemError.code;
|
||||
this.erroredSysCall = systemError.syscall;
|
||||
}
|
||||
}
|
||||
}
|
267
node_modules/node-fetch/src/headers.js
generated
vendored
Normal file
267
node_modules/node-fetch/src/headers.js
generated
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
/**
|
||||
* Headers.js
|
||||
*
|
||||
* Headers class offers convenient helpers
|
||||
*/
|
||||
|
||||
import {types} from 'node:util';
|
||||
import http from 'node:http';
|
||||
|
||||
/* c8 ignore next 9 */
|
||||
const validateHeaderName = typeof http.validateHeaderName === 'function' ?
|
||||
http.validateHeaderName :
|
||||
name => {
|
||||
if (!/^[\^`\-\w!#$%&'*+.|~]+$/.test(name)) {
|
||||
const error = new TypeError(`Header name must be a valid HTTP token [${name}]`);
|
||||
Object.defineProperty(error, 'code', {value: 'ERR_INVALID_HTTP_TOKEN'});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/* c8 ignore next 9 */
|
||||
const validateHeaderValue = typeof http.validateHeaderValue === 'function' ?
|
||||
http.validateHeaderValue :
|
||||
(name, value) => {
|
||||
if (/[^\t\u0020-\u007E\u0080-\u00FF]/.test(value)) {
|
||||
const error = new TypeError(`Invalid character in header content ["${name}"]`);
|
||||
Object.defineProperty(error, 'code', {value: 'ERR_INVALID_CHAR'});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Headers | Record<string, string> | Iterable<readonly [string, string]> | Iterable<Iterable<string>>} HeadersInit
|
||||
*/
|
||||
|
||||
/**
|
||||
* This Fetch API interface allows you to perform various actions on HTTP request and response headers.
|
||||
* These actions include retrieving, setting, adding to, and removing.
|
||||
* A Headers object has an associated header list, which is initially empty and consists of zero or more name and value pairs.
|
||||
* You can add to this using methods like append() (see Examples.)
|
||||
* In all methods of this interface, header names are matched by case-insensitive byte sequence.
|
||||
*
|
||||
*/
|
||||
export default class Headers extends URLSearchParams {
|
||||
/**
|
||||
* Headers class
|
||||
*
|
||||
* @constructor
|
||||
* @param {HeadersInit} [init] - Response headers
|
||||
*/
|
||||
constructor(init) {
|
||||
// Validate and normalize init object in [name, value(s)][]
|
||||
/** @type {string[][]} */
|
||||
let result = [];
|
||||
if (init instanceof Headers) {
|
||||
const raw = init.raw();
|
||||
for (const [name, values] of Object.entries(raw)) {
|
||||
result.push(...values.map(value => [name, value]));
|
||||
}
|
||||
} else if (init == null) { // eslint-disable-line no-eq-null, eqeqeq
|
||||
// No op
|
||||
} else if (typeof init === 'object' && !types.isBoxedPrimitive(init)) {
|
||||
const method = init[Symbol.iterator];
|
||||
// eslint-disable-next-line no-eq-null, eqeqeq
|
||||
if (method == null) {
|
||||
// Record<ByteString, ByteString>
|
||||
result.push(...Object.entries(init));
|
||||
} else {
|
||||
if (typeof method !== 'function') {
|
||||
throw new TypeError('Header pairs must be iterable');
|
||||
}
|
||||
|
||||
// Sequence<sequence<ByteString>>
|
||||
// Note: per spec we have to first exhaust the lists then process them
|
||||
result = [...init]
|
||||
.map(pair => {
|
||||
if (
|
||||
typeof pair !== 'object' || types.isBoxedPrimitive(pair)
|
||||
) {
|
||||
throw new TypeError('Each header pair must be an iterable object');
|
||||
}
|
||||
|
||||
return [...pair];
|
||||
}).map(pair => {
|
||||
if (pair.length !== 2) {
|
||||
throw new TypeError('Each header pair must be a name/value tuple');
|
||||
}
|
||||
|
||||
return [...pair];
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new TypeError('Failed to construct \'Headers\': The provided value is not of type \'(sequence<sequence<ByteString>> or record<ByteString, ByteString>)');
|
||||
}
|
||||
|
||||
// Validate and lowercase
|
||||
result =
|
||||
result.length > 0 ?
|
||||
result.map(([name, value]) => {
|
||||
validateHeaderName(name);
|
||||
validateHeaderValue(name, String(value));
|
||||
return [String(name).toLowerCase(), String(value)];
|
||||
}) :
|
||||
undefined;
|
||||
|
||||
super(result);
|
||||
|
||||
// Returning a Proxy that will lowercase key names, validate parameters and sort keys
|
||||
// eslint-disable-next-line no-constructor-return
|
||||
return new Proxy(this, {
|
||||
get(target, p, receiver) {
|
||||
switch (p) {
|
||||
case 'append':
|
||||
case 'set':
|
||||
return (name, value) => {
|
||||
validateHeaderName(name);
|
||||
validateHeaderValue(name, String(value));
|
||||
return URLSearchParams.prototype[p].call(
|
||||
target,
|
||||
String(name).toLowerCase(),
|
||||
String(value)
|
||||
);
|
||||
};
|
||||
|
||||
case 'delete':
|
||||
case 'has':
|
||||
case 'getAll':
|
||||
return name => {
|
||||
validateHeaderName(name);
|
||||
return URLSearchParams.prototype[p].call(
|
||||
target,
|
||||
String(name).toLowerCase()
|
||||
);
|
||||
};
|
||||
|
||||
case 'keys':
|
||||
return () => {
|
||||
target.sort();
|
||||
return new Set(URLSearchParams.prototype.keys.call(target)).keys();
|
||||
};
|
||||
|
||||
default:
|
||||
return Reflect.get(target, p, receiver);
|
||||
}
|
||||
}
|
||||
});
|
||||
/* c8 ignore next */
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return this.constructor.name;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return Object.prototype.toString.call(this);
|
||||
}
|
||||
|
||||
get(name) {
|
||||
const values = this.getAll(name);
|
||||
if (values.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let value = values.join(', ');
|
||||
if (/^content-encoding$/i.test(name)) {
|
||||
value = value.toLowerCase();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
forEach(callback, thisArg = undefined) {
|
||||
for (const name of this.keys()) {
|
||||
Reflect.apply(callback, thisArg, [this.get(name), name, this]);
|
||||
}
|
||||
}
|
||||
|
||||
* values() {
|
||||
for (const name of this.keys()) {
|
||||
yield this.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {() => IterableIterator<[string, string]>}
|
||||
*/
|
||||
* entries() {
|
||||
for (const name of this.keys()) {
|
||||
yield [name, this.get(name)];
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Node-fetch non-spec method
|
||||
* returning all headers and their values as array
|
||||
* @returns {Record<string, string[]>}
|
||||
*/
|
||||
raw() {
|
||||
return [...this.keys()].reduce((result, key) => {
|
||||
result[key] = this.getAll(key);
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* For better console.log(headers) and also to convert Headers into Node.js Request compatible format
|
||||
*/
|
||||
[Symbol.for('nodejs.util.inspect.custom')]() {
|
||||
return [...this.keys()].reduce((result, key) => {
|
||||
const values = this.getAll(key);
|
||||
// Http.request() only supports string as Host header.
|
||||
// This hack makes specifying custom Host header possible.
|
||||
if (key === 'host') {
|
||||
result[key] = values[0];
|
||||
} else {
|
||||
result[key] = values.length > 1 ? values : values[0];
|
||||
}
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-shaping object for Web IDL tests
|
||||
* Only need to do it for overridden methods
|
||||
*/
|
||||
Object.defineProperties(
|
||||
Headers.prototype,
|
||||
['get', 'entries', 'forEach', 'values'].reduce((result, property) => {
|
||||
result[property] = {enumerable: true};
|
||||
return result;
|
||||
}, {})
|
||||
);
|
||||
|
||||
/**
|
||||
* Create a Headers object from an http.IncomingMessage.rawHeaders, ignoring those that do
|
||||
* not conform to HTTP grammar productions.
|
||||
* @param {import('http').IncomingMessage['rawHeaders']} headers
|
||||
*/
|
||||
export function fromRawHeaders(headers = []) {
|
||||
return new Headers(
|
||||
headers
|
||||
// Split into pairs
|
||||
.reduce((result, value, index, array) => {
|
||||
if (index % 2 === 0) {
|
||||
result.push(array.slice(index, index + 2));
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [])
|
||||
.filter(([name, value]) => {
|
||||
try {
|
||||
validateHeaderName(name);
|
||||
validateHeaderValue(name, String(value));
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
|
||||
);
|
||||
}
|
414
node_modules/node-fetch/src/index.js
generated
vendored
Normal file
414
node_modules/node-fetch/src/index.js
generated
vendored
Normal file
@ -0,0 +1,414 @@
|
||||
/**
|
||||
* Index.js
|
||||
*
|
||||
* a request API compatible with window.fetch
|
||||
*
|
||||
* All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/.
|
||||
*/
|
||||
|
||||
import http from 'node:http';
|
||||
import https from 'node:https';
|
||||
import zlib from 'node:zlib';
|
||||
import Stream, {PassThrough, pipeline as pump} from 'node:stream';
|
||||
import {Buffer} from 'node:buffer';
|
||||
|
||||
import dataUriToBuffer from 'data-uri-to-buffer';
|
||||
|
||||
import {writeToStream, clone} from './body.js';
|
||||
import Response from './response.js';
|
||||
import Headers, {fromRawHeaders} from './headers.js';
|
||||
import Request, {getNodeRequestOptions} from './request.js';
|
||||
import {FetchError} from './errors/fetch-error.js';
|
||||
import {AbortError} from './errors/abort-error.js';
|
||||
import {isRedirect} from './utils/is-redirect.js';
|
||||
import {FormData} from 'formdata-polyfill/esm.min.js';
|
||||
import {isDomainOrSubdomain} from './utils/is.js';
|
||||
import {parseReferrerPolicyFromHeader} from './utils/referrer.js';
|
||||
import {
|
||||
Blob,
|
||||
File,
|
||||
fileFromSync,
|
||||
fileFrom,
|
||||
blobFromSync,
|
||||
blobFrom
|
||||
} from 'fetch-blob/from.js';
|
||||
|
||||
export {FormData, Headers, Request, Response, FetchError, AbortError, isRedirect};
|
||||
export {Blob, File, fileFromSync, fileFrom, blobFromSync, blobFrom};
|
||||
|
||||
const supportedSchemas = new Set(['data:', 'http:', 'https:']);
|
||||
|
||||
/**
|
||||
* Fetch function
|
||||
*
|
||||
* @param {string | URL | import('./request').default} url - Absolute url or Request instance
|
||||
* @param {*} [options_] - Fetch options
|
||||
* @return {Promise<import('./response').default>}
|
||||
*/
|
||||
export default async function fetch(url, options_) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Build request object
|
||||
const request = new Request(url, options_);
|
||||
const {parsedURL, options} = getNodeRequestOptions(request);
|
||||
if (!supportedSchemas.has(parsedURL.protocol)) {
|
||||
throw new TypeError(`node-fetch cannot load ${url}. URL scheme "${parsedURL.protocol.replace(/:$/, '')}" is not supported.`);
|
||||
}
|
||||
|
||||
if (parsedURL.protocol === 'data:') {
|
||||
const data = dataUriToBuffer(request.url);
|
||||
const response = new Response(data, {headers: {'Content-Type': data.typeFull}});
|
||||
resolve(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrap http.request into fetch
|
||||
const send = (parsedURL.protocol === 'https:' ? https : http).request;
|
||||
const {signal} = request;
|
||||
let response = null;
|
||||
|
||||
const abort = () => {
|
||||
const error = new AbortError('The operation was aborted.');
|
||||
reject(error);
|
||||
if (request.body && request.body instanceof Stream.Readable) {
|
||||
request.body.destroy(error);
|
||||
}
|
||||
|
||||
if (!response || !response.body) {
|
||||
return;
|
||||
}
|
||||
|
||||
response.body.emit('error', error);
|
||||
};
|
||||
|
||||
if (signal && signal.aborted) {
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
const abortAndFinalize = () => {
|
||||
abort();
|
||||
finalize();
|
||||
};
|
||||
|
||||
// Send request
|
||||
const request_ = send(parsedURL.toString(), options);
|
||||
|
||||
if (signal) {
|
||||
signal.addEventListener('abort', abortAndFinalize);
|
||||
}
|
||||
|
||||
const finalize = () => {
|
||||
request_.abort();
|
||||
if (signal) {
|
||||
signal.removeEventListener('abort', abortAndFinalize);
|
||||
}
|
||||
};
|
||||
|
||||
request_.on('error', error => {
|
||||
reject(new FetchError(`request to ${request.url} failed, reason: ${error.message}`, 'system', error));
|
||||
finalize();
|
||||
});
|
||||
|
||||
fixResponseChunkedTransferBadEnding(request_, error => {
|
||||
if (response && response.body) {
|
||||
response.body.destroy(error);
|
||||
}
|
||||
});
|
||||
|
||||
/* c8 ignore next 18 */
|
||||
if (process.version < 'v14') {
|
||||
// Before Node.js 14, pipeline() does not fully support async iterators and does not always
|
||||
// properly handle when the socket close/end events are out of order.
|
||||
request_.on('socket', s => {
|
||||
let endedWithEventsCount;
|
||||
s.prependListener('end', () => {
|
||||
endedWithEventsCount = s._eventsCount;
|
||||
});
|
||||
s.prependListener('close', hadError => {
|
||||
// if end happened before close but the socket didn't emit an error, do it now
|
||||
if (response && endedWithEventsCount < s._eventsCount && !hadError) {
|
||||
const error = new Error('Premature close');
|
||||
error.code = 'ERR_STREAM_PREMATURE_CLOSE';
|
||||
response.body.emit('error', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
request_.on('response', response_ => {
|
||||
request_.setTimeout(0);
|
||||
const headers = fromRawHeaders(response_.rawHeaders);
|
||||
|
||||
// HTTP fetch step 5
|
||||
if (isRedirect(response_.statusCode)) {
|
||||
// HTTP fetch step 5.2
|
||||
const location = headers.get('Location');
|
||||
|
||||
// HTTP fetch step 5.3
|
||||
let locationURL = null;
|
||||
try {
|
||||
locationURL = location === null ? null : new URL(location, request.url);
|
||||
} catch {
|
||||
// error here can only be invalid URL in Location: header
|
||||
// do not throw when options.redirect == manual
|
||||
// let the user extract the errorneous redirect URL
|
||||
if (request.redirect !== 'manual') {
|
||||
reject(new FetchError(`uri requested responds with an invalid redirect URL: ${location}`, 'invalid-redirect'));
|
||||
finalize();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// HTTP fetch step 5.5
|
||||
switch (request.redirect) {
|
||||
case 'error':
|
||||
reject(new FetchError(`uri requested responds with a redirect, redirect mode is set to error: ${request.url}`, 'no-redirect'));
|
||||
finalize();
|
||||
return;
|
||||
case 'manual':
|
||||
// Nothing to do
|
||||
break;
|
||||
case 'follow': {
|
||||
// HTTP-redirect fetch step 2
|
||||
if (locationURL === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
// HTTP-redirect fetch step 5
|
||||
if (request.counter >= request.follow) {
|
||||
reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect'));
|
||||
finalize();
|
||||
return;
|
||||
}
|
||||
|
||||
// HTTP-redirect fetch step 6 (counter increment)
|
||||
// Create a new Request object.
|
||||
const requestOptions = {
|
||||
headers: new Headers(request.headers),
|
||||
follow: request.follow,
|
||||
counter: request.counter + 1,
|
||||
agent: request.agent,
|
||||
compress: request.compress,
|
||||
method: request.method,
|
||||
body: clone(request),
|
||||
signal: request.signal,
|
||||
size: request.size,
|
||||
referrer: request.referrer,
|
||||
referrerPolicy: request.referrerPolicy
|
||||
};
|
||||
|
||||
// when forwarding sensitive headers like "Authorization",
|
||||
// "WWW-Authenticate", and "Cookie" to untrusted targets,
|
||||
// headers will be ignored when following a redirect to a domain
|
||||
// that is not a subdomain match or exact match of the initial domain.
|
||||
// For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com"
|
||||
// will forward the sensitive headers, but a redirect to "bar.com" will not.
|
||||
if (!isDomainOrSubdomain(request.url, locationURL)) {
|
||||
for (const name of ['authorization', 'www-authenticate', 'cookie', 'cookie2']) {
|
||||
requestOptions.headers.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
// HTTP-redirect fetch step 9
|
||||
if (response_.statusCode !== 303 && request.body && options_.body instanceof Stream.Readable) {
|
||||
reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect'));
|
||||
finalize();
|
||||
return;
|
||||
}
|
||||
|
||||
// HTTP-redirect fetch step 11
|
||||
if (response_.statusCode === 303 || ((response_.statusCode === 301 || response_.statusCode === 302) && request.method === 'POST')) {
|
||||
requestOptions.method = 'GET';
|
||||
requestOptions.body = undefined;
|
||||
requestOptions.headers.delete('content-length');
|
||||
}
|
||||
|
||||
// HTTP-redirect fetch step 14
|
||||
const responseReferrerPolicy = parseReferrerPolicyFromHeader(headers);
|
||||
if (responseReferrerPolicy) {
|
||||
requestOptions.referrerPolicy = responseReferrerPolicy;
|
||||
}
|
||||
|
||||
// HTTP-redirect fetch step 15
|
||||
resolve(fetch(new Request(locationURL, requestOptions)));
|
||||
finalize();
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
return reject(new TypeError(`Redirect option '${request.redirect}' is not a valid value of RequestRedirect`));
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare response
|
||||
if (signal) {
|
||||
response_.once('end', () => {
|
||||
signal.removeEventListener('abort', abortAndFinalize);
|
||||
});
|
||||
}
|
||||
|
||||
let body = pump(response_, new PassThrough(), error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
// see https://github.com/nodejs/node/pull/29376
|
||||
/* c8 ignore next 3 */
|
||||
if (process.version < 'v12.10') {
|
||||
response_.on('aborted', abortAndFinalize);
|
||||
}
|
||||
|
||||
const responseOptions = {
|
||||
url: request.url,
|
||||
status: response_.statusCode,
|
||||
statusText: response_.statusMessage,
|
||||
headers,
|
||||
size: request.size,
|
||||
counter: request.counter,
|
||||
highWaterMark: request.highWaterMark
|
||||
};
|
||||
|
||||
// HTTP-network fetch step 12.1.1.3
|
||||
const codings = headers.get('Content-Encoding');
|
||||
|
||||
// HTTP-network fetch step 12.1.1.4: handle content codings
|
||||
|
||||
// in following scenarios we ignore compression support
|
||||
// 1. compression support is disabled
|
||||
// 2. HEAD request
|
||||
// 3. no Content-Encoding header
|
||||
// 4. no content response (204)
|
||||
// 5. content not modified response (304)
|
||||
if (!request.compress || request.method === 'HEAD' || codings === null || response_.statusCode === 204 || response_.statusCode === 304) {
|
||||
response = new Response(body, responseOptions);
|
||||
resolve(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// For Node v6+
|
||||
// Be less strict when decoding compressed responses, since sometimes
|
||||
// servers send slightly invalid responses that are still accepted
|
||||
// by common browsers.
|
||||
// Always using Z_SYNC_FLUSH is what cURL does.
|
||||
const zlibOptions = {
|
||||
flush: zlib.Z_SYNC_FLUSH,
|
||||
finishFlush: zlib.Z_SYNC_FLUSH
|
||||
};
|
||||
|
||||
// For gzip
|
||||
if (codings === 'gzip' || codings === 'x-gzip') {
|
||||
body = pump(body, zlib.createGunzip(zlibOptions), error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
response = new Response(body, responseOptions);
|
||||
resolve(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// For deflate
|
||||
if (codings === 'deflate' || codings === 'x-deflate') {
|
||||
// Handle the infamous raw deflate response from old servers
|
||||
// a hack for old IIS and Apache servers
|
||||
const raw = pump(response_, new PassThrough(), error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
raw.once('data', chunk => {
|
||||
// See http://stackoverflow.com/questions/37519828
|
||||
if ((chunk[0] & 0x0F) === 0x08) {
|
||||
body = pump(body, zlib.createInflate(), error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
body = pump(body, zlib.createInflateRaw(), error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
response = new Response(body, responseOptions);
|
||||
resolve(response);
|
||||
});
|
||||
raw.once('end', () => {
|
||||
// Some old IIS servers return zero-length OK deflate responses, so
|
||||
// 'data' is never emitted. See https://github.com/node-fetch/node-fetch/pull/903
|
||||
if (!response) {
|
||||
response = new Response(body, responseOptions);
|
||||
resolve(response);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// For br
|
||||
if (codings === 'br') {
|
||||
body = pump(body, zlib.createBrotliDecompress(), error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
response = new Response(body, responseOptions);
|
||||
resolve(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, use response as-is
|
||||
response = new Response(body, responseOptions);
|
||||
resolve(response);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line promise/prefer-await-to-then
|
||||
writeToStream(request_, request).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
function fixResponseChunkedTransferBadEnding(request, errorCallback) {
|
||||
const LAST_CHUNK = Buffer.from('0\r\n\r\n');
|
||||
|
||||
let isChunkedTransfer = false;
|
||||
let properLastChunkReceived = false;
|
||||
let previousChunk;
|
||||
|
||||
request.on('response', response => {
|
||||
const {headers} = response;
|
||||
isChunkedTransfer = headers['transfer-encoding'] === 'chunked' && !headers['content-length'];
|
||||
});
|
||||
|
||||
request.on('socket', socket => {
|
||||
const onSocketClose = () => {
|
||||
if (isChunkedTransfer && !properLastChunkReceived) {
|
||||
const error = new Error('Premature close');
|
||||
error.code = 'ERR_STREAM_PREMATURE_CLOSE';
|
||||
errorCallback(error);
|
||||
}
|
||||
};
|
||||
|
||||
const onData = buf => {
|
||||
properLastChunkReceived = Buffer.compare(buf.slice(-5), LAST_CHUNK) === 0;
|
||||
|
||||
// Sometimes final 0-length chunk and end of message code are in separate packets
|
||||
if (!properLastChunkReceived && previousChunk) {
|
||||
properLastChunkReceived = (
|
||||
Buffer.compare(previousChunk.slice(-3), LAST_CHUNK.slice(0, 3)) === 0 &&
|
||||
Buffer.compare(buf.slice(-2), LAST_CHUNK.slice(3)) === 0
|
||||
);
|
||||
}
|
||||
|
||||
previousChunk = buf;
|
||||
};
|
||||
|
||||
socket.prependListener('close', onSocketClose);
|
||||
socket.on('data', onData);
|
||||
|
||||
request.on('close', () => {
|
||||
socket.removeListener('close', onSocketClose);
|
||||
socket.removeListener('data', onData);
|
||||
});
|
||||
});
|
||||
}
|
317
node_modules/node-fetch/src/request.js
generated
vendored
Normal file
317
node_modules/node-fetch/src/request.js
generated
vendored
Normal file
@ -0,0 +1,317 @@
|
||||
/**
|
||||
* Request.js
|
||||
*
|
||||
* Request class contains server only options
|
||||
*
|
||||
* All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/.
|
||||
*/
|
||||
|
||||
import {format as formatUrl} from 'node:url';
|
||||
import {deprecate} from 'node:util';
|
||||
import Headers from './headers.js';
|
||||
import Body, {clone, extractContentType, getTotalBytes} from './body.js';
|
||||
import {isAbortSignal} from './utils/is.js';
|
||||
import {getSearch} from './utils/get-search.js';
|
||||
import {
|
||||
validateReferrerPolicy, determineRequestsReferrer, DEFAULT_REFERRER_POLICY
|
||||
} from './utils/referrer.js';
|
||||
|
||||
const INTERNALS = Symbol('Request internals');
|
||||
|
||||
/**
|
||||
* Check if `obj` is an instance of Request.
|
||||
*
|
||||
* @param {*} object
|
||||
* @return {boolean}
|
||||
*/
|
||||
const isRequest = object => {
|
||||
return (
|
||||
typeof object === 'object' &&
|
||||
typeof object[INTERNALS] === 'object'
|
||||
);
|
||||
};
|
||||
|
||||
const doBadDataWarn = deprecate(() => {},
|
||||
'.data is not a valid RequestInit property, use .body instead',
|
||||
'https://github.com/node-fetch/node-fetch/issues/1000 (request)');
|
||||
|
||||
/**
|
||||
* Request class
|
||||
*
|
||||
* Ref: https://fetch.spec.whatwg.org/#request-class
|
||||
*
|
||||
* @param Mixed input Url or Request instance
|
||||
* @param Object init Custom options
|
||||
* @return Void
|
||||
*/
|
||||
export default class Request extends Body {
|
||||
constructor(input, init = {}) {
|
||||
let parsedURL;
|
||||
|
||||
// Normalize input and force URL to be encoded as UTF-8 (https://github.com/node-fetch/node-fetch/issues/245)
|
||||
if (isRequest(input)) {
|
||||
parsedURL = new URL(input.url);
|
||||
} else {
|
||||
parsedURL = new URL(input);
|
||||
input = {};
|
||||
}
|
||||
|
||||
if (parsedURL.username !== '' || parsedURL.password !== '') {
|
||||
throw new TypeError(`${parsedURL} is an url with embedded credentials.`);
|
||||
}
|
||||
|
||||
let method = init.method || input.method || 'GET';
|
||||
if (/^(delete|get|head|options|post|put)$/i.test(method)) {
|
||||
method = method.toUpperCase();
|
||||
}
|
||||
|
||||
if ('data' in init) {
|
||||
doBadDataWarn();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-eq-null, eqeqeq
|
||||
if ((init.body != null || (isRequest(input) && input.body !== null)) &&
|
||||
(method === 'GET' || method === 'HEAD')) {
|
||||
throw new TypeError('Request with GET/HEAD method cannot have body');
|
||||
}
|
||||
|
||||
const inputBody = init.body ?
|
||||
init.body :
|
||||
(isRequest(input) && input.body !== null ?
|
||||
clone(input) :
|
||||
null);
|
||||
|
||||
super(inputBody, {
|
||||
size: init.size || input.size || 0
|
||||
});
|
||||
|
||||
const headers = new Headers(init.headers || input.headers || {});
|
||||
|
||||
if (inputBody !== null && !headers.has('Content-Type')) {
|
||||
const contentType = extractContentType(inputBody, this);
|
||||
if (contentType) {
|
||||
headers.set('Content-Type', contentType);
|
||||
}
|
||||
}
|
||||
|
||||
let signal = isRequest(input) ?
|
||||
input.signal :
|
||||
null;
|
||||
if ('signal' in init) {
|
||||
signal = init.signal;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-eq-null, eqeqeq
|
||||
if (signal != null && !isAbortSignal(signal)) {
|
||||
throw new TypeError('Expected signal to be an instanceof AbortSignal or EventTarget');
|
||||
}
|
||||
|
||||
// §5.4, Request constructor steps, step 15.1
|
||||
// eslint-disable-next-line no-eq-null, eqeqeq
|
||||
let referrer = init.referrer == null ? input.referrer : init.referrer;
|
||||
if (referrer === '') {
|
||||
// §5.4, Request constructor steps, step 15.2
|
||||
referrer = 'no-referrer';
|
||||
} else if (referrer) {
|
||||
// §5.4, Request constructor steps, step 15.3.1, 15.3.2
|
||||
const parsedReferrer = new URL(referrer);
|
||||
// §5.4, Request constructor steps, step 15.3.3, 15.3.4
|
||||
referrer = /^about:(\/\/)?client$/.test(parsedReferrer) ? 'client' : parsedReferrer;
|
||||
} else {
|
||||
referrer = undefined;
|
||||
}
|
||||
|
||||
this[INTERNALS] = {
|
||||
method,
|
||||
redirect: init.redirect || input.redirect || 'follow',
|
||||
headers,
|
||||
parsedURL,
|
||||
signal,
|
||||
referrer
|
||||
};
|
||||
|
||||
// Node-fetch-only options
|
||||
this.follow = init.follow === undefined ? (input.follow === undefined ? 20 : input.follow) : init.follow;
|
||||
this.compress = init.compress === undefined ? (input.compress === undefined ? true : input.compress) : init.compress;
|
||||
this.counter = init.counter || input.counter || 0;
|
||||
this.agent = init.agent || input.agent;
|
||||
this.highWaterMark = init.highWaterMark || input.highWaterMark || 16384;
|
||||
this.insecureHTTPParser = init.insecureHTTPParser || input.insecureHTTPParser || false;
|
||||
|
||||
// §5.4, Request constructor steps, step 16.
|
||||
// Default is empty string per https://fetch.spec.whatwg.org/#concept-request-referrer-policy
|
||||
this.referrerPolicy = init.referrerPolicy || input.referrerPolicy || '';
|
||||
}
|
||||
|
||||
/** @returns {string} */
|
||||
get method() {
|
||||
return this[INTERNALS].method;
|
||||
}
|
||||
|
||||
/** @returns {string} */
|
||||
get url() {
|
||||
return formatUrl(this[INTERNALS].parsedURL);
|
||||
}
|
||||
|
||||
/** @returns {Headers} */
|
||||
get headers() {
|
||||
return this[INTERNALS].headers;
|
||||
}
|
||||
|
||||
get redirect() {
|
||||
return this[INTERNALS].redirect;
|
||||
}
|
||||
|
||||
/** @returns {AbortSignal} */
|
||||
get signal() {
|
||||
return this[INTERNALS].signal;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-request-referrer
|
||||
get referrer() {
|
||||
if (this[INTERNALS].referrer === 'no-referrer') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (this[INTERNALS].referrer === 'client') {
|
||||
return 'about:client';
|
||||
}
|
||||
|
||||
if (this[INTERNALS].referrer) {
|
||||
return this[INTERNALS].referrer.toString();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get referrerPolicy() {
|
||||
return this[INTERNALS].referrerPolicy;
|
||||
}
|
||||
|
||||
set referrerPolicy(referrerPolicy) {
|
||||
this[INTERNALS].referrerPolicy = validateReferrerPolicy(referrerPolicy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone this request
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
clone() {
|
||||
return new Request(this);
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return 'Request';
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperties(Request.prototype, {
|
||||
method: {enumerable: true},
|
||||
url: {enumerable: true},
|
||||
headers: {enumerable: true},
|
||||
redirect: {enumerable: true},
|
||||
clone: {enumerable: true},
|
||||
signal: {enumerable: true},
|
||||
referrer: {enumerable: true},
|
||||
referrerPolicy: {enumerable: true}
|
||||
});
|
||||
|
||||
/**
|
||||
* Convert a Request to Node.js http request options.
|
||||
*
|
||||
* @param {Request} request - A Request instance
|
||||
* @return The options object to be passed to http.request
|
||||
*/
|
||||
export const getNodeRequestOptions = request => {
|
||||
const {parsedURL} = request[INTERNALS];
|
||||
const headers = new Headers(request[INTERNALS].headers);
|
||||
|
||||
// Fetch step 1.3
|
||||
if (!headers.has('Accept')) {
|
||||
headers.set('Accept', '*/*');
|
||||
}
|
||||
|
||||
// HTTP-network-or-cache fetch steps 2.4-2.7
|
||||
let contentLengthValue = null;
|
||||
if (request.body === null && /^(post|put)$/i.test(request.method)) {
|
||||
contentLengthValue = '0';
|
||||
}
|
||||
|
||||
if (request.body !== null) {
|
||||
const totalBytes = getTotalBytes(request);
|
||||
// Set Content-Length if totalBytes is a number (that is not NaN)
|
||||
if (typeof totalBytes === 'number' && !Number.isNaN(totalBytes)) {
|
||||
contentLengthValue = String(totalBytes);
|
||||
}
|
||||
}
|
||||
|
||||
if (contentLengthValue) {
|
||||
headers.set('Content-Length', contentLengthValue);
|
||||
}
|
||||
|
||||
// 4.1. Main fetch, step 2.6
|
||||
// > If request's referrer policy is the empty string, then set request's referrer policy to the
|
||||
// > default referrer policy.
|
||||
if (request.referrerPolicy === '') {
|
||||
request.referrerPolicy = DEFAULT_REFERRER_POLICY;
|
||||
}
|
||||
|
||||
// 4.1. Main fetch, step 2.7
|
||||
// > If request's referrer is not "no-referrer", set request's referrer to the result of invoking
|
||||
// > determine request's referrer.
|
||||
if (request.referrer && request.referrer !== 'no-referrer') {
|
||||
request[INTERNALS].referrer = determineRequestsReferrer(request);
|
||||
} else {
|
||||
request[INTERNALS].referrer = 'no-referrer';
|
||||
}
|
||||
|
||||
// 4.5. HTTP-network-or-cache fetch, step 6.9
|
||||
// > If httpRequest's referrer is a URL, then append `Referer`/httpRequest's referrer, serialized
|
||||
// > and isomorphic encoded, to httpRequest's header list.
|
||||
if (request[INTERNALS].referrer instanceof URL) {
|
||||
headers.set('Referer', request.referrer);
|
||||
}
|
||||
|
||||
// HTTP-network-or-cache fetch step 2.11
|
||||
if (!headers.has('User-Agent')) {
|
||||
headers.set('User-Agent', 'node-fetch');
|
||||
}
|
||||
|
||||
// HTTP-network-or-cache fetch step 2.15
|
||||
if (request.compress && !headers.has('Accept-Encoding')) {
|
||||
headers.set('Accept-Encoding', 'gzip, deflate, br');
|
||||
}
|
||||
|
||||
let {agent} = request;
|
||||
if (typeof agent === 'function') {
|
||||
agent = agent(parsedURL);
|
||||
}
|
||||
|
||||
if (!headers.has('Connection') && !agent) {
|
||||
headers.set('Connection', 'close');
|
||||
}
|
||||
|
||||
// HTTP-network fetch step 4.2
|
||||
// chunked encoding is handled by Node.js
|
||||
|
||||
const search = getSearch(parsedURL);
|
||||
|
||||
// Pass the full URL directly to request(), but overwrite the following
|
||||
// options:
|
||||
const options = {
|
||||
// Overwrite search to retain trailing ? (issue #776)
|
||||
path: parsedURL.pathname + search,
|
||||
// The following options are not expressed in the URL
|
||||
method: request.method,
|
||||
headers: headers[Symbol.for('nodejs.util.inspect.custom')](),
|
||||
insecureHTTPParser: request.insecureHTTPParser,
|
||||
agent
|
||||
};
|
||||
|
||||
return {
|
||||
/** @type {URL} */
|
||||
parsedURL,
|
||||
options
|
||||
};
|
||||
};
|
141
node_modules/node-fetch/src/response.js
generated
vendored
Normal file
141
node_modules/node-fetch/src/response.js
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Response.js
|
||||
*
|
||||
* Response class provides content decoding
|
||||
*/
|
||||
|
||||
import Headers from './headers.js';
|
||||
import Body, {clone, extractContentType} from './body.js';
|
||||
import {isRedirect} from './utils/is-redirect.js';
|
||||
|
||||
const INTERNALS = Symbol('Response internals');
|
||||
|
||||
/**
|
||||
* Response class
|
||||
*
|
||||
* Ref: https://fetch.spec.whatwg.org/#response-class
|
||||
*
|
||||
* @param Stream body Readable stream
|
||||
* @param Object opts Response options
|
||||
* @return Void
|
||||
*/
|
||||
export default class Response extends Body {
|
||||
constructor(body = null, options = {}) {
|
||||
super(body, options);
|
||||
|
||||
// eslint-disable-next-line no-eq-null, eqeqeq, no-negated-condition
|
||||
const status = options.status != null ? options.status : 200;
|
||||
|
||||
const headers = new Headers(options.headers);
|
||||
|
||||
if (body !== null && !headers.has('Content-Type')) {
|
||||
const contentType = extractContentType(body, this);
|
||||
if (contentType) {
|
||||
headers.append('Content-Type', contentType);
|
||||
}
|
||||
}
|
||||
|
||||
this[INTERNALS] = {
|
||||
type: 'default',
|
||||
url: options.url,
|
||||
status,
|
||||
statusText: options.statusText || '',
|
||||
headers,
|
||||
counter: options.counter,
|
||||
highWaterMark: options.highWaterMark
|
||||
};
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this[INTERNALS].type;
|
||||
}
|
||||
|
||||
get url() {
|
||||
return this[INTERNALS].url || '';
|
||||
}
|
||||
|
||||
get status() {
|
||||
return this[INTERNALS].status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience property representing if the request ended normally
|
||||
*/
|
||||
get ok() {
|
||||
return this[INTERNALS].status >= 200 && this[INTERNALS].status < 300;
|
||||
}
|
||||
|
||||
get redirected() {
|
||||
return this[INTERNALS].counter > 0;
|
||||
}
|
||||
|
||||
get statusText() {
|
||||
return this[INTERNALS].statusText;
|
||||
}
|
||||
|
||||
get headers() {
|
||||
return this[INTERNALS].headers;
|
||||
}
|
||||
|
||||
get highWaterMark() {
|
||||
return this[INTERNALS].highWaterMark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone this response
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
clone() {
|
||||
return new Response(clone(this, this.highWaterMark), {
|
||||
type: this.type,
|
||||
url: this.url,
|
||||
status: this.status,
|
||||
statusText: this.statusText,
|
||||
headers: this.headers,
|
||||
ok: this.ok,
|
||||
redirected: this.redirected,
|
||||
size: this.size,
|
||||
highWaterMark: this.highWaterMark
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url The URL that the new response is to originate from.
|
||||
* @param {number} status An optional status code for the response (e.g., 302.)
|
||||
* @returns {Response} A Response object.
|
||||
*/
|
||||
static redirect(url, status = 302) {
|
||||
if (!isRedirect(status)) {
|
||||
throw new RangeError('Failed to execute "redirect" on "response": Invalid status code');
|
||||
}
|
||||
|
||||
return new Response(null, {
|
||||
headers: {
|
||||
location: new URL(url).toString()
|
||||
},
|
||||
status
|
||||
});
|
||||
}
|
||||
|
||||
static error() {
|
||||
const response = new Response(null, {status: 0, statusText: ''});
|
||||
response[INTERNALS].type = 'error';
|
||||
return response;
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return 'Response';
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperties(Response.prototype, {
|
||||
type: {enumerable: true},
|
||||
url: {enumerable: true},
|
||||
status: {enumerable: true},
|
||||
ok: {enumerable: true},
|
||||
redirected: {enumerable: true},
|
||||
statusText: {enumerable: true},
|
||||
headers: {enumerable: true},
|
||||
clone: {enumerable: true}
|
||||
});
|
9
node_modules/node-fetch/src/utils/get-search.js
generated
vendored
Normal file
9
node_modules/node-fetch/src/utils/get-search.js
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
export const getSearch = parsedURL => {
|
||||
if (parsedURL.search) {
|
||||
return parsedURL.search;
|
||||
}
|
||||
|
||||
const lastOffset = parsedURL.href.length - 1;
|
||||
const hash = parsedURL.hash || (parsedURL.href[lastOffset] === '#' ? '#' : '');
|
||||
return parsedURL.href[lastOffset - hash.length] === '?' ? '?' : '';
|
||||
};
|
11
node_modules/node-fetch/src/utils/is-redirect.js
generated
vendored
Normal file
11
node_modules/node-fetch/src/utils/is-redirect.js
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
const redirectStatus = new Set([301, 302, 303, 307, 308]);
|
||||
|
||||
/**
|
||||
* Redirect code matching
|
||||
*
|
||||
* @param {number} code - Status code
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const isRedirect = code => {
|
||||
return redirectStatus.has(code);
|
||||
};
|
73
node_modules/node-fetch/src/utils/is.js
generated
vendored
Normal file
73
node_modules/node-fetch/src/utils/is.js
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Is.js
|
||||
*
|
||||
* Object type checks.
|
||||
*/
|
||||
|
||||
const NAME = Symbol.toStringTag;
|
||||
|
||||
/**
|
||||
* Check if `obj` is a URLSearchParams object
|
||||
* ref: https://github.com/node-fetch/node-fetch/issues/296#issuecomment-307598143
|
||||
* @param {*} object - Object to check for
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const isURLSearchParameters = object => {
|
||||
return (
|
||||
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' &&
|
||||
typeof object.sort === 'function' &&
|
||||
object[NAME] === 'URLSearchParams'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `object` is a W3C `Blob` object (which `File` inherits from)
|
||||
* @param {*} object - Object to check for
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const isBlob = object => {
|
||||
return (
|
||||
object &&
|
||||
typeof object === 'object' &&
|
||||
typeof object.arrayBuffer === 'function' &&
|
||||
typeof object.type === 'string' &&
|
||||
typeof object.stream === 'function' &&
|
||||
typeof object.constructor === 'function' &&
|
||||
/^(Blob|File)$/.test(object[NAME])
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `obj` is an instance of AbortSignal.
|
||||
* @param {*} object - Object to check for
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const isAbortSignal = object => {
|
||||
return (
|
||||
typeof object === 'object' && (
|
||||
object[NAME] === 'AbortSignal' ||
|
||||
object[NAME] === 'EventTarget'
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* isDomainOrSubdomain reports whether sub is a subdomain (or exact match) of
|
||||
* the parent domain.
|
||||
*
|
||||
* Both domains must already be in canonical form.
|
||||
* @param {string|URL} original
|
||||
* @param {string|URL} destination
|
||||
*/
|
||||
export const isDomainOrSubdomain = (destination, original) => {
|
||||
const orig = new URL(original).hostname;
|
||||
const dest = new URL(destination).hostname;
|
||||
|
||||
return orig === dest || orig.endsWith(`.${dest}`);
|
||||
};
|
432
node_modules/node-fetch/src/utils/multipart-parser.js
generated
vendored
Normal file
432
node_modules/node-fetch/src/utils/multipart-parser.js
generated
vendored
Normal file
@ -0,0 +1,432 @@
|
||||
import {File} from 'fetch-blob/from.js';
|
||||
import {FormData} from 'formdata-polyfill/esm.min.js';
|
||||
|
||||
let s = 0;
|
||||
const S = {
|
||||
START_BOUNDARY: s++,
|
||||
HEADER_FIELD_START: s++,
|
||||
HEADER_FIELD: s++,
|
||||
HEADER_VALUE_START: s++,
|
||||
HEADER_VALUE: s++,
|
||||
HEADER_VALUE_ALMOST_DONE: s++,
|
||||
HEADERS_ALMOST_DONE: s++,
|
||||
PART_DATA_START: s++,
|
||||
PART_DATA: s++,
|
||||
END: s++
|
||||
};
|
||||
|
||||
let f = 1;
|
||||
const F = {
|
||||
PART_BOUNDARY: f,
|
||||
LAST_BOUNDARY: f *= 2
|
||||
};
|
||||
|
||||
const LF = 10;
|
||||
const CR = 13;
|
||||
const SPACE = 32;
|
||||
const HYPHEN = 45;
|
||||
const COLON = 58;
|
||||
const A = 97;
|
||||
const Z = 122;
|
||||
|
||||
const lower = c => c | 0x20;
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
class MultipartParser {
|
||||
/**
|
||||
* @param {string} boundary
|
||||
*/
|
||||
constructor(boundary) {
|
||||
this.index = 0;
|
||||
this.flags = 0;
|
||||
|
||||
this.onHeaderEnd = noop;
|
||||
this.onHeaderField = noop;
|
||||
this.onHeadersEnd = noop;
|
||||
this.onHeaderValue = noop;
|
||||
this.onPartBegin = noop;
|
||||
this.onPartData = noop;
|
||||
this.onPartEnd = noop;
|
||||
|
||||
this.boundaryChars = {};
|
||||
|
||||
boundary = '\r\n--' + boundary;
|
||||
const ui8a = new Uint8Array(boundary.length);
|
||||
for (let i = 0; i < boundary.length; i++) {
|
||||
ui8a[i] = boundary.charCodeAt(i);
|
||||
this.boundaryChars[ui8a[i]] = true;
|
||||
}
|
||||
|
||||
this.boundary = ui8a;
|
||||
this.lookbehind = new Uint8Array(this.boundary.length + 8);
|
||||
this.state = S.START_BOUNDARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} data
|
||||
*/
|
||||
write(data) {
|
||||
let i = 0;
|
||||
const length_ = data.length;
|
||||
let previousIndex = this.index;
|
||||
let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
|
||||
const boundaryLength = this.boundary.length;
|
||||
const boundaryEnd = boundaryLength - 1;
|
||||
const bufferLength = data.length;
|
||||
let c;
|
||||
let cl;
|
||||
|
||||
const mark = name => {
|
||||
this[name + 'Mark'] = i;
|
||||
};
|
||||
|
||||
const clear = name => {
|
||||
delete this[name + 'Mark'];
|
||||
};
|
||||
|
||||
const callback = (callbackSymbol, start, end, ui8a) => {
|
||||
if (start === undefined || start !== end) {
|
||||
this[callbackSymbol](ui8a && ui8a.subarray(start, end));
|
||||
}
|
||||
};
|
||||
|
||||
const dataCallback = (name, clear) => {
|
||||
const markSymbol = name + 'Mark';
|
||||
if (!(markSymbol in this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (clear) {
|
||||
callback(name, this[markSymbol], i, data);
|
||||
delete this[markSymbol];
|
||||
} else {
|
||||
callback(name, this[markSymbol], data.length, data);
|
||||
this[markSymbol] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
for (i = 0; i < length_; i++) {
|
||||
c = data[i];
|
||||
|
||||
switch (state) {
|
||||
case S.START_BOUNDARY:
|
||||
if (index === boundary.length - 2) {
|
||||
if (c === HYPHEN) {
|
||||
flags |= F.LAST_BOUNDARY;
|
||||
} else if (c !== CR) {
|
||||
return;
|
||||
}
|
||||
|
||||
index++;
|
||||
break;
|
||||
} else if (index - 1 === boundary.length - 2) {
|
||||
if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
|
||||
state = S.END;
|
||||
flags = 0;
|
||||
} else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
|
||||
index = 0;
|
||||
callback('onPartBegin');
|
||||
state = S.HEADER_FIELD_START;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (c !== boundary[index + 2]) {
|
||||
index = -2;
|
||||
}
|
||||
|
||||
if (c === boundary[index + 2]) {
|
||||
index++;
|
||||
}
|
||||
|
||||
break;
|
||||
case S.HEADER_FIELD_START:
|
||||
state = S.HEADER_FIELD;
|
||||
mark('onHeaderField');
|
||||
index = 0;
|
||||
// falls through
|
||||
case S.HEADER_FIELD:
|
||||
if (c === CR) {
|
||||
clear('onHeaderField');
|
||||
state = S.HEADERS_ALMOST_DONE;
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (c === HYPHEN) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (c === COLON) {
|
||||
if (index === 1) {
|
||||
// empty header field
|
||||
return;
|
||||
}
|
||||
|
||||
dataCallback('onHeaderField', true);
|
||||
state = S.HEADER_VALUE_START;
|
||||
break;
|
||||
}
|
||||
|
||||
cl = lower(c);
|
||||
if (cl < A || cl > Z) {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case S.HEADER_VALUE_START:
|
||||
if (c === SPACE) {
|
||||
break;
|
||||
}
|
||||
|
||||
mark('onHeaderValue');
|
||||
state = S.HEADER_VALUE;
|
||||
// falls through
|
||||
case S.HEADER_VALUE:
|
||||
if (c === CR) {
|
||||
dataCallback('onHeaderValue', true);
|
||||
callback('onHeaderEnd');
|
||||
state = S.HEADER_VALUE_ALMOST_DONE;
|
||||
}
|
||||
|
||||
break;
|
||||
case S.HEADER_VALUE_ALMOST_DONE:
|
||||
if (c !== LF) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = S.HEADER_FIELD_START;
|
||||
break;
|
||||
case S.HEADERS_ALMOST_DONE:
|
||||
if (c !== LF) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback('onHeadersEnd');
|
||||
state = S.PART_DATA_START;
|
||||
break;
|
||||
case S.PART_DATA_START:
|
||||
state = S.PART_DATA;
|
||||
mark('onPartData');
|
||||
// falls through
|
||||
case S.PART_DATA:
|
||||
previousIndex = index;
|
||||
|
||||
if (index === 0) {
|
||||
// boyer-moore derrived algorithm to safely skip non-boundary data
|
||||
i += boundaryEnd;
|
||||
while (i < bufferLength && !(data[i] in boundaryChars)) {
|
||||
i += boundaryLength;
|
||||
}
|
||||
|
||||
i -= boundaryEnd;
|
||||
c = data[i];
|
||||
}
|
||||
|
||||
if (index < boundary.length) {
|
||||
if (boundary[index] === c) {
|
||||
if (index === 0) {
|
||||
dataCallback('onPartData', true);
|
||||
}
|
||||
|
||||
index++;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else if (index === boundary.length) {
|
||||
index++;
|
||||
if (c === CR) {
|
||||
// CR = part boundary
|
||||
flags |= F.PART_BOUNDARY;
|
||||
} else if (c === HYPHEN) {
|
||||
// HYPHEN = end boundary
|
||||
flags |= F.LAST_BOUNDARY;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else if (index - 1 === boundary.length) {
|
||||
if (flags & F.PART_BOUNDARY) {
|
||||
index = 0;
|
||||
if (c === LF) {
|
||||
// unset the PART_BOUNDARY flag
|
||||
flags &= ~F.PART_BOUNDARY;
|
||||
callback('onPartEnd');
|
||||
callback('onPartBegin');
|
||||
state = S.HEADER_FIELD_START;
|
||||
break;
|
||||
}
|
||||
} else if (flags & F.LAST_BOUNDARY) {
|
||||
if (c === HYPHEN) {
|
||||
callback('onPartEnd');
|
||||
state = S.END;
|
||||
flags = 0;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
// when matching a possible boundary, keep a lookbehind reference
|
||||
// in case it turns out to be a false lead
|
||||
lookbehind[index - 1] = c;
|
||||
} else if (previousIndex > 0) {
|
||||
// if our boundary turned out to be rubbish, the captured lookbehind
|
||||
// belongs to partData
|
||||
const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
|
||||
callback('onPartData', 0, previousIndex, _lookbehind);
|
||||
previousIndex = 0;
|
||||
mark('onPartData');
|
||||
|
||||
// reconsider the current character even so it interrupted the sequence
|
||||
// it could be the beginning of a new sequence
|
||||
i--;
|
||||
}
|
||||
|
||||
break;
|
||||
case S.END:
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unexpected state entered: ${state}`);
|
||||
}
|
||||
}
|
||||
|
||||
dataCallback('onHeaderField');
|
||||
dataCallback('onHeaderValue');
|
||||
dataCallback('onPartData');
|
||||
|
||||
// Update properties for the next call
|
||||
this.index = index;
|
||||
this.state = state;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
end() {
|
||||
if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
|
||||
(this.state === S.PART_DATA && this.index === this.boundary.length)) {
|
||||
this.onPartEnd();
|
||||
} else if (this.state !== S.END) {
|
||||
throw new Error('MultipartParser.end(): stream ended unexpectedly');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _fileName(headerValue) {
|
||||
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
|
||||
const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
const match = m[2] || m[3] || '';
|
||||
let filename = match.slice(match.lastIndexOf('\\') + 1);
|
||||
filename = filename.replace(/%22/g, '"');
|
||||
filename = filename.replace(/&#(\d{4});/g, (m, code) => {
|
||||
return String.fromCharCode(code);
|
||||
});
|
||||
return filename;
|
||||
}
|
||||
|
||||
export async function toFormData(Body, ct) {
|
||||
if (!/multipart/i.test(ct)) {
|
||||
throw new TypeError('Failed to fetch');
|
||||
}
|
||||
|
||||
const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
|
||||
|
||||
if (!m) {
|
||||
throw new TypeError('no or bad content-type header, no multipart boundary');
|
||||
}
|
||||
|
||||
const parser = new MultipartParser(m[1] || m[2]);
|
||||
|
||||
let headerField;
|
||||
let headerValue;
|
||||
let entryValue;
|
||||
let entryName;
|
||||
let contentType;
|
||||
let filename;
|
||||
const entryChunks = [];
|
||||
const formData = new FormData();
|
||||
|
||||
const onPartData = ui8a => {
|
||||
entryValue += decoder.decode(ui8a, {stream: true});
|
||||
};
|
||||
|
||||
const appendToFile = ui8a => {
|
||||
entryChunks.push(ui8a);
|
||||
};
|
||||
|
||||
const appendFileToFormData = () => {
|
||||
const file = new File(entryChunks, filename, {type: contentType});
|
||||
formData.append(entryName, file);
|
||||
};
|
||||
|
||||
const appendEntryToFormData = () => {
|
||||
formData.append(entryName, entryValue);
|
||||
};
|
||||
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
decoder.decode();
|
||||
|
||||
parser.onPartBegin = function () {
|
||||
parser.onPartData = onPartData;
|
||||
parser.onPartEnd = appendEntryToFormData;
|
||||
|
||||
headerField = '';
|
||||
headerValue = '';
|
||||
entryValue = '';
|
||||
entryName = '';
|
||||
contentType = '';
|
||||
filename = null;
|
||||
entryChunks.length = 0;
|
||||
};
|
||||
|
||||
parser.onHeaderField = function (ui8a) {
|
||||
headerField += decoder.decode(ui8a, {stream: true});
|
||||
};
|
||||
|
||||
parser.onHeaderValue = function (ui8a) {
|
||||
headerValue += decoder.decode(ui8a, {stream: true});
|
||||
};
|
||||
|
||||
parser.onHeaderEnd = function () {
|
||||
headerValue += decoder.decode();
|
||||
headerField = headerField.toLowerCase();
|
||||
|
||||
if (headerField === 'content-disposition') {
|
||||
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
|
||||
const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
|
||||
|
||||
if (m) {
|
||||
entryName = m[2] || m[3] || '';
|
||||
}
|
||||
|
||||
filename = _fileName(headerValue);
|
||||
|
||||
if (filename) {
|
||||
parser.onPartData = appendToFile;
|
||||
parser.onPartEnd = appendFileToFormData;
|
||||
}
|
||||
} else if (headerField === 'content-type') {
|
||||
contentType = headerValue;
|
||||
}
|
||||
|
||||
headerValue = '';
|
||||
headerField = '';
|
||||
};
|
||||
|
||||
for await (const chunk of Body) {
|
||||
parser.write(chunk);
|
||||
}
|
||||
|
||||
parser.end();
|
||||
|
||||
return formData;
|
||||
}
|
340
node_modules/node-fetch/src/utils/referrer.js
generated
vendored
Normal file
340
node_modules/node-fetch/src/utils/referrer.js
generated
vendored
Normal file
@ -0,0 +1,340 @@
|
||||
import {isIP} from 'node:net';
|
||||
|
||||
/**
|
||||
* @external URL
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/URL|URL}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module utils/referrer
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#strip-url|Referrer Policy §8.4. Strip url for use as a referrer}
|
||||
* @param {string} URL
|
||||
* @param {boolean} [originOnly=false]
|
||||
*/
|
||||
export function stripURLForUseAsAReferrer(url, originOnly = false) {
|
||||
// 1. If url is null, return no referrer.
|
||||
if (url == null) { // eslint-disable-line no-eq-null, eqeqeq
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
url = new URL(url);
|
||||
|
||||
// 2. If url's scheme is a local scheme, then return no referrer.
|
||||
if (/^(about|blob|data):$/.test(url.protocol)) {
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
// 3. Set url's username to the empty string.
|
||||
url.username = '';
|
||||
|
||||
// 4. Set url's password to null.
|
||||
// Note: `null` appears to be a mistake as this actually results in the password being `"null"`.
|
||||
url.password = '';
|
||||
|
||||
// 5. Set url's fragment to null.
|
||||
// Note: `null` appears to be a mistake as this actually results in the fragment being `"#null"`.
|
||||
url.hash = '';
|
||||
|
||||
// 6. If the origin-only flag is true, then:
|
||||
if (originOnly) {
|
||||
// 6.1. Set url's path to null.
|
||||
// Note: `null` appears to be a mistake as this actually results in the path being `"/null"`.
|
||||
url.pathname = '';
|
||||
|
||||
// 6.2. Set url's query to null.
|
||||
// Note: `null` appears to be a mistake as this actually results in the query being `"?null"`.
|
||||
url.search = '';
|
||||
}
|
||||
|
||||
// 7. Return url.
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#enumdef-referrerpolicy|enum ReferrerPolicy}
|
||||
*/
|
||||
export const ReferrerPolicy = new Set([
|
||||
'',
|
||||
'no-referrer',
|
||||
'no-referrer-when-downgrade',
|
||||
'same-origin',
|
||||
'origin',
|
||||
'strict-origin',
|
||||
'origin-when-cross-origin',
|
||||
'strict-origin-when-cross-origin',
|
||||
'unsafe-url'
|
||||
]);
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#default-referrer-policy|default referrer policy}
|
||||
*/
|
||||
export const DEFAULT_REFERRER_POLICY = 'strict-origin-when-cross-origin';
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#referrer-policies|Referrer Policy §3. Referrer Policies}
|
||||
* @param {string} referrerPolicy
|
||||
* @returns {string} referrerPolicy
|
||||
*/
|
||||
export function validateReferrerPolicy(referrerPolicy) {
|
||||
if (!ReferrerPolicy.has(referrerPolicy)) {
|
||||
throw new TypeError(`Invalid referrerPolicy: ${referrerPolicy}`);
|
||||
}
|
||||
|
||||
return referrerPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy|Referrer Policy §3.2. Is origin potentially trustworthy?}
|
||||
* @param {external:URL} url
|
||||
* @returns `true`: "Potentially Trustworthy", `false`: "Not Trustworthy"
|
||||
*/
|
||||
export function isOriginPotentiallyTrustworthy(url) {
|
||||
// 1. If origin is an opaque origin, return "Not Trustworthy".
|
||||
// Not applicable
|
||||
|
||||
// 2. Assert: origin is a tuple origin.
|
||||
// Not for implementations
|
||||
|
||||
// 3. If origin's scheme is either "https" or "wss", return "Potentially Trustworthy".
|
||||
if (/^(http|ws)s:$/.test(url.protocol)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. If origin's host component matches one of the CIDR notations 127.0.0.0/8 or ::1/128 [RFC4632], return "Potentially Trustworthy".
|
||||
const hostIp = url.host.replace(/(^\[)|(]$)/g, '');
|
||||
const hostIPVersion = isIP(hostIp);
|
||||
|
||||
if (hostIPVersion === 4 && /^127\./.test(hostIp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hostIPVersion === 6 && /^(((0+:){7})|(::(0+:){0,6}))0*1$/.test(hostIp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 5. If origin's host component is "localhost" or falls within ".localhost", and the user agent conforms to the name resolution rules in [let-localhost-be-localhost], return "Potentially Trustworthy".
|
||||
// We are returning FALSE here because we cannot ensure conformance to
|
||||
// let-localhost-be-loalhost (https://tools.ietf.org/html/draft-west-let-localhost-be-localhost)
|
||||
if (/^(.+\.)*localhost$/.test(url.host)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6. If origin's scheme component is file, return "Potentially Trustworthy".
|
||||
if (url.protocol === 'file:') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 7. If origin's scheme component is one which the user agent considers to be authenticated, return "Potentially Trustworthy".
|
||||
// Not supported
|
||||
|
||||
// 8. If origin has been configured as a trustworthy origin, return "Potentially Trustworthy".
|
||||
// Not supported
|
||||
|
||||
// 9. Return "Not Trustworthy".
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-secure-contexts/#is-url-trustworthy|Referrer Policy §3.3. Is url potentially trustworthy?}
|
||||
* @param {external:URL} url
|
||||
* @returns `true`: "Potentially Trustworthy", `false`: "Not Trustworthy"
|
||||
*/
|
||||
export function isUrlPotentiallyTrustworthy(url) {
|
||||
// 1. If url is "about:blank" or "about:srcdoc", return "Potentially Trustworthy".
|
||||
if (/^about:(blank|srcdoc)$/.test(url)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. If url's scheme is "data", return "Potentially Trustworthy".
|
||||
if (url.protocol === 'data:') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note: The origin of blob: and filesystem: URLs is the origin of the context in which they were
|
||||
// created. Therefore, blobs created in a trustworthy origin will themselves be potentially
|
||||
// trustworthy.
|
||||
if (/^(blob|filesystem):$/.test(url.protocol)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Return the result of executing §3.2 Is origin potentially trustworthy? on url's origin.
|
||||
return isOriginPotentiallyTrustworthy(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the referrerURL to enforce any extra security policy considerations.
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer|Referrer Policy §8.3. Determine request's Referrer}, step 7
|
||||
* @callback module:utils/referrer~referrerURLCallback
|
||||
* @param {external:URL} referrerURL
|
||||
* @returns {external:URL} modified referrerURL
|
||||
*/
|
||||
|
||||
/**
|
||||
* Modifies the referrerOrigin to enforce any extra security policy considerations.
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer|Referrer Policy §8.3. Determine request's Referrer}, step 7
|
||||
* @callback module:utils/referrer~referrerOriginCallback
|
||||
* @param {external:URL} referrerOrigin
|
||||
* @returns {external:URL} modified referrerOrigin
|
||||
*/
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer|Referrer Policy §8.3. Determine request's Referrer}
|
||||
* @param {Request} request
|
||||
* @param {object} o
|
||||
* @param {module:utils/referrer~referrerURLCallback} o.referrerURLCallback
|
||||
* @param {module:utils/referrer~referrerOriginCallback} o.referrerOriginCallback
|
||||
* @returns {external:URL} Request's referrer
|
||||
*/
|
||||
export function determineRequestsReferrer(request, {referrerURLCallback, referrerOriginCallback} = {}) {
|
||||
// There are 2 notes in the specification about invalid pre-conditions. We return null, here, for
|
||||
// these cases:
|
||||
// > Note: If request's referrer is "no-referrer", Fetch will not call into this algorithm.
|
||||
// > Note: If request's referrer policy is the empty string, Fetch will not call into this
|
||||
// > algorithm.
|
||||
if (request.referrer === 'no-referrer' || request.referrerPolicy === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1. Let policy be request's associated referrer policy.
|
||||
const policy = request.referrerPolicy;
|
||||
|
||||
// 2. Let environment be request's client.
|
||||
// not applicable to node.js
|
||||
|
||||
// 3. Switch on request's referrer:
|
||||
if (request.referrer === 'about:client') {
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
// "a URL": Let referrerSource be request's referrer.
|
||||
const referrerSource = request.referrer;
|
||||
|
||||
// 4. Let request's referrerURL be the result of stripping referrerSource for use as a referrer.
|
||||
let referrerURL = stripURLForUseAsAReferrer(referrerSource);
|
||||
|
||||
// 5. Let referrerOrigin be the result of stripping referrerSource for use as a referrer, with the
|
||||
// origin-only flag set to true.
|
||||
let referrerOrigin = stripURLForUseAsAReferrer(referrerSource, true);
|
||||
|
||||
// 6. If the result of serializing referrerURL is a string whose length is greater than 4096, set
|
||||
// referrerURL to referrerOrigin.
|
||||
if (referrerURL.toString().length > 4096) {
|
||||
referrerURL = referrerOrigin;
|
||||
}
|
||||
|
||||
// 7. The user agent MAY alter referrerURL or referrerOrigin at this point to enforce arbitrary
|
||||
// policy considerations in the interests of minimizing data leakage. For example, the user
|
||||
// agent could strip the URL down to an origin, modify its host, replace it with an empty
|
||||
// string, etc.
|
||||
if (referrerURLCallback) {
|
||||
referrerURL = referrerURLCallback(referrerURL);
|
||||
}
|
||||
|
||||
if (referrerOriginCallback) {
|
||||
referrerOrigin = referrerOriginCallback(referrerOrigin);
|
||||
}
|
||||
|
||||
// 8.Execute the statements corresponding to the value of policy:
|
||||
const currentURL = new URL(request.url);
|
||||
|
||||
switch (policy) {
|
||||
case 'no-referrer':
|
||||
return 'no-referrer';
|
||||
|
||||
case 'origin':
|
||||
return referrerOrigin;
|
||||
|
||||
case 'unsafe-url':
|
||||
return referrerURL;
|
||||
|
||||
case 'strict-origin':
|
||||
// 1. If referrerURL is a potentially trustworthy URL and request's current URL is not a
|
||||
// potentially trustworthy URL, then return no referrer.
|
||||
if (isUrlPotentiallyTrustworthy(referrerURL) && !isUrlPotentiallyTrustworthy(currentURL)) {
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
// 2. Return referrerOrigin.
|
||||
return referrerOrigin.toString();
|
||||
|
||||
case 'strict-origin-when-cross-origin':
|
||||
// 1. If the origin of referrerURL and the origin of request's current URL are the same, then
|
||||
// return referrerURL.
|
||||
if (referrerURL.origin === currentURL.origin) {
|
||||
return referrerURL;
|
||||
}
|
||||
|
||||
// 2. If referrerURL is a potentially trustworthy URL and request's current URL is not a
|
||||
// potentially trustworthy URL, then return no referrer.
|
||||
if (isUrlPotentiallyTrustworthy(referrerURL) && !isUrlPotentiallyTrustworthy(currentURL)) {
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
// 3. Return referrerOrigin.
|
||||
return referrerOrigin;
|
||||
|
||||
case 'same-origin':
|
||||
// 1. If the origin of referrerURL and the origin of request's current URL are the same, then
|
||||
// return referrerURL.
|
||||
if (referrerURL.origin === currentURL.origin) {
|
||||
return referrerURL;
|
||||
}
|
||||
|
||||
// 2. Return no referrer.
|
||||
return 'no-referrer';
|
||||
|
||||
case 'origin-when-cross-origin':
|
||||
// 1. If the origin of referrerURL and the origin of request's current URL are the same, then
|
||||
// return referrerURL.
|
||||
if (referrerURL.origin === currentURL.origin) {
|
||||
return referrerURL;
|
||||
}
|
||||
|
||||
// Return referrerOrigin.
|
||||
return referrerOrigin;
|
||||
|
||||
case 'no-referrer-when-downgrade':
|
||||
// 1. If referrerURL is a potentially trustworthy URL and request's current URL is not a
|
||||
// potentially trustworthy URL, then return no referrer.
|
||||
if (isUrlPotentiallyTrustworthy(referrerURL) && !isUrlPotentiallyTrustworthy(currentURL)) {
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
// 2. Return referrerURL.
|
||||
return referrerURL;
|
||||
|
||||
default:
|
||||
throw new TypeError(`Invalid referrerPolicy: ${policy}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header|Referrer Policy §8.1. Parse a referrer policy from a Referrer-Policy header}
|
||||
* @param {Headers} headers Response headers
|
||||
* @returns {string} policy
|
||||
*/
|
||||
export function parseReferrerPolicyFromHeader(headers) {
|
||||
// 1. Let policy-tokens be the result of extracting header list values given `Referrer-Policy`
|
||||
// and response’s header list.
|
||||
const policyTokens = (headers.get('referrer-policy') || '').split(/[,\s]+/);
|
||||
|
||||
// 2. Let policy be the empty string.
|
||||
let policy = '';
|
||||
|
||||
// 3. For each token in policy-tokens, if token is a referrer policy and token is not the empty
|
||||
// string, then set policy to token.
|
||||
// Note: This algorithm loops over multiple policy values to allow deployment of new policy
|
||||
// values with fallbacks for older user agents, as described in § 11.1 Unknown Policy Values.
|
||||
for (const token of policyTokens) {
|
||||
if (token && ReferrerPolicy.has(token)) {
|
||||
policy = token;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Return policy.
|
||||
return policy;
|
||||
}
|
402
node_modules/undici/README.md
generated
vendored
402
node_modules/undici/README.md
generated
vendored
@ -1,402 +0,0 @@
|
||||
# undici
|
||||
|
||||
[](https://github.com/nodejs/undici/actions/workflows/nodejs.yml) [](http://standardjs.com/) [](https://badge.fury.io/js/undici) [](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 16.
|
||||
The benchmarks below have the [simd](https://github.com/WebAssembly/simd) feature enabled.
|
||||
|
||||
### Connections 1
|
||||
|
||||
| Tests | Samples | Result | Tolerance | Difference with slowest |
|
||||
|---------------------|---------|---------------|-----------|-------------------------|
|
||||
| http - no keepalive | 15 | 4.63 req/sec | ± 2.77 % | - |
|
||||
| http - keepalive | 10 | 4.81 req/sec | ± 2.16 % | + 3.94 % |
|
||||
| undici - stream | 25 | 62.22 req/sec | ± 2.67 % | + 1244.58 % |
|
||||
| undici - dispatch | 15 | 64.33 req/sec | ± 2.47 % | + 1290.24 % |
|
||||
| undici - request | 15 | 66.08 req/sec | ± 2.48 % | + 1327.88 % |
|
||||
| undici - pipeline | 10 | 66.13 req/sec | ± 1.39 % | + 1329.08 % |
|
||||
|
||||
### Connections 50
|
||||
|
||||
| Tests | Samples | Result | Tolerance | Difference with slowest |
|
||||
|---------------------|---------|------------------|-----------|-------------------------|
|
||||
| http - no keepalive | 50 | 3546.49 req/sec | ± 2.90 % | - |
|
||||
| http - keepalive | 15 | 5692.67 req/sec | ± 2.48 % | + 60.52 % |
|
||||
| undici - pipeline | 25 | 8478.71 req/sec | ± 2.62 % | + 139.07 % |
|
||||
| undici - request | 20 | 9766.66 req/sec | ± 2.79 % | + 175.39 % |
|
||||
| undici - stream | 15 | 10109.74 req/sec | ± 2.94 % | + 185.06 % |
|
||||
| undici - dispatch | 25 | 10949.73 req/sec | ± 2.54 % | + 208.75 % |
|
||||
|
||||
## 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.5+.
|
||||
|
||||
This is [experimental](https://nodejs.org/api/documentation.html#documentation_stability_index) and is not yet fully compliant with the Fetch Standard.
|
||||
We plan to ship breaking changes to this feature until it is out of experimental.
|
||||
Help us improve the test coverage by following instructions at [nodejs/undici/#951](https://github.com/nodejs/undici/issues/951).
|
||||
|
||||
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' });
|
||||
```
|
||||
|
||||
#### `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`
|
||||
|
||||
### `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
|
||||
|
||||
## 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>
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
79
node_modules/undici/docs/api/Agent.md
generated
vendored
79
node_modules/undici/docs/api/Agent.md
generated
vendored
@ -1,79 +0,0 @@
|
||||
# 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: [`ClientOptions`](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`.
|
||||
|
||||
## 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
99
node_modules/undici/docs/api/BalancedPool.md
generated
vendored
@ -1,99 +0,0 @@
|
||||
# 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).
|
263
node_modules/undici/docs/api/Client.md
generated
vendored
263
node_modules/undici/docs/api/Client.md
generated
vendored
@ -1,263 +0,0 @@
|
||||
# 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`
|
||||
|
||||
* **bodyTimeout** `number | null` (optional) - Default: `30e3` - 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 30 seconds.
|
||||
* **headersTimeout** `number | null` (optional) - Default: `30e3` - The amount of time the parser will wait to receive the complete HTTP headers. Defaults to 30 seconds.
|
||||
* **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout` when overridden by *keep-alive* hints from the server. Defaults to 10 minutes.
|
||||
* **keepAliveTimeout** `number | null` (optional) - Default: `4e3` - The timeout 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 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: `16384` - The maximum length of request headers in bytes. Defaults to 16KiB.
|
||||
* **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.
|
||||
|
||||
#### 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) - Default `10e3`
|
||||
* **servername** `string | null` (optional)
|
||||
|
||||
### 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.
|
113
node_modules/undici/docs/api/Connector.md
generated
vendored
113
node_modules/undici/docs/api/Connector.md
generated
vendored
@ -1,113 +0,0 @@
|
||||
# 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) - 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** `number` (required)
|
||||
* **servername** `string` (optional)
|
||||
|
||||
### 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
|
||||
}
|
||||
```
|
137
node_modules/undici/docs/api/DiagnosticsChannel.md
generated
vendored
137
node_modules/undici/docs/api/DiagnosticsChannel.md
generated
vendored
@ -1,137 +0,0 @@
|
||||
# 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}`)
|
||||
})
|
885
node_modules/undici/docs/api/Dispatcher.md
generated
vendored
885
node_modules/undici/docs/api/Dispatcher.md
generated
vendored
@ -1,885 +0,0 @@
|
||||
# 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** `http.IncomingHttpHeaders`
|
||||
* **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`
|
||||
* **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 30 seconds.
|
||||
* **headersTimeout** `number | null` (optional) - The amount of time the parser will wait to receive the complete HTTP headers. Defaults to 30 seconds.
|
||||
* **throwOnError** `boolean` (optional) - Default: `false` - Whether Undici should throw an error upon receiving a 4xx or 5xx response from the 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** `IncomingHttpHeaders`
|
||||
* **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** `http.IncomingHttpHeaders` - 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** `http.IncomingHttpHeaders`
|
||||
* **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`
|
||||
|
||||
* `http.IncomingHttpHeaders | 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 `http.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', '*/*'
|
||||
]
|
||||
```
|
40
node_modules/undici/docs/api/Errors.md
generated
vendored
40
node_modules/undici/docs/api/Errors.md
generated
vendored
@ -1,40 +0,0 @@
|
||||
# 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 |
|
||||
| ------------------------------------ | ------------------------------------- | -------------------------------------------------- |
|
||||
| `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 |
|
||||
|
||||
### `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`.
|
523
node_modules/undici/docs/api/MockAgent.md
generated
vendored
523
node_modules/undici/docs/api/MockAgent.md
generated
vendored
@ -1,523 +0,0 @@
|
||||
# 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 - 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
77
node_modules/undici/docs/api/MockClient.md
generated
vendored
@ -1,77 +0,0 @@
|
||||
# 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
|
||||
}
|
||||
```
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user