├── .gitignore ├── .travis.yml ├── README.md ├── example.js ├── index.js ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - 4 5 | - 5 6 | - 6 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # hyperdrive-stats 3 | 4 | Live & persistent stats module for hyperdrive. 5 | 6 | [![build status](https://travis-ci.org/juliangruber/hyperdrive-stats.svg?branch=master)](http://travis-ci.org/juliangruber/hyperdrive-stats) 7 | 8 | ## Usage 9 | 10 | ```js 11 | var Stats = require('hyperdrive-stats') 12 | var stats = Stats({ 13 | archive: archive, 14 | db: db 15 | }) 16 | 17 | stats.on('update', function () { 18 | console.log(stats.get()) 19 | }) 20 | ``` 21 | 22 | ## Stats 23 | 24 | - `"bytesTotal"` 25 | - `"bytesProgress"` 26 | - `"blocksTotal"` 27 | - `"filesTotal"` 28 | 29 | 30 | ## License 31 | 32 | MIT 33 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var Stats = require('./') 4 | var hyperdrive = require('hyperdrive') 5 | var memdb = require('memdb') 6 | 7 | var db = memdb() 8 | var drive = hyperdrive(db) 9 | 10 | var archive = drive.createArchive() 11 | 12 | var stats = Stats({ 13 | archive: archive, 14 | db: db 15 | }) 16 | stats.on('update', function () { 17 | console.log(stats.get()) 18 | }) 19 | 20 | var ws = archive.createFileWriteStream('file') 21 | ws.on('finish', function () { 22 | ws = archive.createFileWriteStream('file') 23 | ws.on('finish', function () { 24 | archive.createFileWriteStream('file').end('beepboop') 25 | }) 26 | ws.end('bar') 27 | }) 28 | ws.end('foo') 29 | archive 30 | .createFileWriteStream({ 31 | name: 'directory', 32 | type: 'directory' 33 | }) 34 | .end() 35 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var EventEmitter = require('events').EventEmitter 4 | var index = require('hypercore-index') 5 | var encoding = require('hyperdrive-encoding') 6 | var inherits = require('util').inherits 7 | var objectAssign = require('object-assign') 8 | var assert = require('assert') 9 | 10 | module.exports = Stats 11 | inherits(Stats, EventEmitter) 12 | 13 | function Stats (opts) { 14 | if (!(this instanceof Stats)) return new Stats(opts) 15 | EventEmitter.call(this) 16 | 17 | assert(opts.archive, '.archive required') 18 | assert(opts.db, '.db required') 19 | 20 | var archive = opts.archive 21 | var db = opts.db 22 | var self = this 23 | this._stats = { 24 | bytesTotal: 0, 25 | blocksProgress: 0, 26 | blocksTotal: 0, 27 | filesTotal: 0 28 | } 29 | 30 | db.get('stats', function (err, stats) { 31 | if (err && !err.notFound) return self.emit('error', err) 32 | if (stats) { 33 | objectAssign(self._stats, JSON.parse(stats)) 34 | self.update() 35 | } 36 | 37 | archive.open(function (err) { 38 | if (err && /is closed/.test(err.message)) return 39 | if (err) return self.emit('error', err) 40 | 41 | if (!archive.owner) { 42 | var blocksProgress = 0 43 | for (var i = 0; i < archive.content.blocks; i++) { 44 | if (archive.content.has(i)) blocksProgress++ 45 | } 46 | self.update({ blocksProgress: blocksProgress }) 47 | archive.content.on('download', function () { 48 | self.update({ blocksProgress: self._stats.blocksProgress + 1 }) 49 | }) 50 | } 51 | 52 | index( 53 | { 54 | feed: opts.archive.metadata, 55 | db: db 56 | }, 57 | function (buf, cb) { 58 | var entry = encoding.decode(buf) 59 | if (entry.type === 'file') { 60 | db.get('!entry!' + entry.name, function (err, last) { 61 | if (err && !err.notFound) return cb(err) 62 | var lastFound = !!last 63 | last = JSON.parse(last || '{}') 64 | 65 | var update = { 66 | bytesTotal: self._stats.bytesTotal + 67 | entry.length - 68 | (last.length || 0), 69 | blocksTotal: self._stats.blocksTotal + 70 | entry.blocks - 71 | (last.blocks || 0) 72 | } 73 | if (archive.owner) update.blocksProgress = update.blocksTotal 74 | if (!lastFound) update.filesTotal = self._stats.filesTotal + 1 75 | 76 | self.update(update) 77 | db 78 | .batch() 79 | .put('!entry!' + entry.name, JSON.stringify(entry)) 80 | .put('stats', JSON.stringify(self._stats)) 81 | .write(cb) 82 | }) 83 | } else { 84 | cb() 85 | } 86 | }, 87 | function (err) { 88 | if (err) self.emit('error', err) 89 | } 90 | ) 91 | }) 92 | }) 93 | } 94 | 95 | Stats.prototype.update = function (data) { 96 | var keys = Object.keys(data || {}) 97 | for (var i = 0; i < keys.length; i++) { 98 | var key = keys[i] 99 | this._stats[key] = data[key] 100 | this.emit('update:' + key) 101 | } 102 | this.emit('update') 103 | } 104 | 105 | Stats.prototype.get = function () { 106 | return objectAssign({}, this._stats) 107 | } 108 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyperdrive-stats", 3 | "version": "3.0.0", 4 | "repository": "juliangruber/hyperdrive-stats", 5 | "license": "MIT", 6 | "devDependencies": { 7 | "hyperdrive": "^7.6.0", 8 | "memdb": "^1.3.1", 9 | "prettier-standard-formatter": "^0.222222222222222.333333333333333", 10 | "random-access-file": "^1.3.1", 11 | "tap": "^7.1.2" 12 | }, 13 | "scripts": { 14 | "test": "prettier-standard-formatter *.js && node example.js" 15 | }, 16 | "dependencies": { 17 | "hypercore-index": "^5.0.0", 18 | "hyperdrive-encoding": "^2.0.0", 19 | "object-assign": "^4.1.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Stats = require('.') 4 | const test = require('tap').test 5 | const hyperdrive = require('hyperdrive') 6 | const memdb = require('memdb') 7 | 8 | const db = memdb() 9 | const drive = hyperdrive(db) 10 | 11 | test(t => { 12 | t.plan(6) 13 | const archive = drive.createArchive({ live: false }) 14 | archive.createFileWriteStream('file').end('content') 15 | archive.finalize(() => { 16 | const stats = new Stats({ archive, db }) 17 | stats.on('update:filesProgress', v => t.equal(v, 1, 'filesProgress')) 18 | stats.on('update:filesTotal', v => t.equal(v, 1, 'filesTotal')) 19 | stats.on('update:bytesProgress', v => t.equal(v, 7, 'bytesProgress')) 20 | stats.on('update:bytesTotal', v => t.equal(v, 7, 'bytesTotal')) 21 | stats.on('update', s => t.ok(s, 'update')) 22 | }) 23 | }) 24 | --------------------------------------------------------------------------------