@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/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
+ };
@@ -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
+ };
@@ -0,0 +1,11 @@
1
+ const redirectStatus = new Set([301, 302, 303, 307, 308]);
2
+
3
+ /**
4
+ * Redirect code matching
5
+ *
6
+ * @param {number} code - Status code
7
+ * @return {boolean}
8
+ */
9
+ export const isRedirect = code => {
10
+ return redirectStatus.has(code);
11
+ };
@@ -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
+ };