Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,34 @@
## Usage

```js
import { Recordable } from './index.js'
import { Recordable } from '@nicholaswmin/recordable'

const task = new Recordable()
task.record(1)
task.record(100)

task.record(1)
for (let i = 0; i < 600; i++)
task.record(Math.round(Math.random() * 20) + 1)
task.record(100)

console.log(task.min)
// 3.05 ms
console.log(task.mean)
// 11.42 ms
// 23.42 ms
console.log(task.max)
// 85.17 m
// 85.17 ms
console.log(task.stddev)
// 5.17 ms
// 15.17 ms
```

### Plotting

```js
const task = new Recordable()
task.record(1)
task.record(100)

task.record(1)
for (let i = 0; i < 600; i++)
task.record(Math.round(Math.random() * 20) + 1)
task.record(100)

task.plot()
```
Expand Down
5 changes: 4 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { StatsView } from './src/stats-view.js'
import { StatsList } from './src/stats-list.js'
import { StatsStore } from './src/stats-store.js'
import { Recordable } from './src/recordable.js'

export { Recordable }
export { Recordable, StatsStore, StatsList, StatsView }
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'node:test'
import { Recordable } from '../index.js'
import { Recordable } from '../../index.js'

await test('#toClampedAverages(maxLength)', async t => {
let recordable, result = null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'node:test'
import { Recordable } from '../index.js'
import { Recordable } from '../../index.js'

await test('#constructor(name)', async t => {
let recordable = null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'node:test'
import { Recordable } from '../index.js'
import { Recordable } from '../../index.js'

await test('#recordable.histogram values', async t => {
let recordable
Expand Down
2 changes: 1 addition & 1 deletion test/histogram.test.js → recordable/histogram.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'node:test'
import { Recordable } from '../index.js'
import { Recordable } from '../../index.js'

await test('#historicalMeans', async t => {
let recordable, histogram = null
Expand Down
10 changes: 10 additions & 0 deletions recordable/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import './constructor.test.js'
import './record-delta.test.js'
import './histogram.test.js'
import './histogram-values.test.js'
import './clamped-averages.test.js'
import './to-json.test.js'
import './record.test.js'
import './reset.test.js'
import './plot.test.js'
import './tick.test.js'
39 changes: 39 additions & 0 deletions recordable/patch-event.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import test from 'node:test'
import { Recordable } from '../../index.js'

await test('#patchEvent', async t => {
let recordable

t.beforeEach(() => {
recordable = new Recordable({ name: 'foo' })
})

await t.test('a value is recorded', async t => {
let message = null

t.beforeEach(async () => {
recordable.ee.on('value:recorded', msg => {
message = msg
})

recordable.record(20)

return new Promise(res => setTimeout(res, 50))
})

await t.test('event is fired', async t => {
t.assert.ok(message)
})

await t.test('message look ok', async t => {
t.assert.ok(typeof message, 'object')
t.assert.ok(Object.hasOwn(message, 'name'))
t.assert.ok(Object.hasOwn(message, 'val'))
})

await t.test('has correct values', async t => {
t.assert.strictEqual(message.name, 'foo')
t.assert.strictEqual(message.val, 20)
})
})
})
2 changes: 1 addition & 1 deletion test/plot.test.js → recordable/plot.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'node:test'
import { Recordable } from '../index.js'
import { Recordable } from '../../index.js'

await test('#plot()', async t => {
let plot = null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'node:test'
import { Recordable } from '../index.js'
import { Recordable } from '../../index.js'

const sleep = (ms = 5) => new Promise(resolve => setTimeout(resolve, ms))

Expand Down
2 changes: 1 addition & 1 deletion test/record.test.js → recordable/record.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'node:test'
import { Recordable } from '../index.js'
import { Recordable } from '../../index.js'

await test('#record(val)', async t => {
let recordable
Expand Down
63 changes: 63 additions & 0 deletions recordable/remote-patch.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import test from 'node:test'
import { Recordable } from '../../index.js'

await test('#applyRemotePatch', async t => {
let recordable

t.beforeEach(() => {
recordable = new Recordable({ name: 'foo' })
})

await t.test('name is different', async t => {
await t.test('throws Error', t => {
t.assert.throws(() => {
recordable.applyRemotePatch({ name: 'bar', val: 10 })
}, {
name: 'RangeError'
})
})
})

await t.test('value is not a positive integer', async t => {
await t.test('throws Error', t => {
t.assert.throws(() => {
recordable.applyRemotePatch({ name: 'foo', val: 0 })
}, {
name: 'RangeError'
})
})
})

await t.test('patch is valid', async t => {
t.beforeEach(() => {
recordable.applyRemotePatch({ name: 'foo', val: 10 })
recordable.applyRemotePatch({ name: 'foo', val: 20 })
})

await t.test('does not throw error', t => {
t.assert.doesNotThrow(() => {
recordable.applyRemotePatch({ name: 'foo', val: 10 })
})
})

await t.test('does not emit "value:recorded" event', async t => {
recordable.ee.on('value:recorded', e => {
})

recordable.applyRemotePatch({ name: 'foo', val: 10 })
await new Promise(resolve => setTimeout(resolve, 25))
})

await t.test('increases count', t => {
t.assert.strictEqual(recordable.count, 2)
})

await t.test('increases mean', t => {
t.assert.strictEqual(recordable.mean, 15)
})

await t.test('adds to items', t => {
t.assert.strictEqual(recordable.values.length, 2)
})
})
})
2 changes: 1 addition & 1 deletion test/reset.test.js → recordable/reset.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'node:test'
import { Recordable } from '../index.js'
import { Recordable } from '../../index.js'

await test('#reset()', async t => {
let recordable = null
Expand Down
2 changes: 1 addition & 1 deletion test/tick.test.js → recordable/tick.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'node:test'
import { Recordable } from '../index.js'
import { Recordable } from '../../index.js'

await test('#tick()', async t => {
const recordable = new Recordable()
Expand Down
2 changes: 1 addition & 1 deletion test/to-json.test.js → recordable/to-json.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'node:test'
import { Recordable } from '../index.js'
import { Recordable } from '../../index.js'

await test('#record(val)', async t => {
await t.test('reimporting its JSON revives it to same state', async t => {
Expand Down
21 changes: 21 additions & 0 deletions src/colorlist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const colorlist = {
black: '\x1B[30m',
red: '\x1B[31m',
green: '\x1B[32m',
yellow: '\x1B[33m',
blue: '\x1B[34m',
magenta: '\x1B[35m',
cyan: '\x1B[36m',
lightgray: '\x1B[37m',
default: '\x1B[39m',
darkgray: '\x1B[90m',
lightred: '\x1B[91m',
lightgreen: '\x1B[92m',
lightyellow: '\x1B[93m',
lightblue: '\x1B[94m',
lightmagenta: '\x1B[95m',
lightcyan: '\x1B[96m',
white: '\x1B[97m'
}

export default colorlist
4 changes: 2 additions & 2 deletions src/recordable.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ class Recordable {
})
})

this._recordFn = this.histogram.record.bind(this.histogram)
this.histogramRecord = this.histogram.record.bind(this.histogram)
this.histogram.record = val => {
const result = this._recordFn(val)
const result = this.histogramRecord(val)

this.values.push(val)

Expand Down
57 changes: 57 additions & 0 deletions src/stats-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
class StatsList {
constructor({
title = '', subtitle = '', maxRows = 5,
sort = function(a, b) { a - b },
fields = [
['foo.mean', 'Mean Task Duration (ms)'],
['bar.count', 'Total Count']
]
} = {}, statsStore = null) {
this.statsStore = statsStore

this.title = title
this.subtitle = subtitle
this.maxRows = maxRows
this.fields = fields

this.rows = []
this.sort = sort
}

render() {
const rows = this.statsStore
? [this.statsStore.getRow()]
: this.rows.slice(this.rows.length - this.maxRows, this.rows.length)

const values = this.rows
.sort(this.sort)
.slice(this.rows.length - this.maxRows, this.rows.length)
.map(row => {
return this.fields.reduce((acc, field) => {
const split = field[0].split('.')
return {
...acc,
[field[1]] : row[2]
? row[2](row[split[0]][split[1]])
: row[split[0]][split[1]]
}
}, {})
})

this.title
? console.log('Title:', this.title)
: null

console.table(values)

this.subtitle
? console.log(this.subtitle)
: null
}

append(row) {
this.rows.push(row)
}
}

export { StatsList }
23 changes: 23 additions & 0 deletions src/stats-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Recordable } from './recordable.js'

class StatsStore {
constructor(...args) {
Object.assign(this, args.reduce((acc, name) => {
return { ...acc, [name]: new Recordable({ name }) }
}, {}))
}

getMembers() {
return Object.values(this).filter(value => value instanceof Recordable)
}

getRow() {
return this.getMembers().reduce((acc, member) => {
return {
...acc, [member.name]: member.histogram.toJSON()
}
}, { id: process.pid })
}
}

export { StatsStore }
36 changes: 36 additions & 0 deletions src/stats-view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class StatsView {
constructor(lists, {
title = '',
subtitle = '',
updateInterval = 500
} = {}) {
this.title = title
this.subtitle = subtitle
this.lists = lists

this.timer = setInterval(this.render.bind(this), updateInterval)
}

render() {
console.clear()

console.log('\n')

this.title
? console.log('Title:', this.title)
: null

this.lists.forEach(list => {
list.render()
console.log('\n')
})

console.log('\n')
}

stop() {
clearInterval(this.timer)
}
}

export { StatsView }
Loading