node-fetch
Advanced tools
+7
-7
@@ -14,13 +14,13 @@ "use strict"; | ||
| var global = getGlobal(); | ||
| var globalObject = getGlobal(); | ||
| module.exports = exports = global.fetch; | ||
| module.exports = exports = globalObject.fetch; | ||
| // Needed for TypeScript and Webpack. | ||
| if (global.fetch) { | ||
| exports.default = global.fetch.bind(global); | ||
| if (globalObject.fetch) { | ||
| exports.default = globalObject.fetch.bind(global); | ||
| } | ||
| exports.Headers = global.Headers; | ||
| exports.Request = global.Request; | ||
| exports.Response = global.Response; | ||
| exports.Headers = globalObject.Headers; | ||
| exports.Request = globalObject.Request; | ||
| exports.Response = globalObject.Response; |
+90
-2
@@ -1417,2 +1417,16 @@ process.emitWarning("The .es.js file is deprecated. Use .mjs instead."); | ||
| /** | ||
| * isSameProtocol reports whether the two provided URLs use the same protocol. | ||
| * | ||
| * Both domains must already be in canonical form. | ||
| * @param {string|URL} original | ||
| * @param {string|URL} destination | ||
| */ | ||
| const isSameProtocol = function isSameProtocol(destination, original) { | ||
| const orig = new URL$1(original).protocol; | ||
| const dest = new URL$1(destination).protocol; | ||
| return orig === dest; | ||
| }; | ||
| /** | ||
| * Fetch function | ||
@@ -1448,3 +1462,3 @@ * | ||
| if (request.body && request.body instanceof Stream.Readable) { | ||
| request.body.destroy(error); | ||
| destroyStream(request.body, error); | ||
| } | ||
@@ -1490,5 +1504,37 @@ if (!response || !response.body) return; | ||
| reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); | ||
| if (response && response.body) { | ||
| destroyStream(response.body, err); | ||
| } | ||
| finalize(); | ||
| }); | ||
| fixResponseChunkedTransferBadEnding(req, function (err) { | ||
| if (signal && signal.aborted) { | ||
| return; | ||
| } | ||
| destroyStream(response.body, err); | ||
| }); | ||
| /* c8 ignore next 18 */ | ||
| if (parseInt(process.version.substring(1)) < 14) { | ||
| // 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. | ||
| req.on('socket', function (s) { | ||
| s.addListener('close', function (hadError) { | ||
| // if a data listener is still present we didn't end cleanly | ||
| const hasDataListener = s.listenerCount('data') > 0; | ||
| // if end happened before close but the socket didn't emit an error, do it now | ||
| if (response && hasDataListener && !hadError && !(signal && signal.aborted)) { | ||
| const err = new Error('Premature close'); | ||
| err.code = 'ERR_STREAM_PREMATURE_CLOSE'; | ||
| response.body.emit('error', err); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| req.on('response', function (res) { | ||
@@ -1565,3 +1611,3 @@ clearTimeout(reqTimeout); | ||
| if (!isDomainOrSubdomain(request.url, locationURL)) { | ||
| if (!isDomainOrSubdomain(request.url, locationURL) || !isSameProtocol(request.url, locationURL)) { | ||
| for (const name of ['authorization', 'www-authenticate', 'cookie', 'cookie2']) { | ||
@@ -1659,2 +1705,9 @@ requestOpts.headers.delete(name); | ||
| }); | ||
| raw.on('end', function () { | ||
| // some old IIS servers return zero-length OK deflate responses, so 'data' is never emitted. | ||
| if (!response) { | ||
| response = new Response(body, response_options); | ||
| resolve(response); | ||
| } | ||
| }); | ||
| return; | ||
@@ -1679,2 +1732,37 @@ } | ||
| } | ||
| function fixResponseChunkedTransferBadEnding(request, errorCallback) { | ||
| let socket; | ||
| request.on('socket', function (s) { | ||
| socket = s; | ||
| }); | ||
| request.on('response', function (response) { | ||
| const headers = response.headers; | ||
| if (headers['transfer-encoding'] === 'chunked' && !headers['content-length']) { | ||
| response.once('close', function (hadError) { | ||
| // if a data listener is still present we didn't end cleanly | ||
| const hasDataListener = socket.listenerCount('data') > 0; | ||
| if (hasDataListener && !hadError) { | ||
| const err = new Error('Premature close'); | ||
| err.code = 'ERR_STREAM_PREMATURE_CLOSE'; | ||
| errorCallback(err); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| function destroyStream(stream, err) { | ||
| if (stream.destroy) { | ||
| stream.destroy(err); | ||
| } else { | ||
| // node < 8 | ||
| stream.emit('error', err); | ||
| stream.end(); | ||
| } | ||
| } | ||
| /** | ||
@@ -1681,0 +1769,0 @@ * Redirect code matching |
+90
-2
@@ -1421,2 +1421,16 @@ 'use strict'; | ||
| /** | ||
| * isSameProtocol reports whether the two provided URLs use the same protocol. | ||
| * | ||
| * Both domains must already be in canonical form. | ||
| * @param {string|URL} original | ||
| * @param {string|URL} destination | ||
| */ | ||
| const isSameProtocol = function isSameProtocol(destination, original) { | ||
| const orig = new URL$1(original).protocol; | ||
| const dest = new URL$1(destination).protocol; | ||
| return orig === dest; | ||
| }; | ||
| /** | ||
| * Fetch function | ||
@@ -1452,3 +1466,3 @@ * | ||
| if (request.body && request.body instanceof Stream.Readable) { | ||
| request.body.destroy(error); | ||
| destroyStream(request.body, error); | ||
| } | ||
@@ -1494,5 +1508,37 @@ if (!response || !response.body) return; | ||
| reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); | ||
| if (response && response.body) { | ||
| destroyStream(response.body, err); | ||
| } | ||
| finalize(); | ||
| }); | ||
| fixResponseChunkedTransferBadEnding(req, function (err) { | ||
| if (signal && signal.aborted) { | ||
| return; | ||
| } | ||
| destroyStream(response.body, err); | ||
| }); | ||
| /* c8 ignore next 18 */ | ||
| if (parseInt(process.version.substring(1)) < 14) { | ||
| // 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. | ||
| req.on('socket', function (s) { | ||
| s.addListener('close', function (hadError) { | ||
| // if a data listener is still present we didn't end cleanly | ||
| const hasDataListener = s.listenerCount('data') > 0; | ||
| // if end happened before close but the socket didn't emit an error, do it now | ||
| if (response && hasDataListener && !hadError && !(signal && signal.aborted)) { | ||
| const err = new Error('Premature close'); | ||
| err.code = 'ERR_STREAM_PREMATURE_CLOSE'; | ||
| response.body.emit('error', err); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| req.on('response', function (res) { | ||
@@ -1569,3 +1615,3 @@ clearTimeout(reqTimeout); | ||
| if (!isDomainOrSubdomain(request.url, locationURL)) { | ||
| if (!isDomainOrSubdomain(request.url, locationURL) || !isSameProtocol(request.url, locationURL)) { | ||
| for (const name of ['authorization', 'www-authenticate', 'cookie', 'cookie2']) { | ||
@@ -1663,2 +1709,9 @@ requestOpts.headers.delete(name); | ||
| }); | ||
| raw.on('end', function () { | ||
| // some old IIS servers return zero-length OK deflate responses, so 'data' is never emitted. | ||
| if (!response) { | ||
| response = new Response(body, response_options); | ||
| resolve(response); | ||
| } | ||
| }); | ||
| return; | ||
@@ -1683,2 +1736,37 @@ } | ||
| } | ||
| function fixResponseChunkedTransferBadEnding(request, errorCallback) { | ||
| let socket; | ||
| request.on('socket', function (s) { | ||
| socket = s; | ||
| }); | ||
| request.on('response', function (response) { | ||
| const headers = response.headers; | ||
| if (headers['transfer-encoding'] === 'chunked' && !headers['content-length']) { | ||
| response.once('close', function (hadError) { | ||
| // if a data listener is still present we didn't end cleanly | ||
| const hasDataListener = socket.listenerCount('data') > 0; | ||
| if (hasDataListener && !hadError) { | ||
| const err = new Error('Premature close'); | ||
| err.code = 'ERR_STREAM_PREMATURE_CLOSE'; | ||
| errorCallback(err); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| function destroyStream(stream, err) { | ||
| if (stream.destroy) { | ||
| stream.destroy(err); | ||
| } else { | ||
| // node < 8 | ||
| stream.emit('error', err); | ||
| stream.end(); | ||
| } | ||
| } | ||
| /** | ||
@@ -1685,0 +1773,0 @@ * Redirect code matching |
+90
-2
@@ -1415,2 +1415,16 @@ import Stream from 'stream'; | ||
| /** | ||
| * isSameProtocol reports whether the two provided URLs use the same protocol. | ||
| * | ||
| * Both domains must already be in canonical form. | ||
| * @param {string|URL} original | ||
| * @param {string|URL} destination | ||
| */ | ||
| const isSameProtocol = function isSameProtocol(destination, original) { | ||
| const orig = new URL$1(original).protocol; | ||
| const dest = new URL$1(destination).protocol; | ||
| return orig === dest; | ||
| }; | ||
| /** | ||
| * Fetch function | ||
@@ -1446,3 +1460,3 @@ * | ||
| if (request.body && request.body instanceof Stream.Readable) { | ||
| request.body.destroy(error); | ||
| destroyStream(request.body, error); | ||
| } | ||
@@ -1488,5 +1502,37 @@ if (!response || !response.body) return; | ||
| reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); | ||
| if (response && response.body) { | ||
| destroyStream(response.body, err); | ||
| } | ||
| finalize(); | ||
| }); | ||
| fixResponseChunkedTransferBadEnding(req, function (err) { | ||
| if (signal && signal.aborted) { | ||
| return; | ||
| } | ||
| destroyStream(response.body, err); | ||
| }); | ||
| /* c8 ignore next 18 */ | ||
| if (parseInt(process.version.substring(1)) < 14) { | ||
| // 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. | ||
| req.on('socket', function (s) { | ||
| s.addListener('close', function (hadError) { | ||
| // if a data listener is still present we didn't end cleanly | ||
| const hasDataListener = s.listenerCount('data') > 0; | ||
| // if end happened before close but the socket didn't emit an error, do it now | ||
| if (response && hasDataListener && !hadError && !(signal && signal.aborted)) { | ||
| const err = new Error('Premature close'); | ||
| err.code = 'ERR_STREAM_PREMATURE_CLOSE'; | ||
| response.body.emit('error', err); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| req.on('response', function (res) { | ||
@@ -1563,3 +1609,3 @@ clearTimeout(reqTimeout); | ||
| if (!isDomainOrSubdomain(request.url, locationURL)) { | ||
| if (!isDomainOrSubdomain(request.url, locationURL) || !isSameProtocol(request.url, locationURL)) { | ||
| for (const name of ['authorization', 'www-authenticate', 'cookie', 'cookie2']) { | ||
@@ -1657,2 +1703,9 @@ requestOpts.headers.delete(name); | ||
| }); | ||
| raw.on('end', function () { | ||
| // some old IIS servers return zero-length OK deflate responses, so 'data' is never emitted. | ||
| if (!response) { | ||
| response = new Response(body, response_options); | ||
| resolve(response); | ||
| } | ||
| }); | ||
| return; | ||
@@ -1677,2 +1730,37 @@ } | ||
| } | ||
| function fixResponseChunkedTransferBadEnding(request, errorCallback) { | ||
| let socket; | ||
| request.on('socket', function (s) { | ||
| socket = s; | ||
| }); | ||
| request.on('response', function (response) { | ||
| const headers = response.headers; | ||
| if (headers['transfer-encoding'] === 'chunked' && !headers['content-length']) { | ||
| response.once('close', function (hadError) { | ||
| // if a data listener is still present we didn't end cleanly | ||
| const hasDataListener = socket.listenerCount('data') > 0; | ||
| if (hasDataListener && !hadError) { | ||
| const err = new Error('Premature close'); | ||
| err.code = 'ERR_STREAM_PREMATURE_CLOSE'; | ||
| errorCallback(err); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| function destroyStream(stream, err) { | ||
| if (stream.destroy) { | ||
| stream.destroy(err); | ||
| } else { | ||
| // node < 8 | ||
| stream.emit('error', err); | ||
| stream.end(); | ||
| } | ||
| } | ||
| /** | ||
@@ -1679,0 +1767,0 @@ * Redirect code matching |
+16
-3
| { | ||
| "name": "node-fetch", | ||
| "version": "2.6.7", | ||
| "version": "2.6.8", | ||
| "description": "A light-weight module that brings window.fetch to node.js", | ||
@@ -42,3 +42,3 @@ "main": "lib/index.js", | ||
| }, | ||
| "peerDependencies": { | ||
| "peerDependencies": { | ||
| "encoding": "^0.1.0" | ||
@@ -57,3 +57,5 @@ }, | ||
| "babel-plugin-istanbul": "^4.1.6", | ||
| "babel-preset-env": "^1.6.1", | ||
| "babel-plugin-transform-async-generator-functions": "^6.24.1", | ||
| "babel-polyfill": "^6.26.0", | ||
| "babel-preset-env": "1.4.0", | ||
| "babel-register": "^6.16.3", | ||
@@ -77,3 +79,14 @@ "chai": "^3.5.0", | ||
| "teeny-request": "3.7.0" | ||
| }, | ||
| "release": { | ||
| "branches": [ | ||
| "+([0-9]).x", | ||
| "main", | ||
| "next", | ||
| { | ||
| "name": "beta", | ||
| "prerelease": true | ||
| } | ||
| ] | ||
| } | ||
| } |
+43
-0
@@ -191,2 +191,45 @@ node-fetch | ||
| 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 | ||
| const fetch = require('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 | ||
| const fetch = require('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); | ||
| } | ||
| ``` | ||
| #### Buffer | ||
@@ -193,0 +236,0 @@ If you prefer to cache binary data in full, use buffer(). (NOTE: `buffer()` is a `node-fetch`-only API) |
Network access
Supply chain riskThis module accesses the network.
Found 3 instances in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 3 instances in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
161490
6.08%4619
5.07%634
7.28%26
8.33%22
10%