@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/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,10 @@
1
+ import {FetchBaseError} from './base.js';
2
+
3
+ /**
4
+ * AbortError interface for cancelled requests
5
+ */
6
+ export class AbortError extends FetchBaseError {
7
+ constructor(message, type = 'aborted') {
8
+ super(message, type);
9
+ }
10
+ }
@@ -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
+ }