status-bar
Advanced tools
Comparing version 2.0.2 to 2.0.3
429
lib/index.js
@@ -1,245 +0,246 @@ | ||
"use strict"; | ||
'use strict'; | ||
var util = require ("util"); | ||
var stream = require ("stream"); | ||
var pbf = require ("progress-bar-formatter"); | ||
var util = require('util'); | ||
var stream = require('stream'); | ||
var PBF = require('progress-bar-formatter'); | ||
module.exports.create = function (options){ | ||
return new StatusBar (options); | ||
module.exports.create = function (options) { | ||
return new StatusBar(options); | ||
}; | ||
var storage = [" B ", " KiB", " MiB", " GiB", " TiB", " PiB", " EiB", " ZiB", | ||
" YiB"]; | ||
var speeds = ["B/s", "K/s", "M/s", "G/s", "T/s", "P/s", "E/s", "Z/s", "Y/s"]; | ||
var storage = [' B ', ' KiB', ' MiB', ' GiB', ' TiB', ' PiB', ' EiB', ' ZiB', | ||
' YiB']; | ||
var speeds = ['B/s', 'K/s', 'M/s', 'G/s', 'T/s', 'P/s', 'E/s', 'Z/s', 'Y/s']; | ||
var space = function (n, max){ | ||
n += ""; | ||
var spaces = max - n.length; | ||
for (var i=0; i<spaces; i++){ | ||
n = " " + n; | ||
} | ||
return n; | ||
var space = function (n, max) { | ||
n += ''; | ||
var spaces = max - n.length; | ||
for (var i = 0; i < spaces; i++) { | ||
n = ' ' + n; | ||
} | ||
return n; | ||
}; | ||
var unit = function (n, arr, pow, decimals){ | ||
var chars = decimals ? 5 + decimals : 4; | ||
if (n < pow) return space (n, chars) + arr[0]; | ||
var i = 1; | ||
while (i < 9){ | ||
n /= pow; | ||
if (n < pow) return space (n.toFixed (decimals), chars) + arr[i]; | ||
i++; | ||
} | ||
return ">=" + pow + arr[7]; | ||
var unit = function (n, arr, pow, decimals) { | ||
var chars = decimals ? 5 + decimals : 4; | ||
if (n < pow) return space(n, chars) + arr[0]; | ||
var i = 1; | ||
while (i < 9) { | ||
n /= pow; | ||
if (n < pow) return space(n.toFixed(decimals), chars) + arr[i]; | ||
i++; | ||
} | ||
return '>=' + pow + arr[7]; | ||
}; | ||
var zero = function (n){ | ||
return n < 10 ? "0" + n : n; | ||
var zero = function (n) { | ||
return n < 10 ? '0' + n : n; | ||
}; | ||
var Formatter = function (statusBar){ | ||
this._statusBar = statusBar; | ||
var Formatter = function (statusBar) { | ||
this._statusBar = statusBar; | ||
}; | ||
Formatter.prototype.storage = function (b, decimals){ | ||
return unit (Math.floor (b), storage, 1024, | ||
decimals === undefined ? 1 : decimals); | ||
Formatter.prototype.storage = function (b, decimals) { | ||
return unit(Math.floor(b), storage, 1024, | ||
decimals === undefined ? 1 : decimals); | ||
}; | ||
Formatter.prototype.speed = function (bps, decimals){ | ||
return unit (Math.floor (bps), speeds, 1000, | ||
decimals === undefined ? 1 : decimals); | ||
Formatter.prototype.speed = function (bps, decimals) { | ||
return unit(Math.floor(bps), speeds, 1000, | ||
decimals === undefined ? 1 : decimals); | ||
}; | ||
Formatter.prototype.time = function (s){ | ||
if (s === undefined) return "--:--"; | ||
if (s >= 86400) return " > 1d"; | ||
if (s >= 3600) return " > 1h"; | ||
var min = Math.floor (s/60); | ||
var sec = Math.floor (s%60); | ||
return zero (min) + ":" + zero (sec); | ||
Formatter.prototype.time = function (s) { | ||
if (s === undefined) return '--:--'; | ||
if (s >= 86400) return ' > 1d'; | ||
if (s >= 3600) return ' > 1h'; | ||
var min = Math.floor(s / 60); | ||
var sec = Math.floor(s % 60); | ||
return zero(min) + ':' + zero(sec); | ||
}; | ||
Formatter.prototype.percentage = function (n){ | ||
return space (Math.round (n*100) + "%", 4); | ||
Formatter.prototype.percentage = function (n) { | ||
return space(Math.round(n * 100) + '%', 4); | ||
}; | ||
Formatter.prototype.progressBar = function (n){ | ||
return this._statusBar._progress.format (n); | ||
Formatter.prototype.progressBar = function (n) { | ||
return this._statusBar._pbf.format(n); | ||
}; | ||
var StatusBar = function (options){ | ||
if (options.total === undefined || options.total === null){ | ||
throw new Error ("Missing file size"); | ||
} | ||
stream.Writable.call (this); | ||
this.format = new Formatter (this); | ||
var me = this; | ||
this.on ("unpipe", function (){ | ||
me.cancel (); | ||
}); | ||
this._frequency = options.frequency || 200; | ||
this._progress = pbf.create ({ | ||
complete: options.progressBarComplete, | ||
incomplete: options.progressBarIncomplete, | ||
length: options.progressBarLength | ||
}); | ||
this._hungUp = false; | ||
this._finished = false; | ||
this._current = 0; | ||
this._total = Math.floor (options.total); | ||
this._onRenderTimer = null; | ||
this._elapsedTimer = null; | ||
this._start = 0; | ||
this._chunkTimestamp = 0; | ||
this._secondsWithoutUpdate = 0; | ||
this._avgBytes = 0; | ||
this._lastElapsed = 0; | ||
//Smooth factors | ||
this._speedSmooth = 0.2; | ||
this._avgSmooth = 0.2; | ||
this._elapsedSmooth = 0.7; | ||
this._stats = { | ||
currentSize: 0, | ||
totalSize: this._total, | ||
remainingSize: this._total, | ||
speed: 0, | ||
elapsedTime: 0 | ||
}; | ||
var percentage; | ||
if (this._total === 0){ | ||
percentage = 1; | ||
this._stats.remainingTime = 0; | ||
}else{ | ||
percentage = 0; | ||
//Undefined remainingTime | ||
} | ||
this._stats.percentage = percentage; | ||
if (this._total === 0){ | ||
this._finished = true; | ||
process.nextTick (function (){ | ||
me.emit ("render", me._stats); | ||
me.emit ("finish"); | ||
}); | ||
}else{ | ||
process.nextTick (function (){ | ||
me.emit ("render", me._stats); | ||
me._onRenderTimer = setInterval (function (){ | ||
me.emit ("render", me._stats); | ||
}, me._frequency); | ||
}); | ||
} | ||
var StatusBar = function (options) { | ||
if (options.total === undefined || options.total === null) { | ||
throw new Error('Missing file size'); | ||
} | ||
stream.Writable.call(this); | ||
this.format = new Formatter(this); | ||
var me = this; | ||
this.on('unpipe', function () { | ||
me.cancel(); | ||
}); | ||
this._frequency = options.frequency || 200; | ||
this._pbf = new PBF({ | ||
complete: options.progressBarComplete, | ||
incomplete: options.progressBarIncomplete, | ||
length: options.progressBarLength | ||
}); | ||
this._hungUp = false; | ||
this._finished = false; | ||
this._current = 0; | ||
this._total = Math.floor(options.total); | ||
this._onRenderTimer = null; | ||
this._elapsedTimer = null; | ||
this._start = 0; | ||
this._chunkTimestamp = 0; | ||
this._secondsWithoutUpdate = 0; | ||
this._avgBytes = 0; | ||
this._lastElapsed = 0; | ||
// Smooth factors | ||
this._speedSmooth = 0.2; | ||
this._avgSmooth = 0.2; | ||
this._elapsedSmooth = 0.7; | ||
this._stats = { | ||
currentSize: 0, | ||
totalSize: this._total, | ||
remainingSize: this._total, | ||
speed: 0, | ||
elapsedTime: 0 | ||
}; | ||
var percentage; | ||
if (this._total === 0) { | ||
percentage = 1; | ||
this._stats.remainingTime = 0; | ||
} else { | ||
percentage = 0; | ||
// Undefined remainingTime | ||
} | ||
this._stats.percentage = percentage; | ||
if (this._total === 0) { | ||
this._finished = true; | ||
process.nextTick(function () { | ||
me.emit('render', me._stats); | ||
me.emit('finish'); | ||
}); | ||
} else { | ||
process.nextTick(function () { | ||
me.emit('render', me._stats); | ||
me._onRenderTimer = setInterval(function () { | ||
me.emit('render', me._stats); | ||
}, me._frequency); | ||
}); | ||
} | ||
}; | ||
util.inherits (StatusBar, stream.Writable); | ||
util.inherits(StatusBar, stream.Writable); | ||
StatusBar.prototype._write = function (chunk, encoding, cb){ | ||
if (this._finished) throw new Error ("Total length exceeded"); | ||
if (!this._start) this._start = Date.now (); | ||
this._secondsWithoutUpdate = 0; | ||
this._hungUp = false; | ||
//Allow any object with a length property | ||
var length = chunk.length; | ||
this._current += length; | ||
this._avgBytes += length; | ||
this._updateStats (length); | ||
//High resolution time calculation between packets | ||
this._chunkTimestamp = process.hrtime (); | ||
//Force a render if the transfer has finished | ||
if (this._current === this._total){ | ||
this.cancel (); | ||
this.emit ("render", this._stats); | ||
this.emit ("finish"); | ||
} | ||
cb (); | ||
StatusBar.prototype._write = function (chunk, encoding, cb) { | ||
if (this._finished) throw new Error('Total length exceeded'); | ||
if (!this._start) this._start = Date.now(); | ||
this._secondsWithoutUpdate = 0; | ||
this._hungUp = false; | ||
// Allow any object with a length property | ||
var length = chunk.length; | ||
this._current += length; | ||
this._avgBytes += length; | ||
this._updateStats(length); | ||
// High resolution time calculation between packets | ||
this._chunkTimestamp = process.hrtime(); | ||
// Force a render if the transfer has finished | ||
if (this._current === this._total) { | ||
this.cancel(); | ||
this.emit('render', this._stats); | ||
this.emit('finish'); | ||
} | ||
cb(); | ||
}; | ||
StatusBar.prototype._updateStats = function (length){ | ||
var end = this._current === this._total; | ||
var elapsed; | ||
//The elapsed time needs to be calculated with a timer because if it's | ||
//calculated using the elapsed time from the start of the transfer, if the | ||
//transfer is hung up, the "elapsedTime" property must still be updated each | ||
//second | ||
if (!this._elapsedTimer){ | ||
var me = this; | ||
this._elapsedTimer = setInterval (function (){ | ||
me._stats.elapsedTime++; | ||
//Wait 3 seconds before considering a transfer hang up | ||
if (me._secondsWithoutUpdate === 3){ | ||
if (!me._hungUp){ | ||
me._hungUp = true; | ||
me._stats.speed = 0; | ||
me._stats.remainingTime = undefined; | ||
me._start = null; | ||
me._avgBytes = 0; | ||
me.emit ("hangup"); | ||
} | ||
}else{ | ||
me._secondsWithoutUpdate++; | ||
} | ||
}, 1000); | ||
} | ||
this._stats.currentSize = this._current; | ||
this._stats.remainingSize = this._total - this._current; | ||
this._stats.percentage = this._current/this._total; | ||
//The speed and remaining time cannot be calculated only with the first packet | ||
if (this._chunkTimestamp){ | ||
//The transfer speed is extrapolated from the time between chunks | ||
elapsed = process.hrtime (this._chunkTimestamp); | ||
//Elapsed in nanoseconds | ||
elapsed = elapsed[0]*1e9 + elapsed[1]; | ||
//Smooth the elapsed time | ||
this._lastElapsed = elapsed = (1 - this._elapsedSmooth)*elapsed + | ||
this._elapsedSmooth*this._lastElapsed; | ||
if (end){ | ||
this._stats.speed = 0; | ||
this._stats.remainingTime = 0; | ||
}else{ | ||
//Bytes per second | ||
var speed = (length*1e9)/elapsed; | ||
var lastSpeed = this._stats.speed || speed; | ||
//Smooth the speed | ||
speed = (1 - this._speedSmooth)*speed + this._speedSmooth*lastSpeed; | ||
//Elapsed in milliseconds | ||
elapsed = (Date.now () - this._start)*0.001; | ||
//Use the average speed as a reference speed to smooth peaks even more | ||
var avgSpeed = this._avgBytes/elapsed; | ||
this._stats.speed = | ||
Math.floor ((1 - this._avgSmooth)*speed + this._avgSmooth*avgSpeed); | ||
//Remaining time | ||
this._stats.remainingTime = | ||
Math.floor (this._stats.remainingSize/avgSpeed) + 1; | ||
} | ||
} | ||
StatusBar.prototype._updateStats = function (length) { | ||
var end = this._current === this._total; | ||
var elapsed; | ||
// The elapsed time needs to be calculated with a timer because if it's | ||
// calculated using the elapsed time from the start of the transfer, if the | ||
// transfer is hung up, the 'elapsedTime' property must still be updated each | ||
// second | ||
if (!this._elapsedTimer) { | ||
var me = this; | ||
this._elapsedTimer = setInterval(function () { | ||
me._stats.elapsedTime++; | ||
// Wait 3 seconds before considering a transfer hang up | ||
if (me._secondsWithoutUpdate === 3) { | ||
if (!me._hungUp) { | ||
me._hungUp = true; | ||
me._stats.speed = 0; | ||
me._stats.remainingTime = undefined; | ||
me._start = null; | ||
me._avgBytes = 0; | ||
me.emit('hangup'); | ||
} | ||
} else { | ||
me._secondsWithoutUpdate++; | ||
} | ||
}, 1000); | ||
} | ||
this._stats.currentSize = this._current; | ||
this._stats.remainingSize = this._total - this._current; | ||
this._stats.percentage = this._current / this._total; | ||
// The speed and remaining time cannot be calculated only with the first | ||
// packet | ||
if (this._chunkTimestamp) { | ||
// The transfer speed is extrapolated from the time between chunks | ||
elapsed = process.hrtime(this._chunkTimestamp); | ||
// Elapsed in nanoseconds | ||
elapsed = elapsed[0] * 1e9 + elapsed[1]; | ||
// Smooth the elapsed time | ||
this._lastElapsed = elapsed = (1 - this._elapsedSmooth) * elapsed + | ||
this._elapsedSmooth * this._lastElapsed; | ||
if (end) { | ||
this._stats.speed = 0; | ||
this._stats.remainingTime = 0; | ||
} else { | ||
// Bytes per second | ||
var speed = (length * 1e9) / elapsed; | ||
var lastSpeed = this._stats.speed || speed; | ||
// Smooth the speed | ||
speed = (1 - this._speedSmooth) * speed + this._speedSmooth * lastSpeed; | ||
// Elapsed in milliseconds | ||
elapsed = (Date.now() - this._start) * 0.001; | ||
// Use the average speed as a reference speed to smooth peaks even more | ||
var avgSpeed = this._avgBytes / elapsed; | ||
this._stats.speed = Math.floor((1 - this._avgSmooth) * speed + | ||
this._avgSmooth * avgSpeed); | ||
// Remaining time | ||
this._stats.remainingTime = | ||
Math.floor(this._stats.remainingSize / avgSpeed) + 1; | ||
} | ||
} | ||
}; | ||
StatusBar.prototype.cancel = function (){ | ||
clearInterval (this._onRenderTimer); | ||
clearInterval (this._elapsedTimer); | ||
StatusBar.prototype.cancel = function () { | ||
clearInterval(this._onRenderTimer); | ||
clearInterval(this._elapsedTimer); | ||
}; |
{ | ||
"name": "status-bar", | ||
"version": "2.0.2", | ||
"description": "A status bar for file transfers", | ||
"keywords": ["status", "bar", "file", "transfer", "speed", "progress", | ||
"stream"], | ||
"author": "Gabriel Llamas <gagle@outlook.com>", | ||
"repository": "git://github.com/gagle/node-status-bar.git", | ||
"engines": { | ||
"node": ">=0.10" | ||
}, | ||
"dependencies": { | ||
"progress-bar-formatter": "1.0.x" | ||
}, | ||
"license": "MIT", | ||
"main": "lib" | ||
"name": "status-bar", | ||
"version": "2.0.3", | ||
"description": "A status bar for file transfers", | ||
"keywords": [ | ||
"status", | ||
"bar", | ||
"file", | ||
"transfer", | ||
"speed", | ||
"progress", | ||
"stream" | ||
], | ||
"author": "Gabriel Llamas <gagle@outlook.com>", | ||
"repository": "git://github.com/gagle/node-status-bar.git", | ||
"engines": { | ||
"node": ">=0.10" | ||
}, | ||
"dependencies": { | ||
"progress-bar-formatter": "^2.0.1" | ||
}, | ||
"license": "MIT", | ||
"main": "lib" | ||
} |
142
README.md
@@ -6,35 +6,33 @@ status-bar | ||
[![NPM version](https://badge.fury.io/js/status-bar.png)](http://badge.fury.io/js/status-bar "Fury Version Badge") | ||
[![Dependency Status](https://david-dm.org/gagle/node-status-bar.png)](https://david-dm.org/gagle/node-status-bar "David Dependency Manager Badge") | ||
[![npm][npm-image]][npm-url] | ||
[![david][david-image]][david-url] | ||
[![NPM installation](https://nodei.co/npm/status-bar.png?mini=true)](https://nodei.co/npm/status-bar "NodeICO Badge") | ||
#### Example #### | ||
```javascript | ||
var path = require ("path"); | ||
var http = require ("http"); | ||
var statusBar = require ("status-bar"); | ||
var path = require('path'); | ||
var http = require('http'); | ||
var statusBar = require('status-bar'); | ||
var url = "http://nodejs.org/dist/latest/node.exe"; | ||
var url = 'http://nodejs.org/dist/latest/node.exe'; | ||
var bar; | ||
http.get (url, function (res){ | ||
bar = statusBar.create ({ total: res.headers["content-length"] }) | ||
.on ("render", function (stats){ | ||
process.stdout.write ( | ||
path.basename (url) + " " + | ||
this.format.storage (stats.currentSize) + " " + | ||
this.format.speed (stats.speed) + " " + | ||
this.format.time (stats.elapsedTime) + " " + | ||
this.format.time (stats.remainingTime) + " [" + | ||
this.format.progressBar (stats.percentage) + "] " + | ||
this.format.percentage (stats.percentage)); | ||
process.stdout.cursorTo (0); | ||
http.get(url, function (res) { | ||
bar = statusBar.create({ total: res.headers['content-length'] }) | ||
.on('render', function (stats) { | ||
process.stdout.write( | ||
path.basename(url) + ' ' + | ||
this.format.storage(stats.currentSize) + ' ' + | ||
this.format.speed(stats.speed) + ' ' + | ||
this.format.time(stats.elapsedTime) + ' ' + | ||
this.format.time(stats.remainingTime) + ' [' + | ||
this.format.progressBar(stats.percentage) + '] ' + | ||
this.format.percentage(stats.percentage)); | ||
process.stdout.cursorTo(0); | ||
}); | ||
res.pipe (bar); | ||
}).on ("error", function (error){ | ||
if (bar) bar.cancel (); | ||
console.error (error); | ||
res.pipe(bar); | ||
}).on('error', function (err) { | ||
if (bar) bar.cancel(); | ||
console.error(err); | ||
}); | ||
@@ -52,3 +50,3 @@ | ||
- It doesn't print anything, it just calculates and returns raw data and provides default formatting functions. | ||
- The status bar can be displayed wherever you want, it is simply a string, so you can render it in the console, in HTML (probably with your own progress bar) via websockets or [node-webkit](https://github.com/rogerwang/node-webkit), etc. | ||
- The status bar can be displayed wherever you want, it is simply a string, so you can render it in the console, in HTML (probably with your own progress bar) via websockets or [NW.js][nwjs], etc. | ||
- You decide how to format and arrange the elements. The default formatting functions have a fixed length, so you can format the status bar very easily. | ||
@@ -66,13 +64,13 @@ - It is very easy to use. Just `pipe()` things to it! | ||
```javascript | ||
var statusBar = require ("status-bar"); | ||
var statusBar = require('status-bar'); | ||
var formatFilename = function (filename){ | ||
var formatFilename = function (filename) { | ||
//80 - 59 | ||
var filenameMaxLength = 21; | ||
if (filename.length > filenameMaxLength){ | ||
filename = filename.slice (0, filenameMaxLength - 3) + "..."; | ||
}else{ | ||
if (filename.length > filenameMaxLength) { | ||
filename = filename.slice(0, filenameMaxLength - 3) + '...'; | ||
} else { | ||
var remaining = filenameMaxLength - filename.length; | ||
while (remaining--){ | ||
filename += " "; | ||
while (remaining--) { | ||
filename += ' '; | ||
} | ||
@@ -83,16 +81,16 @@ } | ||
filename = formatFilename (filename); | ||
filename = formatFilename(filename); | ||
var bar = statusBar.create ({ total: ... }) | ||
.on ("render", function (stats){ | ||
process.stdout.write (filename + " " + | ||
this.format.storage (stats.currentSize) + " " + | ||
this.format.speed (stats.speed) + " " + | ||
this.format.time (stats.remainingTime) + " [" + | ||
this.format.progressBar (stats.percentage) + "] " + | ||
this.format.percentage (stats.percentage)); | ||
process.stdout.cursorTo (0); | ||
var bar = statusBar.create({ total: ... }) | ||
.on('render', function (stats) { | ||
process.stdout.write(filename + ' ' + | ||
this.format.storage(stats.currentSize) + ' ' + | ||
this.format.speed(stats.speed) + ' ' + | ||
this.format.time(stats.remainingTime) + ' [' + | ||
this.format.progressBar(stats.percentage) + '] ' + | ||
this.format.percentage(stats.percentage)); | ||
process.stdout.cursorTo(0); | ||
}); | ||
readableStream.pipe (bar); | ||
readableStream.pipe(bar); | ||
``` | ||
@@ -107,15 +105,15 @@ | ||
```javascript | ||
var statusBar = require ("status-bar"); | ||
var statusBar = require('status-bar'); | ||
var bar = statusBar.create ({ total: ...}) | ||
.on ("render", function (stats){ | ||
process.stdout.write ("Receiving objects: " + | ||
this.format.percentage (stats.percentage).trim () + | ||
" (" + stats.currentSize + "/" + stats.totalSize + "), " + | ||
this.format.storage (stats.currentSize).trim () + " | " + | ||
this.format.speed (stats.speed).trim ()); | ||
process.stdout.cursorTo (0); | ||
var bar = statusBar.create({ total: ...}) | ||
.on('render', function (stats) { | ||
process.stdout.write('Receiving objects: ' + | ||
this.format.percentage(stats.percentage).trim() + | ||
' (' + stats.currentSize + '/' + stats.totalSize + '), ' + | ||
this.format.storage(stats.currentSize).trim() + ' | ' + | ||
this.format.speed(stats.speed).trim()); | ||
process.stdout.cursorTo(0); | ||
}); | ||
readableStream.pipe (bar); | ||
readableStream.pipe(bar); | ||
``` | ||
@@ -247,4 +245,6 @@ | ||
```javascript | ||
console.log (this.format.percentage (0.5)); | ||
// 50% | ||
console.log(this.format.percentage(0.5)); | ||
/* | ||
50% | ||
*/ | ||
``` | ||
@@ -260,4 +260,6 @@ | ||
```javascript | ||
console.log (this.format.progressBar (0.06)); | ||
//#······················· | ||
console.log(this.format.progressBar(0.06)); | ||
/* | ||
#······················· | ||
*/ | ||
``` | ||
@@ -273,4 +275,6 @@ | ||
```javascript | ||
console.log (this.format.speed (30098226)); | ||
// 30.1M/s | ||
console.log(this.format.speed(30098226)); | ||
/* | ||
30.1M/s | ||
*/ | ||
``` | ||
@@ -286,4 +290,6 @@ | ||
```javascript | ||
console.log (this.format.storage (38546744)); | ||
// 36.8 MiB | ||
console.log(this.format.storage(38546744)); | ||
/* | ||
36.8 MiB | ||
*/ | ||
``` | ||
@@ -299,4 +305,12 @@ | ||
```javascript | ||
console.log (this.format.time (63)); | ||
//01:03 | ||
``` | ||
console.log(this.format.time(63)); | ||
/* | ||
01:03 | ||
*/ | ||
``` | ||
[npm-image]: https://img.shields.io/npm/v/status-bar.svg?style=flat | ||
[npm-url]: https://npmjs.org/package/status-bar | ||
[david-image]: https://img.shields.io/david/gagle/node-status-bar.svg?style=flat | ||
[david-url]: https://david-dm.org/gagle/node-status-bar | ||
[nwjs]: https://github.com/nwjs/nw.js |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
18336
5
203
306
1
+ Addedprogress-bar-formatter@2.0.1(transitive)
- Removedprogress-bar-formatter@1.0.0(transitive)