@depup/node-fetch 3.3.2-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@types/index.d.ts +219 -0
- package/LICENSE.md +22 -0
- package/README.md +872 -0
- package/package.json +131 -0
- package/src/body.js +397 -0
- package/src/errors/abort-error.js +10 -0
- package/src/errors/base.js +17 -0
- package/src/errors/fetch-error.js +26 -0
- package/src/headers.js +267 -0
- package/src/index.js +417 -0
- package/src/request.js +313 -0
- package/src/response.js +160 -0
- package/src/utils/get-search.js +9 -0
- package/src/utils/is-redirect.js +11 -0
- package/src/utils/is.js +87 -0
- package/src/utils/multipart-parser.js +432 -0
- package/src/utils/referrer.js +340 -0
package/package.json
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@depup/node-fetch",
|
|
3
|
+
"version": "3.3.2-depup.0",
|
|
4
|
+
"description": "A light-weight module that brings Fetch API to node.js",
|
|
5
|
+
"main": "./src/index.js",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"src",
|
|
10
|
+
"@types/index.d.ts"
|
|
11
|
+
],
|
|
12
|
+
"types": "./@types/index.d.ts",
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "mocha",
|
|
18
|
+
"coverage": "c8 report --reporter=text-lcov | coveralls",
|
|
19
|
+
"test-types": "tsd",
|
|
20
|
+
"lint": "xo"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/node-fetch/node-fetch.git"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"fetch",
|
|
28
|
+
"http",
|
|
29
|
+
"promise",
|
|
30
|
+
"request",
|
|
31
|
+
"curl",
|
|
32
|
+
"wget",
|
|
33
|
+
"xhr",
|
|
34
|
+
"whatwg"
|
|
35
|
+
],
|
|
36
|
+
"author": "David Frank",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/node-fetch/node-fetch/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/node-fetch/node-fetch",
|
|
42
|
+
"funding": {
|
|
43
|
+
"type": "opencollective",
|
|
44
|
+
"url": "https://opencollective.com/node-fetch"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"abort-controller": "^3.0.0",
|
|
48
|
+
"abortcontroller-polyfill": "^1.7.1",
|
|
49
|
+
"busboy": "^1.4.0",
|
|
50
|
+
"c8": "^7.7.2",
|
|
51
|
+
"chai": "^4.3.4",
|
|
52
|
+
"chai-as-promised": "^7.1.1",
|
|
53
|
+
"chai-iterator": "^3.0.2",
|
|
54
|
+
"chai-string": "^1.5.0",
|
|
55
|
+
"coveralls": "^3.1.0",
|
|
56
|
+
"form-data": "^4.0.0",
|
|
57
|
+
"formdata-node": "^4.2.4",
|
|
58
|
+
"mocha": "^9.1.3",
|
|
59
|
+
"p-timeout": "^5.0.0",
|
|
60
|
+
"stream-consumers": "^1.0.1",
|
|
61
|
+
"tsd": "^0.14.0",
|
|
62
|
+
"xo": "^0.39.1"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"data-uri-to-buffer": "^6.0.2",
|
|
66
|
+
"fetch-blob": "^4.0.0",
|
|
67
|
+
"formdata-polyfill": "^4.0.10"
|
|
68
|
+
},
|
|
69
|
+
"tsd": {
|
|
70
|
+
"cwd": "@types",
|
|
71
|
+
"compilerOptions": {
|
|
72
|
+
"esModuleInterop": true
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"xo": {
|
|
76
|
+
"envs": [
|
|
77
|
+
"node",
|
|
78
|
+
"browser"
|
|
79
|
+
],
|
|
80
|
+
"ignores": [
|
|
81
|
+
"example.js"
|
|
82
|
+
],
|
|
83
|
+
"rules": {
|
|
84
|
+
"complexity": 0,
|
|
85
|
+
"import/extensions": 0,
|
|
86
|
+
"import/no-useless-path-segments": 0,
|
|
87
|
+
"import/no-anonymous-default-export": 0,
|
|
88
|
+
"import/no-named-as-default": 0,
|
|
89
|
+
"unicorn/import-index": 0,
|
|
90
|
+
"unicorn/no-array-reduce": 0,
|
|
91
|
+
"unicorn/prefer-node-protocol": 0,
|
|
92
|
+
"unicorn/numeric-separators-style": 0,
|
|
93
|
+
"unicorn/explicit-length-check": 0,
|
|
94
|
+
"capitalized-comments": 0,
|
|
95
|
+
"node/no-unsupported-features/es-syntax": 0,
|
|
96
|
+
"@typescript-eslint/member-ordering": 0
|
|
97
|
+
},
|
|
98
|
+
"overrides": [
|
|
99
|
+
{
|
|
100
|
+
"files": "test/**/*.js",
|
|
101
|
+
"envs": [
|
|
102
|
+
"node",
|
|
103
|
+
"mocha"
|
|
104
|
+
],
|
|
105
|
+
"rules": {
|
|
106
|
+
"max-nested-callbacks": 0,
|
|
107
|
+
"no-unused-expressions": 0,
|
|
108
|
+
"no-warning-comments": 0,
|
|
109
|
+
"new-cap": 0,
|
|
110
|
+
"guard-for-in": 0,
|
|
111
|
+
"unicorn/no-array-for-each": 0,
|
|
112
|
+
"unicorn/prevent-abbreviations": 0,
|
|
113
|
+
"promise/prefer-await-to-then": 0,
|
|
114
|
+
"ava/no-import-test-files": 0
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"runkitExampleFilename": "example.js",
|
|
120
|
+
"release": {
|
|
121
|
+
"branches": [
|
|
122
|
+
"+([0-9]).x",
|
|
123
|
+
"main",
|
|
124
|
+
"next",
|
|
125
|
+
{
|
|
126
|
+
"name": "beta",
|
|
127
|
+
"prerelease": true
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
}
|
package/src/body.js
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Body.js
|
|
4
|
+
*
|
|
5
|
+
* Body interface provides common methods for Request and Response
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import Stream, {PassThrough} from 'node:stream';
|
|
9
|
+
import {types, deprecate, promisify} from 'node:util';
|
|
10
|
+
import {Buffer} from 'node:buffer';
|
|
11
|
+
|
|
12
|
+
import Blob from 'fetch-blob';
|
|
13
|
+
import {FormData, formDataToBlob} from 'formdata-polyfill/esm.min.js';
|
|
14
|
+
|
|
15
|
+
import {FetchError} from './errors/fetch-error.js';
|
|
16
|
+
import {FetchBaseError} from './errors/base.js';
|
|
17
|
+
import {isBlob, isURLSearchParameters} from './utils/is.js';
|
|
18
|
+
|
|
19
|
+
const pipeline = promisify(Stream.pipeline);
|
|
20
|
+
const INTERNALS = Symbol('Body internals');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Body mixin
|
|
24
|
+
*
|
|
25
|
+
* Ref: https://fetch.spec.whatwg.org/#body
|
|
26
|
+
*
|
|
27
|
+
* @param Stream body Readable stream
|
|
28
|
+
* @param Object opts Response options
|
|
29
|
+
* @return Void
|
|
30
|
+
*/
|
|
31
|
+
export default class Body {
|
|
32
|
+
constructor(body, {
|
|
33
|
+
size = 0
|
|
34
|
+
} = {}) {
|
|
35
|
+
let boundary = null;
|
|
36
|
+
|
|
37
|
+
if (body === null) {
|
|
38
|
+
// Body is undefined or null
|
|
39
|
+
body = null;
|
|
40
|
+
} else if (isURLSearchParameters(body)) {
|
|
41
|
+
// Body is a URLSearchParams
|
|
42
|
+
body = Buffer.from(body.toString());
|
|
43
|
+
} else if (isBlob(body)) {
|
|
44
|
+
// Body is blob
|
|
45
|
+
} else if (Buffer.isBuffer(body)) {
|
|
46
|
+
// Body is Buffer
|
|
47
|
+
} else if (types.isAnyArrayBuffer(body)) {
|
|
48
|
+
// Body is ArrayBuffer
|
|
49
|
+
body = Buffer.from(body);
|
|
50
|
+
} else if (ArrayBuffer.isView(body)) {
|
|
51
|
+
// Body is ArrayBufferView
|
|
52
|
+
body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
|
|
53
|
+
} else if (body instanceof Stream) {
|
|
54
|
+
// Body is stream
|
|
55
|
+
} else if (body instanceof FormData) {
|
|
56
|
+
// Body is FormData
|
|
57
|
+
body = formDataToBlob(body);
|
|
58
|
+
boundary = body.type.split('=')[1];
|
|
59
|
+
} else {
|
|
60
|
+
// None of the above
|
|
61
|
+
// coerce to string then buffer
|
|
62
|
+
body = Buffer.from(String(body));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let stream = body;
|
|
66
|
+
|
|
67
|
+
if (Buffer.isBuffer(body)) {
|
|
68
|
+
stream = Stream.Readable.from(body);
|
|
69
|
+
} else if (isBlob(body)) {
|
|
70
|
+
stream = Stream.Readable.from(body.stream());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this[INTERNALS] = {
|
|
74
|
+
body,
|
|
75
|
+
stream,
|
|
76
|
+
boundary,
|
|
77
|
+
disturbed: false,
|
|
78
|
+
error: null
|
|
79
|
+
};
|
|
80
|
+
this.size = size;
|
|
81
|
+
|
|
82
|
+
if (body instanceof Stream) {
|
|
83
|
+
body.on('error', error_ => {
|
|
84
|
+
const error = error_ instanceof FetchBaseError ?
|
|
85
|
+
error_ :
|
|
86
|
+
new FetchError(`Invalid response body while trying to fetch ${this.url}: ${error_.message}`, 'system', error_);
|
|
87
|
+
this[INTERNALS].error = error;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get body() {
|
|
93
|
+
return this[INTERNALS].stream;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
get bodyUsed() {
|
|
97
|
+
return this[INTERNALS].disturbed;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Decode response as ArrayBuffer
|
|
102
|
+
*
|
|
103
|
+
* @return Promise
|
|
104
|
+
*/
|
|
105
|
+
async arrayBuffer() {
|
|
106
|
+
const {buffer, byteOffset, byteLength} = await consumeBody(this);
|
|
107
|
+
return buffer.slice(byteOffset, byteOffset + byteLength);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async formData() {
|
|
111
|
+
const ct = this.headers.get('content-type');
|
|
112
|
+
|
|
113
|
+
if (ct.startsWith('application/x-www-form-urlencoded')) {
|
|
114
|
+
const formData = new FormData();
|
|
115
|
+
const parameters = new URLSearchParams(await this.text());
|
|
116
|
+
|
|
117
|
+
for (const [name, value] of parameters) {
|
|
118
|
+
formData.append(name, value);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return formData;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const {toFormData} = await import('./utils/multipart-parser.js');
|
|
125
|
+
return toFormData(this.body, ct);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Return raw response as Blob
|
|
130
|
+
*
|
|
131
|
+
* @return Promise
|
|
132
|
+
*/
|
|
133
|
+
async blob() {
|
|
134
|
+
const ct = (this.headers && this.headers.get('content-type')) || (this[INTERNALS].body && this[INTERNALS].body.type) || '';
|
|
135
|
+
const buf = await this.arrayBuffer();
|
|
136
|
+
|
|
137
|
+
return new Blob([buf], {
|
|
138
|
+
type: ct
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Decode response as json
|
|
144
|
+
*
|
|
145
|
+
* @return Promise
|
|
146
|
+
*/
|
|
147
|
+
async json() {
|
|
148
|
+
const text = await this.text();
|
|
149
|
+
return JSON.parse(text);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Decode response as text
|
|
154
|
+
*
|
|
155
|
+
* @return Promise
|
|
156
|
+
*/
|
|
157
|
+
async text() {
|
|
158
|
+
const buffer = await consumeBody(this);
|
|
159
|
+
return new TextDecoder().decode(buffer);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Decode response as buffer (non-spec api)
|
|
164
|
+
*
|
|
165
|
+
* @return Promise
|
|
166
|
+
*/
|
|
167
|
+
buffer() {
|
|
168
|
+
return consumeBody(this);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
Body.prototype.buffer = deprecate(Body.prototype.buffer, 'Please use \'response.arrayBuffer()\' instead of \'response.buffer()\'', 'node-fetch#buffer');
|
|
173
|
+
|
|
174
|
+
// In browsers, all properties are enumerable.
|
|
175
|
+
Object.defineProperties(Body.prototype, {
|
|
176
|
+
body: {enumerable: true},
|
|
177
|
+
bodyUsed: {enumerable: true},
|
|
178
|
+
arrayBuffer: {enumerable: true},
|
|
179
|
+
blob: {enumerable: true},
|
|
180
|
+
json: {enumerable: true},
|
|
181
|
+
text: {enumerable: true},
|
|
182
|
+
data: {get: deprecate(() => {},
|
|
183
|
+
'data doesn\'t exist, use json(), text(), arrayBuffer(), or body instead',
|
|
184
|
+
'https://github.com/node-fetch/node-fetch/issues/1000 (response)')}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Consume and convert an entire Body to a Buffer.
|
|
189
|
+
*
|
|
190
|
+
* Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body
|
|
191
|
+
*
|
|
192
|
+
* @return Promise
|
|
193
|
+
*/
|
|
194
|
+
async function consumeBody(data) {
|
|
195
|
+
if (data[INTERNALS].disturbed) {
|
|
196
|
+
throw new TypeError(`body used already for: ${data.url}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
data[INTERNALS].disturbed = true;
|
|
200
|
+
|
|
201
|
+
if (data[INTERNALS].error) {
|
|
202
|
+
throw data[INTERNALS].error;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const {body} = data;
|
|
206
|
+
|
|
207
|
+
// Body is null
|
|
208
|
+
if (body === null) {
|
|
209
|
+
return Buffer.alloc(0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* c8 ignore next 3 */
|
|
213
|
+
if (!(body instanceof Stream)) {
|
|
214
|
+
return Buffer.alloc(0);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Body is stream
|
|
218
|
+
// get ready to actually consume the body
|
|
219
|
+
const accum = [];
|
|
220
|
+
let accumBytes = 0;
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
for await (const chunk of body) {
|
|
224
|
+
if (data.size > 0 && accumBytes + chunk.length > data.size) {
|
|
225
|
+
const error = new FetchError(`content size at ${data.url} over limit: ${data.size}`, 'max-size');
|
|
226
|
+
body.destroy(error);
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
accumBytes += chunk.length;
|
|
231
|
+
accum.push(chunk);
|
|
232
|
+
}
|
|
233
|
+
} catch (error) {
|
|
234
|
+
const error_ = error instanceof FetchBaseError ? error : new FetchError(`Invalid response body while trying to fetch ${data.url}: ${error.message}`, 'system', error);
|
|
235
|
+
throw error_;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (body.readableEnded === true || body._readableState.ended === true) {
|
|
239
|
+
try {
|
|
240
|
+
if (accum.every(c => typeof c === 'string')) {
|
|
241
|
+
return Buffer.from(accum.join(''));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return Buffer.concat(accum, accumBytes);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
throw new FetchError(`Could not create Buffer from response body for ${data.url}: ${error.message}`, 'system', error);
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
throw new FetchError(`Premature close of server response while trying to fetch ${data.url}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Clone body given Res/Req instance
|
|
255
|
+
*
|
|
256
|
+
* @param Mixed instance Response or Request instance
|
|
257
|
+
* @param String highWaterMark highWaterMark for both PassThrough body streams
|
|
258
|
+
* @return Mixed
|
|
259
|
+
*/
|
|
260
|
+
export const clone = (instance, highWaterMark) => {
|
|
261
|
+
let p1;
|
|
262
|
+
let p2;
|
|
263
|
+
let {body} = instance[INTERNALS];
|
|
264
|
+
|
|
265
|
+
// Don't allow cloning a used body
|
|
266
|
+
if (instance.bodyUsed) {
|
|
267
|
+
throw new Error('cannot clone body after it is used');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Check that body is a stream and not form-data object
|
|
271
|
+
// note: we can't clone the form-data object without having it as a dependency
|
|
272
|
+
if ((body instanceof Stream) && (typeof body.getBoundary !== 'function')) {
|
|
273
|
+
// Tee instance body
|
|
274
|
+
p1 = new PassThrough({highWaterMark});
|
|
275
|
+
p2 = new PassThrough({highWaterMark});
|
|
276
|
+
body.pipe(p1);
|
|
277
|
+
body.pipe(p2);
|
|
278
|
+
// Set instance body to teed body and return the other teed body
|
|
279
|
+
instance[INTERNALS].stream = p1;
|
|
280
|
+
body = p2;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return body;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const getNonSpecFormDataBoundary = deprecate(
|
|
287
|
+
body => body.getBoundary(),
|
|
288
|
+
'form-data doesn\'t follow the spec and requires special treatment. Use alternative package',
|
|
289
|
+
'https://github.com/node-fetch/node-fetch/issues/1167'
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Performs the operation "extract a `Content-Type` value from |object|" as
|
|
294
|
+
* specified in the specification:
|
|
295
|
+
* https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
|
296
|
+
*
|
|
297
|
+
* This function assumes that instance.body is present.
|
|
298
|
+
*
|
|
299
|
+
* @param {any} body Any options.body input
|
|
300
|
+
* @returns {string | null}
|
|
301
|
+
*/
|
|
302
|
+
export const extractContentType = (body, request) => {
|
|
303
|
+
// Body is null or undefined
|
|
304
|
+
if (body === null) {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Body is string
|
|
309
|
+
if (typeof body === 'string') {
|
|
310
|
+
return 'text/plain;charset=UTF-8';
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Body is a URLSearchParams
|
|
314
|
+
if (isURLSearchParameters(body)) {
|
|
315
|
+
return 'application/x-www-form-urlencoded;charset=UTF-8';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Body is blob
|
|
319
|
+
if (isBlob(body)) {
|
|
320
|
+
return body.type || null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Body is a Buffer (Buffer, ArrayBuffer or ArrayBufferView)
|
|
324
|
+
if (Buffer.isBuffer(body) || types.isAnyArrayBuffer(body) || ArrayBuffer.isView(body)) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (body instanceof FormData) {
|
|
329
|
+
return `multipart/form-data; boundary=${request[INTERNALS].boundary}`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Detect form data input from form-data module
|
|
333
|
+
if (body && typeof body.getBoundary === 'function') {
|
|
334
|
+
return `multipart/form-data;boundary=${getNonSpecFormDataBoundary(body)}`;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Body is stream - can't really do much about this
|
|
338
|
+
if (body instanceof Stream) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Body constructor defaults other things to string
|
|
343
|
+
return 'text/plain;charset=UTF-8';
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* The Fetch Standard treats this as if "total bytes" is a property on the body.
|
|
348
|
+
* For us, we have to explicitly get it with a function.
|
|
349
|
+
*
|
|
350
|
+
* ref: https://fetch.spec.whatwg.org/#concept-body-total-bytes
|
|
351
|
+
*
|
|
352
|
+
* @param {any} obj.body Body object from the Body instance.
|
|
353
|
+
* @returns {number | null}
|
|
354
|
+
*/
|
|
355
|
+
export const getTotalBytes = request => {
|
|
356
|
+
const {body} = request[INTERNALS];
|
|
357
|
+
|
|
358
|
+
// Body is null or undefined
|
|
359
|
+
if (body === null) {
|
|
360
|
+
return 0;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Body is Blob
|
|
364
|
+
if (isBlob(body)) {
|
|
365
|
+
return body.size;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Body is Buffer
|
|
369
|
+
if (Buffer.isBuffer(body)) {
|
|
370
|
+
return body.length;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Detect form data input from form-data module
|
|
374
|
+
if (body && typeof body.getLengthSync === 'function') {
|
|
375
|
+
return body.hasKnownLength && body.hasKnownLength() ? body.getLengthSync() : null;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Body is stream
|
|
379
|
+
return null;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Write a Body to a Node.js WritableStream (e.g. http.Request) object.
|
|
384
|
+
*
|
|
385
|
+
* @param {Stream.Writable} dest The stream to write to.
|
|
386
|
+
* @param obj.body Body object from the Body instance.
|
|
387
|
+
* @returns {Promise<void>}
|
|
388
|
+
*/
|
|
389
|
+
export const writeToStream = async (dest, {body}) => {
|
|
390
|
+
if (body === null) {
|
|
391
|
+
// Body is null
|
|
392
|
+
dest.end();
|
|
393
|
+
} else {
|
|
394
|
+
// Body is stream
|
|
395
|
+
await pipeline(body, dest);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class FetchBaseError extends Error {
|
|
2
|
+
constructor(message, type) {
|
|
3
|
+
super(message);
|
|
4
|
+
// Hide custom error implementation details from end-users
|
|
5
|
+
Error.captureStackTrace(this, this.constructor);
|
|
6
|
+
|
|
7
|
+
this.type = type;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get name() {
|
|
11
|
+
return this.constructor.name;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get [Symbol.toStringTag]() {
|
|
15
|
+
return this.constructor.name;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
import {FetchBaseError} from './base.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {{ address?: string, code: string, dest?: string, errno: number, info?: object, message: string, path?: string, port?: number, syscall: string}} SystemError
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* FetchError interface for operational errors
|
|
10
|
+
*/
|
|
11
|
+
export class FetchError extends FetchBaseError {
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} message - Error message for human
|
|
14
|
+
* @param {string} [type] - Error type for machine
|
|
15
|
+
* @param {SystemError} [systemError] - For Node.js system error
|
|
16
|
+
*/
|
|
17
|
+
constructor(message, type, systemError) {
|
|
18
|
+
super(message, type);
|
|
19
|
+
// When err.type is `system`, err.erroredSysCall contains system error and err.code contains system error code
|
|
20
|
+
if (systemError) {
|
|
21
|
+
// eslint-disable-next-line no-multi-assign
|
|
22
|
+
this.code = this.errno = systemError.code;
|
|
23
|
+
this.erroredSysCall = systemError.syscall;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|