fix: multi-repo local dev (file:../ deps, package exports, missing deps)
This commit is contained in:
+6
@@ -0,0 +1,6 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- 6
|
||||
- 8
|
||||
- 9
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Mathias Buus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
# record-cache
|
||||
|
||||
Cache optimised for record like things like `host:port` or `domain.names`.
|
||||
|
||||
```
|
||||
npm install record-cache
|
||||
```
|
||||
|
||||
[](https://travis-ci.org/mafintosh/record-cache)
|
||||
|
||||
## Usage
|
||||
|
||||
``` js
|
||||
var recordCache = require('record-cache')
|
||||
|
||||
var cache = recordCache({
|
||||
maxSize: 1000 // store ~1000 values at max
|
||||
maxAge: 1000 // gc values older than ~1000ms
|
||||
})
|
||||
|
||||
cache.add('hello', '127.0.0.1')
|
||||
cache.add('hello', '127.0.1.1')
|
||||
cache.add('hello', '127.0.0.2')
|
||||
|
||||
console.log(cache.get('hello', 2)) // prints two of the above
|
||||
|
||||
// wait 2s
|
||||
setTimeout(function () {
|
||||
console.log(cache.get('hello', 2)) // prints []
|
||||
}, 2000)
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
#### `var cache = recordCache([options])`
|
||||
|
||||
Create a new record cache.
|
||||
|
||||
Options include:
|
||||
|
||||
``` js
|
||||
{
|
||||
maxSize: 1000, // approximate max size
|
||||
maxAge: 1000, // approximate max age in ms
|
||||
onStale: false // function called when evicting stale records
|
||||
}
|
||||
```
|
||||
|
||||
In the worst case the cache will be `2 * maxSize` large, and
|
||||
if `maxAge` is used old values are gc'ed every `0.66 * maxAge - 1.33 * maxAge` with an optional callback to the `onStale` function upon record eviction.
|
||||
|
||||
This is to greatly simplify the data structures and also gives us a pretty decent
|
||||
perf boost compared to other cache modules out there.
|
||||
|
||||
#### `cache.add(recordName, value)`
|
||||
|
||||
Push a new value to the record set. `value` should be serialisable.
|
||||
|
||||
#### `cache.remove(recordName, value)`
|
||||
|
||||
Remove a value from the record set. `value` should be a previously added value.
|
||||
|
||||
#### `var list = cache.get(recordName, [maxCount])`
|
||||
|
||||
Get a list of values from the record set. The list will be randomised.
|
||||
Specify `maxCount` to only get this many values at max.
|
||||
|
||||
#### `cache.size`
|
||||
|
||||
Get the actual size of the cache.
|
||||
|
||||
#### `cache.clear()`
|
||||
|
||||
Clear all values from the cache.
|
||||
|
||||
#### `cache.destroy()`
|
||||
|
||||
Completely destroy the cache. Needed if you are using the `maxAge` option to
|
||||
cancel the gc timer.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
var recordCache = require('./')
|
||||
|
||||
var rc = recordCache({
|
||||
maxAge: 10,
|
||||
maxSize: 100
|
||||
})
|
||||
|
||||
rc.add('hello', 'world')
|
||||
rc.add('hello', 'welt')
|
||||
rc.add('hello', 'verden')
|
||||
|
||||
console.log(rc.get('hello', 2))
|
||||
setTimeout(() => console.log(rc.get('hello', 2)), 200)
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
const b4a = require('b4a')
|
||||
|
||||
var EMPTY = []
|
||||
|
||||
module.exports = RecordCache
|
||||
|
||||
function RecordSet () {
|
||||
this.list = []
|
||||
this.map = new Map()
|
||||
}
|
||||
|
||||
RecordSet.prototype.add = function (record, value) {
|
||||
var k = toString(record)
|
||||
var r = this.map.get(k)
|
||||
if (r) return false
|
||||
|
||||
r = {index: this.list.length, record: value || record}
|
||||
this.list.push(r)
|
||||
this.map.set(k, r)
|
||||
return true
|
||||
}
|
||||
|
||||
RecordSet.prototype.remove = function (record) {
|
||||
var k = toString(record)
|
||||
var r = this.map.get(k)
|
||||
if (!r) return false
|
||||
|
||||
swap(this.list, r.index, this.list.length - 1)
|
||||
this.list.pop()
|
||||
this.map.delete(k)
|
||||
return true
|
||||
}
|
||||
|
||||
function RecordStore () {
|
||||
this.records = new Map()
|
||||
this.size = 0
|
||||
}
|
||||
|
||||
RecordStore.prototype.add = function (name, record, value) {
|
||||
var r = this.records.get(name)
|
||||
|
||||
if (!r) {
|
||||
r = new RecordSet()
|
||||
this.records.set(name, r)
|
||||
}
|
||||
|
||||
if (r.add(record, value)) {
|
||||
this.size++
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
RecordStore.prototype.remove = function (name, record, value) {
|
||||
var r = this.records.get(name)
|
||||
if (!r) return false
|
||||
|
||||
if (r.remove(record, value)) {
|
||||
this.size--
|
||||
if (!r.map.size) this.records.delete(name)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
RecordStore.prototype.get = function (name) {
|
||||
var r = this.records.get(name)
|
||||
return r ? r.list : EMPTY
|
||||
}
|
||||
|
||||
function RecordCache (opts) {
|
||||
if (!(this instanceof RecordCache)) return new RecordCache(opts)
|
||||
if (!opts) opts = {}
|
||||
|
||||
this.maxSize = opts.maxSize || Infinity
|
||||
this.maxAge = opts.maxAge || 0
|
||||
|
||||
this._onstale = opts.onStale || opts.onstale || null
|
||||
this._fresh = new RecordStore()
|
||||
this._stale = new RecordStore()
|
||||
this._interval = null
|
||||
this._gced = false
|
||||
|
||||
if (this.maxAge && this.maxAge < Infinity) {
|
||||
// 2/3 gives us a span of 0.66-1.33 maxAge or avg maxAge
|
||||
var tick = Math.ceil(2 / 3 * this.maxAge)
|
||||
this._interval = setInterval(this._gcAuto.bind(this), tick)
|
||||
if (this._interval.unref) this._interval.unref()
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(RecordCache.prototype, 'size', {
|
||||
get: function () {
|
||||
return this._fresh.size + this._stale.size
|
||||
}
|
||||
})
|
||||
|
||||
RecordCache.prototype.add = function (name, record, value) {
|
||||
this._stale.remove(name, record, value)
|
||||
if (this._fresh.add(name, record, value) && this._fresh.size > this.maxSize) {
|
||||
this._gc()
|
||||
}
|
||||
}
|
||||
|
||||
RecordCache.prototype.remove = function (name, record, value) {
|
||||
this._fresh.remove(name, record, value)
|
||||
this._stale.remove(name, record, value)
|
||||
}
|
||||
|
||||
RecordCache.prototype.get = function (name, n) {
|
||||
var a = this._fresh.get(name)
|
||||
var b = this._stale.get(name)
|
||||
var aLen = a.length
|
||||
var bLen = b.length
|
||||
var len = aLen + bLen
|
||||
|
||||
if (n > len || !n) n = len
|
||||
var result = new Array(n)
|
||||
|
||||
for (var i = 0; i < n; i++) {
|
||||
var j = Math.floor(Math.random() * (aLen + bLen))
|
||||
if (j < aLen) {
|
||||
result[i] = a[j].record
|
||||
swap(a, j, --aLen)
|
||||
} else {
|
||||
j -= aLen
|
||||
result[i] = b[j].record
|
||||
swap(b, j, --bLen)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
RecordCache.prototype._gcAuto = function () {
|
||||
if (!this._gced) this._gc()
|
||||
this._gced = false
|
||||
}
|
||||
|
||||
RecordCache.prototype._gc = function () {
|
||||
if (this._onstale && this._stale.size > 0) this._onstale(this._stale)
|
||||
this._stale = this._fresh
|
||||
this._fresh = new RecordStore()
|
||||
this._gced = true
|
||||
}
|
||||
|
||||
RecordCache.prototype.clear = function () {
|
||||
this._gc()
|
||||
this._gc()
|
||||
}
|
||||
|
||||
RecordCache.prototype.destroy = function () {
|
||||
this.clear()
|
||||
clearInterval(this._interval)
|
||||
this._interval = null
|
||||
}
|
||||
|
||||
function toString (record) {
|
||||
return b4a.isBuffer(record) ? b4a.toString(record, 'hex') : record
|
||||
}
|
||||
|
||||
function swap (list, a, b) {
|
||||
var tmp = list[a]
|
||||
tmp.index = b
|
||||
list[b].index = a
|
||||
list[a] = list[b]
|
||||
list[b] = tmp
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "record-cache",
|
||||
"version": "1.2.0",
|
||||
"description": "Cache optimised for record like things",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"b4a": "^1.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"standard": "^10.0.3",
|
||||
"tape": "^4.8.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "standard && tape test.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mafintosh/record-cache.git"
|
||||
},
|
||||
"author": "Mathias Buus (@mafintosh)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/mafintosh/record-cache/issues"
|
||||
},
|
||||
"homepage": "https://github.com/mafintosh/record-cache"
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
var tape = require('tape')
|
||||
var recordCache = require('./')
|
||||
|
||||
tape('add and get', function (t) {
|
||||
var rc = recordCache()
|
||||
rc.add('hello', 'world')
|
||||
t.same(rc.get('hello'), ['world'])
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('add and get buffer', function (t) {
|
||||
var rc = recordCache()
|
||||
rc.add('hello', Buffer.from('world'))
|
||||
t.same(rc.get('hello'), [Buffer.from('world')])
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('add and get (more than one)', function (t) {
|
||||
var rc = recordCache()
|
||||
rc.add('hello', 'world')
|
||||
rc.add('hello', 'verden')
|
||||
rc.add('hello', 'welt')
|
||||
t.same(rc.get('hello').sort(), ['verden', 'welt', 'world'])
|
||||
|
||||
var list = rc.get('hello', 2)
|
||||
t.ok(list[0] !== list[1])
|
||||
t.ok(['verden', 'welt', 'world'].includes(list[0]))
|
||||
t.ok(['verden', 'welt', 'world'].includes(list[1]))
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('get is randomised', function (t) {
|
||||
var rc = recordCache()
|
||||
|
||||
rc.add('hello', 'a')
|
||||
rc.add('hello', 'b')
|
||||
rc.add('hello', 'c')
|
||||
|
||||
var map = {}
|
||||
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
map[rc.get('hello', 2).join('')] = true
|
||||
}
|
||||
|
||||
t.same(map, {ab: true, ba: true, cb: true, bc: true, ac: true, ca: true})
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('get capped', function (t) {
|
||||
var rc = recordCache({maxSize: 10})
|
||||
|
||||
for (var i = 0; i < 50; i++) {
|
||||
rc.add('hello', '' + i)
|
||||
}
|
||||
|
||||
t.ok(rc.get('hello').length <= 20)
|
||||
t.ok(rc.size <= 20)
|
||||
t.notOk(rc.get('hello').includes('0'))
|
||||
t.notOk(rc.get('hello').includes('29'))
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('get capped with many record sets', function (t) {
|
||||
var rc = recordCache({maxSize: 10})
|
||||
|
||||
for (var i = 0; i < 50; i++) {
|
||||
rc.add('' + i, 'hello')
|
||||
}
|
||||
|
||||
t.ok(rc.size <= 20)
|
||||
t.same(rc.get('0'), [])
|
||||
t.same(rc.get('29'), [])
|
||||
t.same(rc.get('49'), ['hello'])
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('many updates is fine when capped', function (t) {
|
||||
var rc = recordCache({maxSize: 10})
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
rc.add('hello', '' + i)
|
||||
}
|
||||
for (var j = 0; j < 100; j++) {
|
||||
rc.add('hello', '9')
|
||||
}
|
||||
|
||||
t.same(rc.get('hello').sort().join(''), '0123456789')
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('remove', function (t) {
|
||||
var rc = recordCache()
|
||||
|
||||
t.same(rc.get('hello'), [])
|
||||
rc.remove('hello', 'world')
|
||||
t.same(rc.get('hello'), [])
|
||||
rc.add('hello', 'world')
|
||||
t.same(rc.get('hello'), ['world'])
|
||||
rc.remove('hello', 'world')
|
||||
t.same(rc.get('hello'), [])
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('remove with other value', function (t) {
|
||||
var rc = recordCache()
|
||||
|
||||
rc.add('hello', 'hi')
|
||||
t.same(rc.get('hello'), ['hi'])
|
||||
rc.remove('hello', 'world')
|
||||
t.same(rc.get('hello'), ['hi'])
|
||||
rc.add('hello', 'world')
|
||||
t.same(rc.get('hello').sort(), ['hi', 'world'])
|
||||
rc.remove('hello', 'world')
|
||||
t.same(rc.get('hello'), ['hi'])
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('clear', function (t) {
|
||||
var rc = recordCache()
|
||||
|
||||
rc.clear()
|
||||
t.same(rc.get('hello'), [])
|
||||
rc.add('hello', 'a')
|
||||
rc.add('hello', 'b')
|
||||
rc.add('foo', 'bar')
|
||||
t.same(rc.get('hello').sort(), ['a', 'b'])
|
||||
rc.clear()
|
||||
t.same(rc.get('hello'), [])
|
||||
t.same(rc.get('foo'), [])
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('maxAge', function (t) {
|
||||
var rc = recordCache({maxAge: 20})
|
||||
|
||||
rc.add('hello', 'world')
|
||||
rc.add('hello', 'verden')
|
||||
setTimeout(function () {
|
||||
t.same(rc.get('hello').sort(), ['verden', 'world'])
|
||||
setTimeout(function () {
|
||||
t.same(rc.get('hello'), [])
|
||||
rc.destroy()
|
||||
t.end()
|
||||
}, 35)
|
||||
}, 5)
|
||||
})
|
||||
|
||||
tape('maxAge but one value is staying alive', function (t) {
|
||||
var rc = recordCache({maxAge: 20})
|
||||
|
||||
rc.add('hello', 'world')
|
||||
rc.add('hello', 'verden')
|
||||
var interval = setInterval(function () {
|
||||
rc.add('hello', 'verden')
|
||||
}, 5)
|
||||
setTimeout(function () {
|
||||
t.same(rc.get('hello').sort(), ['verden', 'world'])
|
||||
setTimeout(function () {
|
||||
clearInterval(interval)
|
||||
t.same(rc.get('hello'), ['verden'])
|
||||
rc.destroy()
|
||||
t.end()
|
||||
}, 35)
|
||||
}, 5)
|
||||
})
|
||||
|
||||
tape('add dedups buffers', function (t) {
|
||||
var rc = recordCache()
|
||||
|
||||
rc.add('hello', Buffer.from('world'))
|
||||
rc.add('hello', Buffer.from('world'))
|
||||
|
||||
t.same(rc.get('hello'), [Buffer.from('world')])
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape('add and remove buffer', function (t) {
|
||||
var rc = recordCache()
|
||||
|
||||
rc.add('hello', Buffer.from('world'))
|
||||
rc.remove('hello', Buffer.from('world'))
|
||||
|
||||
t.same(rc.get('hello'), [])
|
||||
t.end()
|
||||
})
|
||||
Reference in New Issue
Block a user