@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/src/request.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request.js
|
|
3
|
+
*
|
|
4
|
+
* Request class contains server only options
|
|
5
|
+
*
|
|
6
|
+
* All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {format as formatUrl} from 'node:url';
|
|
10
|
+
import {deprecate} from 'node:util';
|
|
11
|
+
import Headers from './headers.js';
|
|
12
|
+
import Body, {clone, extractContentType, getTotalBytes} from './body.js';
|
|
13
|
+
import {isAbortSignal} from './utils/is.js';
|
|
14
|
+
import {getSearch} from './utils/get-search.js';
|
|
15
|
+
import {
|
|
16
|
+
validateReferrerPolicy, determineRequestsReferrer, DEFAULT_REFERRER_POLICY
|
|
17
|
+
} from './utils/referrer.js';
|
|
18
|
+
|
|
19
|
+
const INTERNALS = Symbol('Request internals');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check if `obj` is an instance of Request.
|
|
23
|
+
*
|
|
24
|
+
* @param {*} object
|
|
25
|
+
* @return {boolean}
|
|
26
|
+
*/
|
|
27
|
+
const isRequest = object => {
|
|
28
|
+
return (
|
|
29
|
+
typeof object === 'object' &&
|
|
30
|
+
typeof object[INTERNALS] === 'object'
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const doBadDataWarn = deprecate(() => {},
|
|
35
|
+
'.data is not a valid RequestInit property, use .body instead',
|
|
36
|
+
'https://github.com/node-fetch/node-fetch/issues/1000 (request)');
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Request class
|
|
40
|
+
*
|
|
41
|
+
* Ref: https://fetch.spec.whatwg.org/#request-class
|
|
42
|
+
*
|
|
43
|
+
* @param Mixed input Url or Request instance
|
|
44
|
+
* @param Object init Custom options
|
|
45
|
+
* @return Void
|
|
46
|
+
*/
|
|
47
|
+
export default class Request extends Body {
|
|
48
|
+
constructor(input, init = {}) {
|
|
49
|
+
let parsedURL;
|
|
50
|
+
|
|
51
|
+
// Normalize input and force URL to be encoded as UTF-8 (https://github.com/node-fetch/node-fetch/issues/245)
|
|
52
|
+
if (isRequest(input)) {
|
|
53
|
+
parsedURL = new URL(input.url);
|
|
54
|
+
} else {
|
|
55
|
+
parsedURL = new URL(input);
|
|
56
|
+
input = {};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (parsedURL.username !== '' || parsedURL.password !== '') {
|
|
60
|
+
throw new TypeError(`${parsedURL} is an url with embedded credentials.`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let method = init.method || input.method || 'GET';
|
|
64
|
+
if (/^(delete|get|head|options|post|put)$/i.test(method)) {
|
|
65
|
+
method = method.toUpperCase();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!isRequest(init) && 'data' in init) {
|
|
69
|
+
doBadDataWarn();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// eslint-disable-next-line no-eq-null, eqeqeq
|
|
73
|
+
if ((init.body != null || (isRequest(input) && input.body !== null)) &&
|
|
74
|
+
(method === 'GET' || method === 'HEAD')) {
|
|
75
|
+
throw new TypeError('Request with GET/HEAD method cannot have body');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const inputBody = init.body ?
|
|
79
|
+
init.body :
|
|
80
|
+
(isRequest(input) && input.body !== null ?
|
|
81
|
+
clone(input) :
|
|
82
|
+
null);
|
|
83
|
+
|
|
84
|
+
super(inputBody, {
|
|
85
|
+
size: init.size || input.size || 0
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const headers = new Headers(init.headers || input.headers || {});
|
|
89
|
+
|
|
90
|
+
if (inputBody !== null && !headers.has('Content-Type')) {
|
|
91
|
+
const contentType = extractContentType(inputBody, this);
|
|
92
|
+
if (contentType) {
|
|
93
|
+
headers.set('Content-Type', contentType);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let signal = isRequest(input) ?
|
|
98
|
+
input.signal :
|
|
99
|
+
null;
|
|
100
|
+
if ('signal' in init) {
|
|
101
|
+
signal = init.signal;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// eslint-disable-next-line no-eq-null, eqeqeq
|
|
105
|
+
if (signal != null && !isAbortSignal(signal)) {
|
|
106
|
+
throw new TypeError('Expected signal to be an instanceof AbortSignal or EventTarget');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// §5.4, Request constructor steps, step 15.1
|
|
110
|
+
// eslint-disable-next-line no-eq-null, eqeqeq
|
|
111
|
+
let referrer = init.referrer == null ? input.referrer : init.referrer;
|
|
112
|
+
if (referrer === '') {
|
|
113
|
+
// §5.4, Request constructor steps, step 15.2
|
|
114
|
+
referrer = 'no-referrer';
|
|
115
|
+
} else if (referrer) {
|
|
116
|
+
// §5.4, Request constructor steps, step 15.3.1, 15.3.2
|
|
117
|
+
const parsedReferrer = new URL(referrer);
|
|
118
|
+
// §5.4, Request constructor steps, step 15.3.3, 15.3.4
|
|
119
|
+
referrer = /^about:(\/\/)?client$/.test(parsedReferrer) ? 'client' : parsedReferrer;
|
|
120
|
+
} else {
|
|
121
|
+
referrer = undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this[INTERNALS] = {
|
|
125
|
+
method,
|
|
126
|
+
redirect: init.redirect || input.redirect || 'follow',
|
|
127
|
+
headers,
|
|
128
|
+
parsedURL,
|
|
129
|
+
signal,
|
|
130
|
+
referrer
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Node-fetch-only options
|
|
134
|
+
this.follow = init.follow === undefined ? (input.follow === undefined ? 20 : input.follow) : init.follow;
|
|
135
|
+
this.compress = init.compress === undefined ? (input.compress === undefined ? true : input.compress) : init.compress;
|
|
136
|
+
this.counter = init.counter || input.counter || 0;
|
|
137
|
+
this.agent = init.agent || input.agent;
|
|
138
|
+
this.highWaterMark = init.highWaterMark || input.highWaterMark || 16384;
|
|
139
|
+
this.insecureHTTPParser = init.insecureHTTPParser || input.insecureHTTPParser || false;
|
|
140
|
+
|
|
141
|
+
// §5.4, Request constructor steps, step 16.
|
|
142
|
+
// Default is empty string per https://fetch.spec.whatwg.org/#concept-request-referrer-policy
|
|
143
|
+
this.referrerPolicy = init.referrerPolicy || input.referrerPolicy || '';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @returns {string} */
|
|
147
|
+
get method() {
|
|
148
|
+
return this[INTERNALS].method;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** @returns {string} */
|
|
152
|
+
get url() {
|
|
153
|
+
return formatUrl(this[INTERNALS].parsedURL);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** @returns {Headers} */
|
|
157
|
+
get headers() {
|
|
158
|
+
return this[INTERNALS].headers;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
get redirect() {
|
|
162
|
+
return this[INTERNALS].redirect;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** @returns {AbortSignal} */
|
|
166
|
+
get signal() {
|
|
167
|
+
return this[INTERNALS].signal;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// https://fetch.spec.whatwg.org/#dom-request-referrer
|
|
171
|
+
get referrer() {
|
|
172
|
+
if (this[INTERNALS].referrer === 'no-referrer') {
|
|
173
|
+
return '';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (this[INTERNALS].referrer === 'client') {
|
|
177
|
+
return 'about:client';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (this[INTERNALS].referrer) {
|
|
181
|
+
return this[INTERNALS].referrer.toString();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
get referrerPolicy() {
|
|
188
|
+
return this[INTERNALS].referrerPolicy;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
set referrerPolicy(referrerPolicy) {
|
|
192
|
+
this[INTERNALS].referrerPolicy = validateReferrerPolicy(referrerPolicy);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Clone this request
|
|
197
|
+
*
|
|
198
|
+
* @return Request
|
|
199
|
+
*/
|
|
200
|
+
clone() {
|
|
201
|
+
return new Request(this);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
get [Symbol.toStringTag]() {
|
|
205
|
+
return 'Request';
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
Object.defineProperties(Request.prototype, {
|
|
210
|
+
method: {enumerable: true},
|
|
211
|
+
url: {enumerable: true},
|
|
212
|
+
headers: {enumerable: true},
|
|
213
|
+
redirect: {enumerable: true},
|
|
214
|
+
clone: {enumerable: true},
|
|
215
|
+
signal: {enumerable: true},
|
|
216
|
+
referrer: {enumerable: true},
|
|
217
|
+
referrerPolicy: {enumerable: true}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Convert a Request to Node.js http request options.
|
|
222
|
+
*
|
|
223
|
+
* @param {Request} request - A Request instance
|
|
224
|
+
* @return The options object to be passed to http.request
|
|
225
|
+
*/
|
|
226
|
+
export const getNodeRequestOptions = request => {
|
|
227
|
+
const {parsedURL} = request[INTERNALS];
|
|
228
|
+
const headers = new Headers(request[INTERNALS].headers);
|
|
229
|
+
|
|
230
|
+
// Fetch step 1.3
|
|
231
|
+
if (!headers.has('Accept')) {
|
|
232
|
+
headers.set('Accept', '*/*');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// HTTP-network-or-cache fetch steps 2.4-2.7
|
|
236
|
+
let contentLengthValue = null;
|
|
237
|
+
if (request.body === null && /^(post|put)$/i.test(request.method)) {
|
|
238
|
+
contentLengthValue = '0';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (request.body !== null) {
|
|
242
|
+
const totalBytes = getTotalBytes(request);
|
|
243
|
+
// Set Content-Length if totalBytes is a number (that is not NaN)
|
|
244
|
+
if (typeof totalBytes === 'number' && !Number.isNaN(totalBytes)) {
|
|
245
|
+
contentLengthValue = String(totalBytes);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (contentLengthValue) {
|
|
250
|
+
headers.set('Content-Length', contentLengthValue);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 4.1. Main fetch, step 2.6
|
|
254
|
+
// > If request's referrer policy is the empty string, then set request's referrer policy to the
|
|
255
|
+
// > default referrer policy.
|
|
256
|
+
if (request.referrerPolicy === '') {
|
|
257
|
+
request.referrerPolicy = DEFAULT_REFERRER_POLICY;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 4.1. Main fetch, step 2.7
|
|
261
|
+
// > If request's referrer is not "no-referrer", set request's referrer to the result of invoking
|
|
262
|
+
// > determine request's referrer.
|
|
263
|
+
if (request.referrer && request.referrer !== 'no-referrer') {
|
|
264
|
+
request[INTERNALS].referrer = determineRequestsReferrer(request);
|
|
265
|
+
} else {
|
|
266
|
+
request[INTERNALS].referrer = 'no-referrer';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 4.5. HTTP-network-or-cache fetch, step 6.9
|
|
270
|
+
// > If httpRequest's referrer is a URL, then append `Referer`/httpRequest's referrer, serialized
|
|
271
|
+
// > and isomorphic encoded, to httpRequest's header list.
|
|
272
|
+
if (request[INTERNALS].referrer instanceof URL) {
|
|
273
|
+
headers.set('Referer', request.referrer);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// HTTP-network-or-cache fetch step 2.11
|
|
277
|
+
if (!headers.has('User-Agent')) {
|
|
278
|
+
headers.set('User-Agent', 'node-fetch');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// HTTP-network-or-cache fetch step 2.15
|
|
282
|
+
if (request.compress && !headers.has('Accept-Encoding')) {
|
|
283
|
+
headers.set('Accept-Encoding', 'gzip, deflate, br');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
let {agent} = request;
|
|
287
|
+
if (typeof agent === 'function') {
|
|
288
|
+
agent = agent(parsedURL);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// HTTP-network fetch step 4.2
|
|
292
|
+
// chunked encoding is handled by Node.js
|
|
293
|
+
|
|
294
|
+
const search = getSearch(parsedURL);
|
|
295
|
+
|
|
296
|
+
// Pass the full URL directly to request(), but overwrite the following
|
|
297
|
+
// options:
|
|
298
|
+
const options = {
|
|
299
|
+
// Overwrite search to retain trailing ? (issue #776)
|
|
300
|
+
path: parsedURL.pathname + search,
|
|
301
|
+
// The following options are not expressed in the URL
|
|
302
|
+
method: request.method,
|
|
303
|
+
headers: headers[Symbol.for('nodejs.util.inspect.custom')](),
|
|
304
|
+
insecureHTTPParser: request.insecureHTTPParser,
|
|
305
|
+
agent
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
/** @type {URL} */
|
|
310
|
+
parsedURL,
|
|
311
|
+
options
|
|
312
|
+
};
|
|
313
|
+
};
|
package/src/response.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response.js
|
|
3
|
+
*
|
|
4
|
+
* Response class provides content decoding
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Headers from './headers.js';
|
|
8
|
+
import Body, {clone, extractContentType} from './body.js';
|
|
9
|
+
import {isRedirect} from './utils/is-redirect.js';
|
|
10
|
+
|
|
11
|
+
const INTERNALS = Symbol('Response internals');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Response class
|
|
15
|
+
*
|
|
16
|
+
* Ref: https://fetch.spec.whatwg.org/#response-class
|
|
17
|
+
*
|
|
18
|
+
* @param Stream body Readable stream
|
|
19
|
+
* @param Object opts Response options
|
|
20
|
+
* @return Void
|
|
21
|
+
*/
|
|
22
|
+
export default class Response extends Body {
|
|
23
|
+
constructor(body = null, options = {}) {
|
|
24
|
+
super(body, options);
|
|
25
|
+
|
|
26
|
+
// eslint-disable-next-line no-eq-null, eqeqeq, no-negated-condition
|
|
27
|
+
const status = options.status != null ? options.status : 200;
|
|
28
|
+
|
|
29
|
+
const headers = new Headers(options.headers);
|
|
30
|
+
|
|
31
|
+
if (body !== null && !headers.has('Content-Type')) {
|
|
32
|
+
const contentType = extractContentType(body, this);
|
|
33
|
+
if (contentType) {
|
|
34
|
+
headers.append('Content-Type', contentType);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this[INTERNALS] = {
|
|
39
|
+
type: 'default',
|
|
40
|
+
url: options.url,
|
|
41
|
+
status,
|
|
42
|
+
statusText: options.statusText || '',
|
|
43
|
+
headers,
|
|
44
|
+
counter: options.counter,
|
|
45
|
+
highWaterMark: options.highWaterMark
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get type() {
|
|
50
|
+
return this[INTERNALS].type;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get url() {
|
|
54
|
+
return this[INTERNALS].url || '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get status() {
|
|
58
|
+
return this[INTERNALS].status;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Convenience property representing if the request ended normally
|
|
63
|
+
*/
|
|
64
|
+
get ok() {
|
|
65
|
+
return this[INTERNALS].status >= 200 && this[INTERNALS].status < 300;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get redirected() {
|
|
69
|
+
return this[INTERNALS].counter > 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get statusText() {
|
|
73
|
+
return this[INTERNALS].statusText;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get headers() {
|
|
77
|
+
return this[INTERNALS].headers;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get highWaterMark() {
|
|
81
|
+
return this[INTERNALS].highWaterMark;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Clone this response
|
|
86
|
+
*
|
|
87
|
+
* @return Response
|
|
88
|
+
*/
|
|
89
|
+
clone() {
|
|
90
|
+
return new Response(clone(this, this.highWaterMark), {
|
|
91
|
+
type: this.type,
|
|
92
|
+
url: this.url,
|
|
93
|
+
status: this.status,
|
|
94
|
+
statusText: this.statusText,
|
|
95
|
+
headers: this.headers,
|
|
96
|
+
ok: this.ok,
|
|
97
|
+
redirected: this.redirected,
|
|
98
|
+
size: this.size,
|
|
99
|
+
highWaterMark: this.highWaterMark
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {string} url The URL that the new response is to originate from.
|
|
105
|
+
* @param {number} status An optional status code for the response (e.g., 302.)
|
|
106
|
+
* @returns {Response} A Response object.
|
|
107
|
+
*/
|
|
108
|
+
static redirect(url, status = 302) {
|
|
109
|
+
if (!isRedirect(status)) {
|
|
110
|
+
throw new RangeError('Failed to execute "redirect" on "response": Invalid status code');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return new Response(null, {
|
|
114
|
+
headers: {
|
|
115
|
+
location: new URL(url).toString()
|
|
116
|
+
},
|
|
117
|
+
status
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static error() {
|
|
122
|
+
const response = new Response(null, {status: 0, statusText: ''});
|
|
123
|
+
response[INTERNALS].type = 'error';
|
|
124
|
+
return response;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static json(data = undefined, init = {}) {
|
|
128
|
+
const body = JSON.stringify(data);
|
|
129
|
+
|
|
130
|
+
if (body === undefined) {
|
|
131
|
+
throw new TypeError('data is not JSON serializable');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const headers = new Headers(init && init.headers);
|
|
135
|
+
|
|
136
|
+
if (!headers.has('content-type')) {
|
|
137
|
+
headers.set('content-type', 'application/json');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return new Response(body, {
|
|
141
|
+
...init,
|
|
142
|
+
headers
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
get [Symbol.toStringTag]() {
|
|
147
|
+
return 'Response';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
Object.defineProperties(Response.prototype, {
|
|
152
|
+
type: {enumerable: true},
|
|
153
|
+
url: {enumerable: true},
|
|
154
|
+
status: {enumerable: true},
|
|
155
|
+
ok: {enumerable: true},
|
|
156
|
+
redirected: {enumerable: true},
|
|
157
|
+
statusText: {enumerable: true},
|
|
158
|
+
headers: {enumerable: true},
|
|
159
|
+
clone: {enumerable: true}
|
|
160
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const getSearch = parsedURL => {
|
|
2
|
+
if (parsedURL.search) {
|
|
3
|
+
return parsedURL.search;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const lastOffset = parsedURL.href.length - 1;
|
|
7
|
+
const hash = parsedURL.hash || (parsedURL.href[lastOffset] === '#' ? '#' : '');
|
|
8
|
+
return parsedURL.href[lastOffset - hash.length] === '?' ? '?' : '';
|
|
9
|
+
};
|
package/src/utils/is.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is.js
|
|
3
|
+
*
|
|
4
|
+
* Object type checks.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const NAME = Symbol.toStringTag;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check if `obj` is a URLSearchParams object
|
|
11
|
+
* ref: https://github.com/node-fetch/node-fetch/issues/296#issuecomment-307598143
|
|
12
|
+
* @param {*} object - Object to check for
|
|
13
|
+
* @return {boolean}
|
|
14
|
+
*/
|
|
15
|
+
export const isURLSearchParameters = object => {
|
|
16
|
+
return (
|
|
17
|
+
typeof object === 'object' &&
|
|
18
|
+
typeof object.append === 'function' &&
|
|
19
|
+
typeof object.delete === 'function' &&
|
|
20
|
+
typeof object.get === 'function' &&
|
|
21
|
+
typeof object.getAll === 'function' &&
|
|
22
|
+
typeof object.has === 'function' &&
|
|
23
|
+
typeof object.set === 'function' &&
|
|
24
|
+
typeof object.sort === 'function' &&
|
|
25
|
+
object[NAME] === 'URLSearchParams'
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check if `object` is a W3C `Blob` object (which `File` inherits from)
|
|
31
|
+
* @param {*} object - Object to check for
|
|
32
|
+
* @return {boolean}
|
|
33
|
+
*/
|
|
34
|
+
export const isBlob = object => {
|
|
35
|
+
return (
|
|
36
|
+
object &&
|
|
37
|
+
typeof object === 'object' &&
|
|
38
|
+
typeof object.arrayBuffer === 'function' &&
|
|
39
|
+
typeof object.type === 'string' &&
|
|
40
|
+
typeof object.stream === 'function' &&
|
|
41
|
+
typeof object.constructor === 'function' &&
|
|
42
|
+
/^(Blob|File)$/.test(object[NAME])
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if `obj` is an instance of AbortSignal.
|
|
48
|
+
* @param {*} object - Object to check for
|
|
49
|
+
* @return {boolean}
|
|
50
|
+
*/
|
|
51
|
+
export const isAbortSignal = object => {
|
|
52
|
+
return (
|
|
53
|
+
typeof object === 'object' && (
|
|
54
|
+
object[NAME] === 'AbortSignal' ||
|
|
55
|
+
object[NAME] === 'EventTarget'
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* isDomainOrSubdomain reports whether sub is a subdomain (or exact match) of
|
|
62
|
+
* the parent domain.
|
|
63
|
+
*
|
|
64
|
+
* Both domains must already be in canonical form.
|
|
65
|
+
* @param {string|URL} original
|
|
66
|
+
* @param {string|URL} destination
|
|
67
|
+
*/
|
|
68
|
+
export const isDomainOrSubdomain = (destination, original) => {
|
|
69
|
+
const orig = new URL(original).hostname;
|
|
70
|
+
const dest = new URL(destination).hostname;
|
|
71
|
+
|
|
72
|
+
return orig === dest || orig.endsWith(`.${dest}`);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* isSameProtocol reports whether the two provided URLs use the same protocol.
|
|
77
|
+
*
|
|
78
|
+
* Both domains must already be in canonical form.
|
|
79
|
+
* @param {string|URL} original
|
|
80
|
+
* @param {string|URL} destination
|
|
81
|
+
*/
|
|
82
|
+
export const isSameProtocol = (destination, original) => {
|
|
83
|
+
const orig = new URL(original).protocol;
|
|
84
|
+
const dest = new URL(destination).protocol;
|
|
85
|
+
|
|
86
|
+
return orig === dest;
|
|
87
|
+
};
|