node-wget-fetch
Advanced tools
Comparing version 1.0.5 to 1.1.0
19
cli.js
@@ -11,4 +11,5 @@ #!/usr/bin/env node | ||
var fetching = require('./'); | ||
var pack = require('./package.json'); | ||
import { wget } from './'; | ||
const require = createRequire(import.meta.url); | ||
const pack = require('../package.json'); | ||
@@ -19,3 +20,3 @@ /* | ||
var argv = process.argv.slice(2); | ||
let argv = process.argv.slice(2); | ||
@@ -26,3 +27,3 @@ /* | ||
var command = Object.keys(pack.bin)[0]; | ||
let command = Object.keys(pack.bin)[0]; | ||
@@ -69,5 +70,5 @@ /** | ||
} else if (argv.length) { | ||
var destinationIndex = argv.indexOf('--destination') + argv.indexOf('-d') + 2; | ||
let destinationIndex = argv.indexOf('--destination') + argv.indexOf('-d') + 2; | ||
var args = {}; | ||
let args = {}; | ||
if (destinationIndex) { | ||
@@ -80,7 +81,7 @@ args.dest = argv[destinationIndex]; | ||
console.log("Downloading..."); | ||
fetching(args.url, args.dest) | ||
wget(args.url, args.dest, { retry: { retries: 5 } }) | ||
.then((info) => { | ||
console.log('Done!'); | ||
console.log('The file ' + info.filepath + ' size' + | ||
(info.retrievedSizeMatch ? '' : " don't") + ' match!'); | ||
(info.fileSizeMatch ? '' : " don't") + ' match!'); | ||
}) | ||
@@ -99,3 +100,3 @@ .catch((error) => { | ||
function firstNonFlag(args) { | ||
for (var i = 0; i < args.length; i++) { | ||
for (let i = 0; i < args.length; i++) { | ||
if (args[i].charAt(0) != '-') { | ||
@@ -102,0 +103,0 @@ return args[i]; |
{ | ||
"name": "node-wget-fetch", | ||
"version": "1.0.5", | ||
"description": "Ultra simple async retrieval of resources or remote files over http or https, an cli tool, and convenience wrapper of node-fetch.", | ||
"version": "1.1.0", | ||
"description": "Ultra simple async retrieval of resources or remote files over http or https, an cli tool, and convenience wrapper of node-fetch, and a seamless retry ability", | ||
"type": "module", | ||
"main": "wget-fetch.js", | ||
"scripts": { | ||
"test": "mocha -R list test/*.js", | ||
"coverage": "npx nyc@latest --reporter json --reporter text mocha -R list test/*.js && codecov -f coverage/coverage-final.json" | ||
"coverage": "npx c8 --reporter json --reporter text npm test && npx codecov -f coverage/coverage-final.json" | ||
}, | ||
@@ -40,3 +41,3 @@ "repository": { | ||
"engines": { | ||
"node": ">=10.0.0" | ||
"node": ">=12.0.0" | ||
}, | ||
@@ -52,6 +53,5 @@ "files": [ | ||
"devDependencies": { | ||
"codecov": "^3.8.0", | ||
"mocha": "^7.2.0", | ||
"mocha": "^8.2.1", | ||
"should": "~13.2.3" | ||
} | ||
} |
@@ -7,3 +7,3 @@ # node-wget-fetch | ||
Ultra simple async retrieval of resources or remote files over http or https, an cli tool, and convenience wrapper of [node-fetch](https://www.npmjs.com/package/node-fetch). | ||
Ultra simple async retrieval of resources or remote files over http or https, an cli tool, convenience wrapper of [node-fetch](https://www.npmjs.com/package/node-fetch), and a seamless retry ability. | ||
@@ -35,4 +35,3 @@ ## Install | ||
- `options` Standard *Request/Fetch* [Options](https://www.npmjs.com/package/node-fetch#fetch-options) for the HTTP(S) request | ||
- Returns: **Promise** of `response body` of above **type**, only if **status text** is `OK` | ||
- Returns: **Promise** of `response body` of above **type**, only if **status text** is `OK`. | ||
- The **response type** will set _Fetch/Request_ **header** `'Content-Type'` as: | ||
@@ -48,10 +47,25 @@ - '`json`' = 'application/json; charset=utf-8' | ||
To customize `retry` *Fetch* operation, update in `options`. | ||
```js | ||
retry: | ||
{ | ||
retries: 1, // The maximum amount of times to retry the operation. | ||
factor: 2, //The exponential factor to use. | ||
minTimeout: 1000, // The number of milliseconds before starting the first retry. | ||
maxTimeout: 'Infinity', // The maximum number of milliseconds between two retries. | ||
randomize: false, // Randomizes the timeouts by multiplying with a factor between 1 to 2. | ||
} | ||
``` | ||
## Convenience Request Methods | ||
**fetching.get**(`url`, `response_body_type` [, `options`]); | ||
**import { get, head, options } from 'node-wget-fetch';** | ||
**fetching.head**(`url`, `response_body_type` [, `options`]); | ||
**get**(`url`, `response_body_type` [, `options`]); | ||
**fetching.options**(`url`, `response_body_type` [, `options`);] | ||
**head**(`url`, `response_body_type` [, `options`]); | ||
**options**(`url`, `response_body_type` [, `options`);] | ||
### For simply submitting `body` data | ||
@@ -61,10 +75,12 @@ | ||
**fetching.post**(`url`, `body`, `response_body_type` [, `options`]); | ||
**import { post, put, patch, delete } from 'node-wget-fetch';** | ||
**fetching.put**(`url`, `body`, `response_body_type` [, `options`]); | ||
**post**(`url`, `body`, `response_body_type` [, `options`]); | ||
**fetching.patch**(`url`, `body`, `response_body_type` [, `options`]); | ||
**put**(`url`, `body`, `response_body_type` [, `options`]); | ||
**fetching.delete**(`url`, `body`, `response_body_type` [, `options`]); | ||
**patch**(`url`, `body`, `response_body_type` [, `options`]); | ||
**delete**(`url`, `body`, `response_body_type` [, `options`]); | ||
## Bring in or access [node-fetch](https://www.npmjs.com/package/node-fetch) directly | ||
@@ -77,13 +93,18 @@ | ||
```javascript | ||
// CommonJS | ||
const fetching = require('node-wget-fetch'); | ||
const wget = fetching.wget; | ||
fetching.wget(url) // retrieve to current directory | ||
// ESM 12+ | ||
import { wget } from 'node-wget-fetch'; | ||
wget(url) // retrieve to current directory | ||
.then((info) => {}); | ||
.catch((error) => {}); | ||
fetching.wget(url, { headers: { Accept: '*/*' } }) // with optional `Fetch` options | ||
wget(url, { headers: { Accept: '*/*' } }) // with optional `Fetch` options | ||
.then((info) => {}); | ||
.catch((error) => {}); | ||
fetching.wget(url, destination_folder_or_filename, { timeout: 2000 } ) // with optional `Fetch` options | ||
wget(url, destination_folder_or_filename, { timeout: 2000 } ) // with optional `Fetch` options | ||
.then((info) => {}); | ||
@@ -115,3 +136,3 @@ .catch((error) => {}); | ||
```javascript | ||
const wget = require('node-wget-fetch'); | ||
import { wget } from 'node-wget-fetch'; | ||
@@ -118,0 +139,0 @@ wget('https://raw.github.com/techno-express/node-wget-fetch/master/angleman.png'); // angleman.png saved to current folder |
'use strict'; | ||
const fs = require('fs'), | ||
fetch = require('node-fetch'); | ||
import { | ||
createWriteStream | ||
} from 'fs'; | ||
import fetch from 'node-fetch'; | ||
@@ -18,2 +20,79 @@ let content_types = { | ||
/** | ||
* Returns a promise that conditionally tries to resolve multiple times, | ||
* as specified by the policy. | ||
* | ||
* @param {object} options - Use to specify the `retry` policy as: | ||
* ```js | ||
* { | ||
* retries: 1, // The maximum amount of times to retry the operation. | ||
* factor: 2, // The exponential factor to use. | ||
* minTimeout: 1000, // The number of milliseconds before starting the first retry. | ||
* maxTimeout: Infinity, // The maximum number of milliseconds between two retries. | ||
* randomize: false // Randomizes the timeouts by multiplying with a factor between 1 to 2. | ||
* } | ||
* ``` | ||
* @param {function} executor - A function that is called for each attempt to resolve the promise. | ||
* Executor function called as `(resolveFn, retryFn, rejectFn)`; | ||
* - `resolveFn` - To be called when the promise resolves normally. | ||
* - `retryFn` - To be called when the promise failed and a retry may be attempted. | ||
* - `rejectFn` - To be called when the promise failed and no retry should be attempted. | ||
* | ||
* @returns {Promise} | ||
*/ | ||
export const retryPromise = fetching.retryPromise = function (options, executor) { | ||
if (executor == undefined) { | ||
executor = options; | ||
options = {}; | ||
} | ||
/* | ||
* Preps the options object, initializing default values and checking constraints. | ||
*/ | ||
let opts = { | ||
retries: 1, | ||
factor: 2, | ||
minTimeout: 1000, | ||
maxTimeout: Infinity, | ||
randomize: false | ||
}; | ||
for (let key in options) { | ||
opts[key] = options[key]; | ||
} | ||
if (opts.minTimeout > opts.maxTimeout) { | ||
throw new Error('minTimeout is greater than maxTimeout'); | ||
} | ||
var attempts = 1; | ||
return new Promise((resolve, reject) => { | ||
let retrying = false; | ||
function retry(err) { | ||
if (retrying) return; | ||
retrying = true; | ||
/** | ||
* Get a timeout value in milliseconds. | ||
*/ | ||
let random = opts.randomize ? Math.random() + 1 : 1; | ||
let timeout = Math.round(random * opts.minTimeout * Math.pow(opts.factor, attempts)); | ||
timeout = Math.min(timeout, opts.maxTimeout); | ||
if (attempts < opts.retries) { | ||
setTimeout(() => { | ||
attempts++; | ||
retrying = false; | ||
executor(resolve, retry, reject, attempts); | ||
}, timeout); | ||
} else { | ||
reject(err); | ||
} | ||
} | ||
executor(resolve, retry, reject, attempts); | ||
}); | ||
} | ||
/** | ||
* Retrieval of resources or remote files over http or https by way of `node-fetch` | ||
@@ -35,3 +114,11 @@ * | ||
* **default is '`download`'** | ||
* @param {Object} options Fetch/Request options | ||
* @param {Object} options Fetch/Request options and to specify the `retry` policy as: | ||
* ```js | ||
* { retry: { retries: 1, // The maximum amount of times to retry the operation. | ||
* factor: 2, // The exponential factor to use. | ||
* minTimeout: 1000, // The number of milliseconds before starting the first retry. | ||
* maxTimeout: Infinity, // The maximum number of milliseconds between two retries. | ||
* randomize: false // Randomizes the timeouts by multiplying with a factor between 1 to 2. | ||
* }} | ||
* ``` | ||
* @return {Promise} Promise of `response body` of above **type**, only if **status text** is `OK` | ||
@@ -52,2 +139,3 @@ */ | ||
destination = './'; | ||
options.action = action; | ||
} else if (isObject(action)) { | ||
@@ -66,2 +154,8 @@ options = Object.assign(options, action); | ||
let retryPolicy = {}; | ||
if (options.retry) { | ||
retryPolicy = options.retry; | ||
delete options.retry; | ||
} | ||
if (destination.substr(destination.length - 1, 1) == '/') { | ||
@@ -76,29 +170,27 @@ destination = destination + file; | ||
} else { | ||
return fetch(src, options) | ||
.then(res => { | ||
if (res.statusText === 'OK' || res.ok) { | ||
switch (action) { | ||
case 'header': | ||
return new Promise((resolve) => resolve(res.headers.raw())); | ||
case 'object': | ||
return new Promise((resolve) => resolve(res)); | ||
case 'array': | ||
return res.arrayBuffer(); | ||
case 'buffer': | ||
return res.buffer(); | ||
case 'blob': | ||
return res.blob(); | ||
case 'json': | ||
return res.json(); | ||
case 'text': | ||
return res.text(); | ||
case 'stream': | ||
return new Promise((resolve) => resolve(res.body)); | ||
default: | ||
return new Promise((resolve, reject) => { | ||
const fileSize = Number.isInteger(res.headers.get('content-length') - 0) | ||
? parseInt(res.headers.get('content-length')) | ||
: 0; | ||
return new retryPromise(retryPolicy, (resolve, retry) => { | ||
fetch(src, options) | ||
.then(res => { | ||
if (res.statusText === 'OK' || res.ok) { | ||
switch (action) { | ||
case 'header': | ||
return resolve(res.headers.raw()); | ||
case 'object': | ||
return resolve(res); | ||
case 'array': | ||
return resolve(res.arrayBuffer()); | ||
case 'buffer': | ||
return resolve(res.buffer()); | ||
case 'blob': | ||
return resolve(res.blob()); | ||
case 'json': | ||
return resolve(res.json()); | ||
case 'text': | ||
return resolve(res.text()); | ||
case 'stream': | ||
return resolve(res.body); | ||
default: | ||
const fileSize = parseInt(res.headers.get('content-length')); | ||
let downloadedSize = 0; | ||
const writer = fs.createWriteStream(destination, { | ||
const writer = createWriteStream(destination, { | ||
flags: 'w+', | ||
@@ -109,3 +201,3 @@ encoding: 'binary' | ||
res.body.on('data', function (chunk) { | ||
res.body.on('data', (chunk) => { | ||
downloadedSize += chunk.length; | ||
@@ -121,16 +213,19 @@ }); | ||
}; | ||
info.headers = res.headers.raw(); | ||
return resolve(info); | ||
}); | ||
writer.on('error', reject); | ||
}); | ||
/* c8 ignore next 4 */ | ||
writer.on('error', (err) => { | ||
writer.end(); | ||
return retry(err); | ||
}); | ||
} | ||
} else { | ||
throw ("Fetch to " + src + " failed, with status text: " + res.statusText); | ||
} | ||
} else { | ||
throw ("Fetch to " + src + " failed, with status text: " + res.statusText); | ||
} | ||
}) | ||
.catch(err => { | ||
return new Promise((resolve, reject) => reject(err)); | ||
}); | ||
}) | ||
.catch((err) => retry(err)); | ||
}).catch((err) => { | ||
return new Promise((resolve, reject) => reject(err)); | ||
}); | ||
} | ||
@@ -147,3 +242,3 @@ } | ||
*/ | ||
function isArray(val) { | ||
export const isArray = fetching.isArray = function (val) { | ||
return toString.call(val) === '[object Array]'; | ||
@@ -158,3 +253,3 @@ } | ||
*/ | ||
function isUndefined(val) { | ||
export const isUndefined = fetching.isUndefined = function (val) { | ||
return typeof val === 'undefined'; | ||
@@ -169,3 +264,3 @@ } | ||
*/ | ||
function isBuffer(val) { | ||
export const isBuffer = fetching.isBuffer = function (val) { | ||
return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) && | ||
@@ -181,3 +276,3 @@ typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val); | ||
*/ | ||
function isArrayBuffer(val) { | ||
export const isArrayBuffer = fetching.isArrayBuffer = function (val) { | ||
return toString.call(val) === '[object ArrayBuffer]'; | ||
@@ -192,3 +287,3 @@ } | ||
*/ | ||
function isString(val) { | ||
export const isString = fetching.isString = function (val) { | ||
return typeof val === 'string'; | ||
@@ -203,3 +298,3 @@ } | ||
*/ | ||
function isNumber(val) { | ||
export const isNumber = fetching.isNumber = function (val) { | ||
return typeof val === 'number'; | ||
@@ -214,3 +309,3 @@ } | ||
*/ | ||
function isObject(val) { | ||
export const isObject = fetching.isObject = function (val) { | ||
return val !== null && typeof val === 'object'; | ||
@@ -225,3 +320,3 @@ } | ||
*/ | ||
function isPlainObject(val) { | ||
export const isPlainObject = fetching.isPlainObject = function (val) { | ||
if (toString.call(val) !== '[object Object]') { | ||
@@ -241,3 +336,3 @@ return false; | ||
*/ | ||
function isBlob(val) { | ||
export const isBlob = fetching.isBlob = function (val) { | ||
return toString.call(val) === '[object Blob]'; | ||
@@ -252,3 +347,3 @@ } | ||
*/ | ||
function isFunction(val) { | ||
export const isFunction = fetching.isFunction = function (val) { | ||
return toString.call(val) === '[object Function]'; | ||
@@ -263,3 +358,3 @@ } | ||
*/ | ||
function isDate(val) { | ||
export const isDate = fetching.isDate = function (val) { | ||
return toString.call(val) === '[object Date]'; | ||
@@ -274,3 +369,3 @@ } | ||
*/ | ||
function isStream(val) { | ||
export const isStream = fetching.isStream = function (val) { | ||
return isObject(val) && isFunction(val.pipe); | ||
@@ -349,3 +444,3 @@ } | ||
*/ | ||
function wget(url, folderFilename = './', options = {}) { | ||
function _wget(url, folderFilename = './', options = {}) { | ||
let params = options; | ||
@@ -370,3 +465,3 @@ if (isObject(folderFilename)) { | ||
*/ | ||
fetching.wget = wget; | ||
export const wget = fetching.wget = _wget; | ||
@@ -389,3 +484,3 @@ /** | ||
*/ | ||
fetching.get = verbFunc('get'); | ||
export const get = fetching.get = verbFunc('get'); | ||
@@ -408,3 +503,3 @@ /** | ||
*/ | ||
fetching.head = verbFunc('head'); | ||
export const head = fetching.head = verbFunc('head'); | ||
@@ -427,3 +522,3 @@ /** | ||
*/ | ||
fetching.options = verbFunc('options'); | ||
export const options = fetching.options = verbFunc('options'); | ||
@@ -448,3 +543,3 @@ /** | ||
*/ | ||
fetching.post = verbFuncBody('post'); | ||
export const post = fetching.post = verbFuncBody('post'); | ||
@@ -469,3 +564,3 @@ /** | ||
*/ | ||
fetching.put = verbFuncBody('put'); | ||
export const put = fetching.put = verbFuncBody('put'); | ||
@@ -490,3 +585,3 @@ /** | ||
*/ | ||
fetching.patch = verbFuncBody('patch'); | ||
export const patch = fetching.patch = verbFuncBody('patch'); | ||
@@ -511,3 +606,6 @@ /** | ||
*/ | ||
fetching.del = verbFuncBody('delete'); | ||
export const del = fetching.del = verbFuncBody('delete'); | ||
export { | ||
del as delete | ||
}; | ||
@@ -543,19 +641,2 @@ /** | ||
module.exports = exports = fetching; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = exports; | ||
exports.isArray = isArray; | ||
exports.isArrayBuffer = isArrayBuffer; | ||
exports.isBuffer = isBuffer; | ||
exports.isString = isString; | ||
exports.isNumber = isNumber; | ||
exports.isObject = isObject; | ||
exports.isPlainObject = isPlainObject; | ||
exports.isUndefined = isUndefined; | ||
exports.isBlob = isBlob; | ||
exports.isFunction = isFunction; | ||
exports.isDate = isDate; | ||
exports.isStream = isStream; | ||
export default fetching; |
33117
2
653
202
Yes