├── .gitignore ├── .travis.yml ├── README.md ├── bench ├── cat.jpg ├── read.js └── write.js ├── example ├── capped.js ├── file.txt └── simple.js ├── index.js ├── lib ├── indexes.js └── util.js ├── package.json └── test ├── append.js ├── capped-appending.js ├── capped.js ├── delete.js ├── end.js ├── exists.js ├── fixtures └── file.txt ├── get.js ├── head.js ├── indexes.test.js ├── key-collisions.js ├── key-stream.js ├── keys.js ├── limit.js ├── live.js ├── opts.js ├── readstream.js ├── replace.js ├── reset.js ├── resume.js ├── reverse.js ├── set.js ├── store.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw* 2 | node_modules 3 | bench/.db 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "0.12" 5 | - 4 6 | - 5 7 | - 6 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # level-store 2 | 3 | A streaming storage engine based on [LevelDB](https://github.com/rvagg/node-levelup). It is 4 | 5 | * **faster** than the `fs` module 6 | * **local** in contrast to Amazon S3 7 | * **streaming** from the first byte in contrast to Amazon S3 8 | * **appending** values when desired 9 | * **resuming** reads when something failed 10 | * **in-process** 11 | 12 | [![Build Status](https://travis-ci.org/juliangruber/level-store.svg)](https://travis-ci.org/juliangruber/level-store) 13 | [![downloads](https://img.shields.io/npm/dm/level-store.svg)](https://www.npmjs.org/package/level-store) 14 | 15 | 16 | Only need in-memory persistence and no resuming? Check out [enstore](https://github.com/juliangruber/enstore). 17 | 18 | *** 19 | 20 | ## Usage 21 | 22 | Store a file in LevelDB under the key `file` and read it out again: 23 | 24 | ```js 25 | var levelup = require('level'); 26 | var Store = require('level-store'); 27 | var fs = require('fs'); 28 | 29 | var store = Store(levelup('/tmp/level-store')); 30 | 31 | fs.createReadStream(__dirname + '/file.txt') 32 | .pipe(store.createWriteStream('file')) 33 | .on('close', function () { 34 | // file.txt is stored in leveldb now 35 | store.createReadStream('file').pipe(process.stdout); 36 | }); 37 | ``` 38 | 39 | ## Live persistence 40 | 41 | If you want to persist a stream and read from it at the same time, without reading what didn't get stored yet, 42 | you can do it like this: 43 | 44 | ```js 45 | // first start reading from the stored version 46 | storage.createReadStream('stream', { live: true }).pipe(someWhere); 47 | // then put your stream into the store 48 | stream.pipe(storage.createWriteStream('stream')); 49 | ``` 50 | 51 | ## Resuming 52 | 53 | When reading fails you might not want to start over again completely but rather 54 | resume after the last chunk you received. First, pass `index: true` as an 55 | option so you don't only get the stored chunks but also their index in the 56 | store: 57 | 58 | ```js 59 | store.createReadStream('file', { index: true }).on('data', console.log); 60 | // => { index: 1363783762087, data : } 61 | ``` 62 | 63 | Now you only need store the timestamp of the last read chunk in a variable and you can 64 | resume reading after an error, passing `{ gt: index }`: 65 | 66 | ```js 67 | store 68 | .createReadStream('file', { gt: 1363783762087, index: true }) 69 | .on('data', console.log); 70 | // => { index: 1363783876109, data : } 71 | ``` 72 | 73 | ## Indexes 74 | 75 | You can choose from several indexing mechanisms, which are from fastest to 76 | slowest: 77 | 78 | * **timestamp**: The default index. Uses timestamps of when a chunk was written. 79 | **Fast** and already enough for resuming. Activate with 80 | `Store(db, { index : 'timestamp' })`. 81 | * **chunks**: The index is the number of chunks already written, starting at `0`. 82 | Activate with `Store(db, { index : 'chunks' })`. 83 | * **TODO**: **bytelength**: The index is the bytelength of what has already been written 84 | under the given `key`. **Slow**, but very flexible. Activate with 85 | `Store(db, { index : 'bytelength' })`. 86 | 87 | ## Capped streams 88 | 89 | If you don't want your stream to grow infinitely and it's ok to cut old parts 90 | off, use `{ capped : x }` to limit to stream to `x` chunks: 91 | 92 | ```js 93 | store.createWriteStream('file', { capped : 3 }).write('...'); 94 | ``` 95 | 96 | ## API 97 | 98 | ### Store(db[, opts]) 99 | 100 | Returns a `level-store` instance. 101 | 102 | `db` is **an instance of LevelUp**. 103 | 104 | If `opts.index` is set, that indexing mechanism is used intead of the 105 | default one (`timestamp`). 106 | 107 | ### store#createReadStream(key[, opts]) 108 | 109 | A readable stream that replays the stream stored at `key`. 110 | 111 | Possible `options` are: 112 | 113 | * `index (Boolean|String)`: If `true`, don't emit raw chunks but rather objects having 114 | `index` and `data` fields. If a `String`, override the index passed to `Store()`. 115 | * `gt (Number|String)`: Emit chunks that have been stored after the given position. 116 | * `gte (Number|String)`: Emit chunks that have been stored at or after the given position. 117 | * `lt (Number|String)`: Emit chunks that have been stored before the given position. 118 | * `lte (Number|String)`: Emit chunks that have been stored at or before the given position. 119 | * `live (Boolean)`: If `true`, the stream will stay open, emitting new data as it comes in. 120 | * `reverse (Boolean)`: If `true`, chunks will be emitted in reverse order. 121 | * `limit (Number)`: Receive max. `limit` chunks. 122 | * `valueEncoding (String)`: Use a specific encoding for values. 123 | 124 | ### store#createWriteStream(key[, opts]) 125 | 126 | A writable stream that persists data written to it under `key`. If something exists under `key` 127 | already it will be deleted. 128 | 129 | Possible `options` are: 130 | 131 | * `append (Boolean)`: If `true`, possibly already existing data stored under `key` will be appended 132 | rather than replaced. 133 | * `capped (Number)`: If set, cap the stream to `x` chunks. 134 | * `index (String)`: Override the index passed to `Store()`. 135 | 136 | ### store#createKeyStream(opts) 137 | 138 | A readable stream that emits all the keys of all streams that are stored. 139 | 140 | Possible `options` are: 141 | 142 | * `reverse (Boolean)` 143 | 144 | ### store#get(key[, opts], cb) 145 | 146 | Async version of `createReadStream`. 147 | 148 | ### store#set(key, value[, opts], cb) 149 | 150 | Async version of `createWriteStream`. 151 | 152 | ### store#keys(cb) 153 | 154 | Async version of `createKeyStream`, without reverse sorting capability. 155 | 156 | ### store#delete(key[, cb]) 157 | 158 | Delete everything stored under `key`. _Returns_ an error if nothing was stored 159 | under `key`. 160 | 161 | ### store#reset(key[, cb]) 162 | 163 | Delete everything stored under `key`. _Doesn't return_ an error if nothing was stored 164 | under `key`. 165 | 166 | ### store#exists(key, cb) 167 | 168 | Check if `key` exists and call `cb` with `(err, exists)`. 169 | 170 | ### store#head(key[, opts], cb) 171 | 172 | Get the last chunk stored under `key`. `opts` are treated like in 173 | `db#createReadStream`. `cb` gets called with `(err, chunk)`. 174 | 175 | ### store#append(key, value[, opts][, cb]) 176 | 177 | Sugar for appending just one `value` to `key`. 178 | 179 | If `opts.index` is set that overrides the index passed to `Store()`. 180 | 181 | ## Installation 182 | 183 | With [npm](http://npmjs.org) do 184 | 185 | ```bash 186 | $ npm install level-store 187 | ``` 188 | 189 | ## License 190 | 191 | Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> 192 | 193 | 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: 194 | 195 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 196 | 197 | 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. 198 | -------------------------------------------------------------------------------- /bench/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliangruber/level-store/f73526f496e84e846affe0263f251eb469b6ad49/bench/cat.jpg -------------------------------------------------------------------------------- /bench/read.js: -------------------------------------------------------------------------------- 1 | var ben = require('ben'); 2 | var Store = require('..'); 3 | var level = require('level'); 4 | var fs = require('fs'); 5 | var rimraf = require('rimraf'); 6 | 7 | // ~0.11 ms 8 | 9 | rimraf.sync(__dirname + '/.db'); 10 | level(__dirname + '/.db', { valueEncoding: 'binary' }, function (err, db) { 11 | if (err) throw err; 12 | 13 | var store = Store(db); 14 | fs.createReadStream(__dirname + '/cat.jpg') 15 | .pipe(store.createWriteStream('cat')) 16 | .on('close', function () { 17 | var test = function (done) { 18 | store.createReadStream('cat').on('close', done); 19 | }; 20 | 21 | ben.async(10000, test, function (ms) { 22 | console.log('%s milliseconds per iteration', ms); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /bench/write.js: -------------------------------------------------------------------------------- 1 | var ben = require('ben'); 2 | var Store = require('..'); 3 | var level = require('level'); 4 | var fs = require('fs'); 5 | var rimraf = require('rimraf'); 6 | 7 | // ~1.32ms 8 | 9 | rimraf.sync(__dirname + '/.db'); 10 | level(__dirname + '/.db', { valueEncoding: 'binary' }, function (err, db) { 11 | if (err) throw err; 12 | 13 | var store = Store(db); 14 | 15 | var test = function (done) { 16 | fs.createReadStream(__dirname + '/cat.jpg') 17 | .pipe(store.createWriteStream(Math.random()+'')) 18 | .on('close', done); 19 | }; 20 | 21 | ben.async(1000, test, function (ms) { 22 | console.log('%s milliseconds per iteration', ms); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /example/capped.js: -------------------------------------------------------------------------------- 1 | var Store = require('..'); 2 | var os = require('os'); 3 | var level = require('level'); 4 | 5 | var store = Store(level(os.tmpDir() + '/level-store-capped')); 6 | 7 | var ws = store.createWriteStream('updates', { capped : 2 }); 8 | 9 | ws.write('foo'); 10 | ws.write('bar'); 11 | ws.write('baz'); 12 | ws.end(); 13 | 14 | ws.on('close', function () { 15 | store.createReadStream('updates').on('data', console.log); 16 | }); 17 | -------------------------------------------------------------------------------- /example/file.txt: -------------------------------------------------------------------------------- 1 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 2 | -------------------------------------------------------------------------------- /example/simple.js: -------------------------------------------------------------------------------- 1 | var Store = require('..'); 2 | var os = require('os'); 3 | var fs = require('fs'); 4 | var level = require('level'); 5 | 6 | var store = Store(level(os.tmpDir() + '/level-store-example')); 7 | 8 | fs.createReadStream(__dirname + '/file.txt') 9 | .pipe(store.createWriteStream('file')) 10 | .on('close', function () { 11 | store.createReadStream('file').pipe(process.stdout); 12 | }); 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var through = require('through'); 2 | var duplexer = require('duplexer'); 3 | var liveStream = require('level-live-stream'); 4 | var deleteRange = require('level-delete-range'); 5 | var cap = require('level-capped'); 6 | var peek = require('level-peek'); 7 | var fix = require('level-fix-range'); 8 | var indexes = require('./lib/indexes'); 9 | var WriteStream = require('level-ws').WriteStream; 10 | 11 | module.exports = Store; 12 | 13 | function noop () {} 14 | 15 | function Store (db, opts) { 16 | if (!(this instanceof Store)) return new Store(db, opts); 17 | this.db = db; 18 | this.index = (opts || {}).index || 'timestamp'; 19 | } 20 | 21 | Store.prototype.delete = function (key, cb) { 22 | var self = this; 23 | self.exists(key, function (err, exists) { 24 | if (err) return cb(err); 25 | if (!exists) return cb(new Error('Stream not found.')); 26 | self.reset(key, cb); 27 | }); 28 | }; 29 | 30 | Store.prototype.reset = function (key, cb) { 31 | deleteRange(this.db, { 32 | start : key + ' ', 33 | end : key + '~' 34 | }, cb || noop); 35 | }; 36 | 37 | Store.prototype.exists = function (key, cb) { 38 | var exists = false; 39 | var opts = { 40 | start : key + ' ', 41 | end : key + '~', 42 | limit : 1 43 | }; 44 | 45 | var keys = this.db.createKeyStream(opts); 46 | 47 | keys.on('data', function () { 48 | exists = true; 49 | keys.destroy(); 50 | cb(null, exists); 51 | }); 52 | 53 | keys.on('end', function () { 54 | if (!exists) cb(null, false); 55 | }); 56 | 57 | keys.on('error', cb); 58 | }; 59 | 60 | Store.prototype.createKeyStream = function (opts) { 61 | if (!opts) opts = {}; 62 | var method = opts.reverse 63 | ? 'last' 64 | : 'first'; 65 | 66 | var tr = through(); 67 | var db = this.db; 68 | 69 | (function next (key) { 70 | var cfg = {}; 71 | if (key) cfg[opts.reverse? 'end': 'start'] = key; 72 | peek[method](db, cfg, function (err, _key) { 73 | if (err) { 74 | if (err.message == 'range not found') return tr.end(); 75 | tr.emit('error', err); 76 | } 77 | 78 | var segs = _key.split(' '); 79 | segs.pop(); 80 | _key = segs.join(' '); 81 | 82 | tr.queue(_key); 83 | 84 | if (opts.reverse) { 85 | var k = _key + ''; 86 | k = k.substr(0, k.length - 2) 87 | + String.fromCharCode(k.charCodeAt(k.length - 1) - 1) 88 | + '~'; 89 | next(k); 90 | } else { 91 | next(_key + '!'); 92 | } 93 | }); 94 | })(); 95 | 96 | return tr; 97 | }; 98 | 99 | Store.prototype.keys = function (cb) { 100 | var keys = []; 101 | var called = false; 102 | this.createKeyStream() 103 | .on('data', function (key) { 104 | keys.push(key); 105 | }) 106 | .on('error', done) 107 | .on('end', done); 108 | 109 | function done (err) { 110 | if (called) return; 111 | called = true; 112 | if (err) cb(err); 113 | else cb(null, keys); 114 | } 115 | }; 116 | 117 | Store.prototype.createWriteStream = function (key, opts) { 118 | if (!opts) opts = {}; 119 | 120 | var index = this._getIndex(opts.index, key); 121 | var input = through(function (chunk) { 122 | this.queue({ 123 | key: index.newKey(), 124 | value: chunk 125 | }); 126 | }).pause(); 127 | var ws = new WriteStream(opts, this.db); 128 | 129 | var dpl = duplexer(input, ws); 130 | input.pipe(ws); 131 | 132 | if (typeof opts.capped != 'undefined') { 133 | var capped = cap(this.db, key, opts.capped); 134 | ws.on('end', capped.end.bind(capped)); 135 | } 136 | 137 | if (opts.append) { 138 | if (index.initialize) index.initialize(ready); 139 | else ready(); 140 | } else { 141 | this.reset(key, ready); 142 | } 143 | 144 | function ready (err) { 145 | if (err) dpl.emit('error', err); 146 | input.resume(); 147 | } 148 | 149 | return dpl; 150 | } 151 | 152 | Store.prototype.createReadStream = function (key, opts) { 153 | if (!opts) opts = {}; 154 | 155 | // backwards compatibility 156 | if (opts.from) opts.gt = opts.from; 157 | if (opts.to) opts.lte = opts.to; 158 | 159 | // choose index 160 | var index = this._getIndex(opts.index, key); 161 | 162 | // set start 163 | var start = key + ' '; 164 | if (index.modKey && (opts.gt || opts.gte)) { 165 | start += index.modKey(opts.gt || opts.gte); 166 | } else { 167 | if (typeof opts.gt != 'undefined') start += opts.gt; 168 | else if (typeof opts.gte != 'undefined') start += opts.gte; 169 | } 170 | 171 | // set end 172 | var end = key; 173 | if (index.modKey && (opts.lt || opts.lte)) { 174 | end += ' ' + index.modKey(opts.lt || opts.lte); 175 | } else { 176 | if (typeof opts.lt != 'undefined') end += ' ' + opts.lt; 177 | else if (typeof opts.lte != 'undefined') end += ' ' + opts.lte; 178 | else end += '~'; 179 | } 180 | 181 | var cfg = fix({ 182 | start: start, 183 | end: end, 184 | reverse: opts.reverse 185 | }); 186 | if (opts.valueEncoding) cfg.valueEncoding = opts.valueEncoding; 187 | 188 | var rs = opts.live 189 | ? liveStream(this.db, cfg) 190 | : this.db.createReadStream(cfg) 191 | 192 | var received = 0; 193 | 194 | return rs.pipe(through(function (chunk) { 195 | chunk = parseIndex(key, chunk); 196 | if (index.parseIndex) chunk = index.parseIndex(chunk); 197 | 198 | var idx = chunk.index; 199 | if ( (typeof opts.lt == 'undefined' || idx < opts.lt) 200 | && (typeof opts.lte == 'undefined' || idx <= opts.lte) 201 | && (typeof opts.gt == 'undefined' || idx > opts.gt) 202 | && (typeof opts.gte == 'undefined' || idx >= opts.gte) 203 | ) { 204 | if (typeof opts.limit != 'undefined' && received++ >= opts.limit) { 205 | rs.destroy(); 206 | } else { 207 | this.queue(opts.index? chunk : chunk.data); 208 | } 209 | } 210 | })); 211 | }; 212 | 213 | Store.prototype.get = function (key, opts, cb) { 214 | if (typeof opts == 'function') { 215 | cb = opts; 216 | opts = {}; 217 | } 218 | var data; 219 | var called = false; 220 | var read = false; 221 | 222 | this.createReadStream(key, opts) 223 | .on('error', done) 224 | .on('end', done) 225 | .on('data', function (d) { 226 | read = true; 227 | if (Buffer.isBuffer(d)) { 228 | if (!data) data = []; 229 | data.push(d); 230 | } else { 231 | if (!data) data = ''; 232 | data += d; 233 | } 234 | }); 235 | 236 | function done (err) { 237 | if (called) return; 238 | called = true; 239 | if (err) return cb(err); 240 | if (!read) return cb(new Error('Stream not found.')); 241 | if (Array.isArray(data)) data = Buffer.concat(data); 242 | cb(null, data); 243 | } 244 | }; 245 | 246 | Store.prototype.head = function (key, opts, cb) { 247 | var self = this; 248 | if (typeof opts == 'function') { 249 | cb = opts; 250 | opts = {}; 251 | } 252 | var index = this._getIndex(opts.index); 253 | 254 | peek.last(self.db, { 255 | start: key + ' ', 256 | end: key + '~' 257 | }, function (err, _key, _value) { 258 | if (err) return cb(err); 259 | 260 | var chunk = parseIndex(key, { key: _key, value: _value }); 261 | if (index.parseIndex) chunk = index.parseIndex(chunk); 262 | if (!opts.index) chunk = chunk.data; 263 | cb(null, chunk); 264 | }); 265 | }; 266 | 267 | Store.prototype.set = function (key, value, opts, cb) { 268 | if (typeof opts == 'function') { 269 | cb = opts; 270 | opts = {}; 271 | } 272 | if (!opts) opts = {}; 273 | if (!cb) cb = function () {}; 274 | var ws = this.createWriteStream(key, opts); 275 | ws.on('close', cb); 276 | ws.on('error', cb); 277 | ws.write(value); 278 | ws.end(); 279 | }; 280 | 281 | Store.prototype.append = function (key, value, opts, cb) { 282 | if (typeof opts == 'function') { 283 | cb = opts; 284 | opts = {}; 285 | } 286 | if (!opts) opts = {}; 287 | opts.append = true; 288 | this.set(key, value, opts, cb); 289 | }; 290 | 291 | function parseIndex (key, chunk) { 292 | return { 293 | index: chunk.key.slice(key.length + 1), 294 | data: chunk.value 295 | }; 296 | }; 297 | 298 | Store.prototype._getIndex = function (name, key) { 299 | var idx = typeof name === 'string' 300 | ? name 301 | : this.index; 302 | return indexes[idx](this.db, key); 303 | }; 304 | -------------------------------------------------------------------------------- /lib/indexes.js: -------------------------------------------------------------------------------- 1 | var through = require('through'); 2 | var timestamp = require('monotonic-timestamp'); 3 | var peek = require('level-peek'); 4 | var padHex = require('./util').padHex; 5 | var unpadHex = require('./util').unpadHex; 6 | 7 | var indexes = module.exports = {} 8 | 9 | indexes.timestamp = function (db, key) { 10 | var idx = {}; 11 | idx.newKey = function () { 12 | return key + ' ' + timestamp(); 13 | } 14 | return idx; 15 | } 16 | 17 | indexes.chunks = function (db, key) { 18 | var idx = {}; 19 | var chunks = 0; 20 | 21 | idx.newKey = function () { 22 | return key + ' ' + padHex(chunks++); 23 | }; 24 | 25 | idx.modKey = padHex; 26 | 27 | idx.initialize = function (cb) { 28 | peek.last(db, { 29 | reverse : true, 30 | start : key + ' ', 31 | end : key + '~' 32 | }, function (err, lastKey) { 33 | if (!err) chunks = unpadHex(lastKey.substr(key.length + 1)); 34 | cb(null); 35 | }); 36 | } 37 | 38 | idx.parseIndex = function (chunk) { 39 | return { 40 | index: unpadHex(chunk.index), 41 | data: chunk.data 42 | }; 43 | } 44 | 45 | return idx; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | var util = module.exports = {}; 2 | 3 | var zeros = '00000000'; //32 bit int 4 | 5 | util.padHex = function (num) { 6 | if(num !== ~~(num)) throw new Error('must be whole number'); 7 | var str = num.toString(16); 8 | return zeros.substring(str.length) + str; 9 | }; 10 | 11 | util.unpadHex = function (numstr) { 12 | return parseInt(/0+([0-9a-f]+)/.exec(numstr)[1], 16); 13 | }; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "level-store", 3 | "description": "A streaming storage engine based on LevelDB.", 4 | "version": "3.11.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/juliangruber/level-store.git" 8 | }, 9 | "homepage": "https://github.com/juliangruber/level-store", 10 | "main": "index.js", 11 | "scripts": { 12 | "test": "tap test/*.js" 13 | }, 14 | "dependencies": { 15 | "duplexer": "~0.1.1", 16 | "level-capped": "~0.0.4", 17 | "level-delete-range": "~0.1.0", 18 | "level-fix-range": "~1.1.2", 19 | "level-live-stream": "1.4.8", 20 | "level-peek": "~1.0.6", 21 | "level-ws": "0.0.1", 22 | "monotonic-timestamp": "~0.0.8", 23 | "through": "~2.2.7" 24 | }, 25 | "devDependencies": { 26 | "ben": "0.0.0", 27 | "level": "^1.4.0", 28 | "rimraf": "~2.2.0", 29 | "tap": "*" 30 | }, 31 | "keywords": [ 32 | "leveldb", 33 | "levelup", 34 | "stream", 35 | "persistent" 36 | ], 37 | "author": { 38 | "name": "Julian Gruber", 39 | "email": "mail@juliangruber.com", 40 | "url": "http://juliangruber.com" 41 | }, 42 | "license": "MIT" 43 | } 44 | -------------------------------------------------------------------------------- /test/append.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('append', function (t, db) { 5 | var store = Store(db); 6 | 7 | var first = store.createWriteStream('key'); 8 | first.write('foo'); 9 | first.on('close', function () { 10 | 11 | var second = store.createWriteStream('key', { append : true }); 12 | second.write('bar'); 13 | second.on('close', function () { 14 | 15 | var data = ''; 16 | store.createReadStream('key') 17 | .on('data', function (d) { data += d }) 18 | .on('end', function () { 19 | t.equal(data, 'foobar', 'appended values'); 20 | t.end(); 21 | }) 22 | }); 23 | second.end(); 24 | }); 25 | first.end(); 26 | }); 27 | 28 | test('sugar', function (t, db) { 29 | var store = Store(db); 30 | store.append('foo', 'bar', function (err) { 31 | t.error(err, 'no error'); 32 | 33 | store.createReadStream('foo').on('data', function (d) { 34 | t.equal(d, 'bar', 'created'); 35 | 36 | store.append('foo', 'baz', function (err) { 37 | t.error(err); 38 | 39 | var data = ''; 40 | store.createReadStream('foo') 41 | .on('data', function (d) { data += d }) 42 | .on('end', function () { 43 | t.equal(data, 'barbaz', 'appended'); 44 | t.end(); 45 | }); 46 | }); 47 | }); 48 | }); 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /test/capped-appending.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('capped appending', function (t, db) { 5 | var store = Store(db); 6 | var ws = store.createWriteStream('key'); 7 | ws.write('foo'); 8 | ws.end(); 9 | ws.on('close', function () { 10 | 11 | ws = store.createWriteStream('key', { capped : 1, append : true }); 12 | ws.write('bar'); 13 | ws.write('baz'); 14 | ws.end(); 15 | ws.on('close', function () { 16 | 17 | setTimeout(function () { 18 | var data = []; 19 | store.createReadStream('key') 20 | .on('data', function (d) { data.push(d) }) 21 | .on('end', function () { 22 | t.deepEqual(data, ['baz'], 'capped to 1'); 23 | t.end(); 24 | }); 25 | }, 500); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/capped.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('capped', function (t, db) { 5 | var store = Store(db); 6 | 7 | var ws = store.createWriteStream('key', { capped : 2 }); 8 | ws.write('foo'); 9 | ws.write('bar'); 10 | ws.write('baz'); 11 | ws.end(); 12 | ws.on('close', function () { 13 | setTimeout(function () { 14 | var data = []; 15 | 16 | store 17 | .createReadStream('key') 18 | .on('data', function (d) { data.push(d) }) 19 | .on('end', function () { 20 | t.deepEqual(data, ['bar', 'baz'], 'deleted first'); 21 | t.end(); 22 | }); 23 | }, 500); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/delete.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('delete existing stream', function (t, db) { 5 | t.plan(4); 6 | var store = Store(db); 7 | 8 | store.append('foo', 'bar', function (err) { 9 | t.error(err); 10 | store.delete('foo', function (err) { 11 | t.error(err); 12 | store.exists('foo', function (err, exists) { 13 | t.error(err); 14 | t.notOk(exists); 15 | }); 16 | }); 17 | }); 18 | }); 19 | 20 | test('delete non existing stream', function (t, db) { 21 | t.plan(1); 22 | Store(db).delete('foo', function (err) { 23 | t.ok(err); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/end.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var fs = require('fs'); 3 | var through = require('through'); 4 | var Store = require('..'); 5 | 6 | var fixture = fs.readFileSync(__dirname + '/fixtures/file.txt', 'utf8'); 7 | 8 | test('end', function (t, db) { 9 | t.plan(3); 10 | 11 | var store = Store(db); 12 | var ws = store.createWriteStream('file'); 13 | ws.write('foo'); 14 | ws.write('bar'); 15 | ws.write('baz'); 16 | ws.end(); 17 | 18 | var i = 0; 19 | 20 | ws.on('close', function () { 21 | store.createReadStream('file', { index: true }) 22 | .on('data', function (chunk) { 23 | if (i++ != 2) return; 24 | store.createReadStream('file', { lte: chunk.index }) 25 | .on('data', function (chunk) { 26 | t.ok(chunk, 'received data'); 27 | }); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/exists.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('exists', function (t, db) { 5 | var store = Store(db); 6 | 7 | store.exists('foo', function (err, exists) { 8 | t.error(err, 'no error'); 9 | t.equal(exists, false, 'initially false'); 10 | 11 | var ws = store.createWriteStream('foo'); 12 | ws.write('bar'); 13 | ws.write('baz'); 14 | ws.end(); 15 | ws.on('close', function () { 16 | store.exists('foo', function (err, exists) { 17 | t.error(err, 'no error'); 18 | t.equal(exists, true, 'now exists'); 19 | t.end(); 20 | }); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/fixtures/file.txt: -------------------------------------------------------------------------------- 1 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 2 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 3 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 4 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 5 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 6 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 7 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 8 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 9 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 10 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 11 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 12 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 13 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 14 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 15 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 16 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 17 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 18 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 19 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 20 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 21 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 22 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 23 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 24 | Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify.Deserunt pour-over pinterest hella eiusmod high life. Ethnic occaecat art party 90's sunt portland. Deserunt aliquip quinoa, dreamcatcher pop-up thundercats scenester. Sartorial forage occaecat labore. Ut eiusmod tousled, veniam flannel chillwave tonx. VHS four loko skateboard synth fugiat semiotics. Helvetica synth seitan polaroid banjo, VHS placeat squid cred small batch next level DIY reprehenderit.Nihil meggings beard, chillwave freegan fanny pack et keytar incididunt tonx twee keffiyeh godard. Godard trust fund ea, wolf small batch mlkshk banksy lo-fi bicycle rights cred incididunt viral enim truffaut. YOLO hella gastropub, vegan lo-fi tofu ennui wes anderson. Shoreditch meggings hella excepteur, seitan terry richardson thundercats minim pariatur occupy typewriter. Pitchfork twee gluten-free keffiyeh do cardigan meggings, quinoa ut authentic delectus umami. Trust fund PBR godard 8-bit, fanny pack officia carles cosby sweater accusamus. Quinoa viral scenester nulla mumblecore gentrify. 25 | -------------------------------------------------------------------------------- /test/get.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('get existing stream', function (t, db) { 5 | t.plan(4); 6 | var store = Store(db); 7 | 8 | store.append('foo', 'bar', function (err) { 9 | t.error(err); 10 | store.append('foo', 'baz', function (err) { 11 | t.error(err); 12 | store.get('foo', function (err, data) { 13 | t.error(err); 14 | t.equal(data, 'barbaz'); 15 | }); 16 | }); 17 | }); 18 | }); 19 | 20 | test('get non existing stream', function (t, db) { 21 | t.plan(1); 22 | Store(db).get('foo', function (err) { 23 | t.ok(err); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/head.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('head', function (t, db) { 5 | t.plan(2); 6 | 7 | var ws = Store(db).createWriteStream('times'); 8 | ws.write('1 o clock'); 9 | ws.write('2 o clock'); 10 | ws.end(); 11 | ws.on('close', function () { 12 | Store(db).head('times', function (err, head) { 13 | t.error(err); 14 | t.equal(head, '2 o clock'); 15 | }); 16 | }); 17 | }); 18 | 19 | test('head with index', function (t, db) { 20 | t.plan(3); 21 | 22 | var ws = Store(db).createWriteStream('times'); 23 | ws.write('1 o clock'); 24 | ws.write('2 o clock'); 25 | ws.end(); 26 | ws.on('close', function () { 27 | Store(db).head('times', { index: true }, function (err, head) { 28 | t.error(err); 29 | t.ok(head.index); 30 | t.equal(head.data, '2 o clock'); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/indexes.test.js: -------------------------------------------------------------------------------- 1 | var Store = require('..'); 2 | var test = require('./util'); 3 | var fs = require('fs'); 4 | var rimraf = require('rimraf'); 5 | var through = require('through'); 6 | 7 | test('indexes', function (t, db) { 8 | var store = Store(db); 9 | 10 | fs.createReadStream(__dirname + '/fixtures/file.txt') 11 | .pipe(store.createWriteStream('file')) 12 | .on('close', function () { 13 | store.createReadStream('file', { index: true }) 14 | .pipe(through(function (chunk) { 15 | t.ok(chunk.index, 'chunk index'); 16 | t.ok(chunk.data, 'chunk data'); 17 | })) 18 | .on('end', t.end.bind(t)); 19 | }); 20 | }); 21 | 22 | test('timestamp', function (t, db) { 23 | var store = Store(db); 24 | 25 | t.test('index value', function (t) { 26 | t.plan(2); 27 | var now = Date.now(); 28 | 29 | store.append('foo', 'value', function (err) { 30 | t.error(err); 31 | 32 | store.createReadStream('foo', { index: true }) 33 | .pipe(through(function (chunk) { 34 | t.assert(chunk.index >= now && chunk.index < now + 5000); 35 | })); 36 | }); 37 | }); 38 | 39 | t.test('store', function (t) { 40 | var ws = store.createWriteStream('file'); 41 | 42 | ws.on('close', function () { 43 | var firstIndex; 44 | var i = 0; 45 | 46 | store.createReadStream('file', { index: true }) 47 | .pipe(through(function (chunk) { 48 | if (i++ == 0) { 49 | firstIndex = chunk.index; 50 | } else { 51 | t.ok(chunk.index > firstIndex, 'monotonically increasing'); 52 | t.end(); 53 | } 54 | })) 55 | }); 56 | 57 | ws.write('foo'); 58 | ws.write('bar'); 59 | ws.end(); 60 | }); 61 | 62 | t.test('resume', function (t) { 63 | t.plan(2); 64 | var ws = store.createWriteStream('file'); 65 | 66 | ws.on('close', function () { 67 | var index; 68 | 69 | store.createReadStream('file', { index: true }) 70 | .pipe(through(function (chunk) { 71 | if (!index) index = chunk.index; 72 | })) 73 | .on('end', function () { 74 | store.createReadStream('file', { gt: index, index: true }) 75 | .pipe(through(function (chunk) { 76 | t.notEqual(chunk.index, index, 'skips given index'); 77 | t.equal(chunk.data, 'bar'); 78 | })); 79 | }); 80 | }); 81 | 82 | ws.write('foo'); 83 | ws.write('bar'); 84 | ws.end(); 85 | }); 86 | 87 | t.end(); 88 | }); 89 | 90 | test('chunks', function (t, db) { 91 | var store = Store(db, { index : 'chunks' }); 92 | 93 | t.test('index value', function (t) { 94 | t.plan(2); 95 | 96 | store.append('foo', 'value', function (err) { 97 | t.error(err); 98 | 99 | store.createReadStream('foo', { index: true }) 100 | .pipe(through(function (chunk) { 101 | t.equal(chunk.index, 0); 102 | })); 103 | }); 104 | }); 105 | 106 | t.test('store', function (t) { 107 | var ws = store.createWriteStream('file'); 108 | 109 | ws.on('close', function () { 110 | var i = 0; 111 | 112 | store.createReadStream('file', { index: true }) 113 | .pipe(through(function (chunk) { 114 | if (i++ == 0) { 115 | t.equal(chunk.index, 0, 'first chunk'); 116 | } else { 117 | t.equal(chunk.index, 1, 'second chunk'); 118 | t.end(); 119 | } 120 | })) 121 | }); 122 | 123 | ws.write('foo'); 124 | ws.write('bar'); 125 | ws.end(); 126 | }); 127 | 128 | t.test('resume', function (t) { 129 | var ws = store.createWriteStream('file'); 130 | 131 | ws.on('close', function () { 132 | var data = ''; 133 | 134 | store.createReadStream('file', { gt: 0, index: true }) 135 | .pipe(through(function (chunk) { 136 | t.equal(chunk.index, 1, 'second chunk'); 137 | t.end(); 138 | })) 139 | }); 140 | 141 | ws.write('foo'); 142 | ws.write('bar'); 143 | ws.end(); 144 | }); 145 | 146 | t.end(); 147 | }); 148 | 149 | -------------------------------------------------------------------------------- /test/key-collisions.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var fs = require('fs'); 3 | var through = require('through'); 4 | var Store = require('..'); 5 | 6 | var fixture = fs.readFileSync(__dirname + '/fixtures/file.txt', 'utf8'); 7 | 8 | test('key collisions', function (t, db) { 9 | var store = Store(db); 10 | fs.createReadStream(__dirname + '/fixtures/file.txt') 11 | .pipe(store.createWriteStream('file')) 12 | .on('close', function () { 13 | var data = ''; 14 | store.createReadStream('file2') 15 | .pipe(through(function (chunk) { 16 | data += chunk; 17 | })) 18 | .on('end', function () { 19 | t.equal(data, ''); 20 | t.end(); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/key-stream.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('forward key-stream', function (t, db) { 5 | t.plan(3); 6 | var store = Store(db); 7 | 8 | store.set('a', ' ', function (err) { 9 | t.error(err); 10 | store.set('b', ' ', function (err) { 11 | t.error(err); 12 | var keys = []; 13 | store.createKeyStream() 14 | .on('data', function (key) { 15 | keys.push(key); 16 | }) 17 | .on('end', function () { 18 | t.deepEqual(keys, ['a', 'b']); 19 | }); 20 | }); 21 | }); 22 | }); 23 | 24 | test('backward key-stream', function (t, db) { 25 | t.plan(3); 26 | var store = Store(db); 27 | 28 | store.set('a', ' ', function (err) { 29 | t.error(err); 30 | store.set('b', ' ', function (err) { 31 | t.error(err); 32 | var keys = []; 33 | store.createKeyStream({ reverse: true }) 34 | .on('data', function (key) { 35 | keys.push(key); 36 | }) 37 | .on('end', function () { 38 | t.deepEqual(keys, ['b', 'a']); 39 | }); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/keys.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('keys', function (t, db) { 5 | t.plan(4); 6 | var store = Store(db); 7 | 8 | store.set('a', ' ', function (err) { 9 | t.error(err); 10 | store.set('b', ' ', function (err) { 11 | t.error(err); 12 | store.keys(function (err, keys) { 13 | t.error(err); 14 | t.deepEqual(keys, ['a', 'b']); 15 | }); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/limit.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('limit', function (t, db) { 5 | var store = Store(db); 6 | 7 | store.append('foo', 'bar', function (err) { 8 | t.error(err); 9 | store.append('foo', 'baz', function (err) { 10 | t.error(err); 11 | store.createReadStream('foo', { limit: 1 }).on('data', function (str) { 12 | t.equals(str, 'bar'); 13 | }); 14 | setTimeout(function () { 15 | t.end(); 16 | }, 100); 17 | }) 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/live.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var through = require('through'); 3 | var fs = require('fs'); 4 | var Store = require('..'); 5 | 6 | var fixture = fs.readFileSync(__dirname + '/fixtures/file.txt', 'utf8'); 7 | 8 | test('live', function (t, db) { 9 | var data = ''; 10 | var store = Store(db); 11 | var live = store.createReadStream('file', { live : true }); 12 | 13 | var data = ''; 14 | live.pipe(through(function (chunk) { 15 | data += chunk; 16 | t.ok(true, 'chunk'); 17 | if (data == fixture) t.end(); 18 | })); 19 | 20 | fs.createReadStream(__dirname + '/fixtures/file.txt') 21 | .pipe(store.createWriteStream('file')); 22 | }); 23 | -------------------------------------------------------------------------------- /test/opts.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var fs = require('fs'); 3 | var through = require('through'); 4 | var Store = require('..'); 5 | 6 | var fixture = fs.readFileSync(__dirname + '/fixtures/file.txt', 'utf8'); 7 | 8 | test('opts', function (t, db) { 9 | var ws = db.createWriteStream; 10 | var rs = db.createReadStream; 11 | 12 | db.createWriteStream = db.writeStream = function(opts) { 13 | t.equal(opts.valueEncoding, 'binary'); 14 | return ws.call(db, opts); 15 | }; 16 | db.createReadStream = db.readStream = function(opts) { 17 | if (opts.values !== false) { 18 | t.equal(opts.valueEncoding, 'binary'); 19 | } 20 | return rs.call(db, opts); 21 | }; 22 | 23 | var store = Store(db); 24 | fs.createReadStream(__dirname + '/fixtures/file.txt') 25 | .pipe(store.createWriteStream('file', { valueEncoding: 'binary' })) 26 | .on('close', function () { 27 | var data = ''; 28 | store.createReadStream('file', { valueEncoding: 'binary' }) 29 | .pipe(through(function (chunk) { 30 | data += chunk; 31 | })) 32 | .on('end', function () { 33 | t.equal(data, fixture); 34 | t.end(); 35 | }); 36 | }); 37 | }); 38 | 39 | -------------------------------------------------------------------------------- /test/readstream.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('gt', function (t, db) { 5 | t.plan(2); 6 | 7 | db.batch() 8 | .put('o 0', '0') 9 | .put('o 1', '1') 10 | .write(function (err) { 11 | t.error(err); 12 | 13 | Store(db).createReadStream('o', { gt: 0 }).on('data', function (d) { 14 | t.equal(d, '1'); 15 | }); 16 | }); 17 | }); 18 | 19 | test('gte', function (t, db) { 20 | t.plan(3); 21 | 22 | db.batch() 23 | .put('o 0', '0') 24 | .put('o 1', '1') 25 | .write(function (err) { 26 | t.error(err); 27 | 28 | var i = 0; 29 | Store(db).createReadStream('o', { gte: 0 }).on('data', function (d) { 30 | t.equal(d, String(i++)); 31 | }); 32 | }); 33 | }); 34 | 35 | test('lt', function (t, db) { 36 | t.plan(2); 37 | 38 | db.batch() 39 | .put('o 0', '0') 40 | .put('o 1', '1') 41 | .write(function (err) { 42 | t.error(err); 43 | 44 | Store(db).createReadStream('o', { lt: 1 }).on('data', function (d) { 45 | t.equal(d, '0'); 46 | }); 47 | }); 48 | }); 49 | 50 | test('lte', function (t, db) { 51 | t.plan(3); 52 | 53 | db.batch() 54 | .put('o 0', '0') 55 | .put('o 1', '1') 56 | .write(function (err) { 57 | t.error(err); 58 | 59 | var i = 0; 60 | Store(db).createReadStream('o', { lte: 1 }).on('data', function (d) { 61 | t.equal(d, String(i++)); 62 | }); 63 | }); 64 | }); 65 | 66 | test('encoding', function (t, db) { 67 | t.plan(2); 68 | 69 | db.batch() 70 | .put('o 0', '0') 71 | .write(function (err) { 72 | t.error(err); 73 | 74 | Store(db).createReadStream('o', { valueEncoding: 'binary' }).on('data', function (d) { 75 | t.ok(Buffer.isBuffer(d)); 76 | }); 77 | }); 78 | }); 79 | 80 | test('integration', function (t, db) { 81 | t.plan(2); 82 | 83 | db.batch() 84 | .put('o 0', '0') 85 | .put('o 1', '1') 86 | .put('o 2', '2') 87 | .put('o 3', '3') 88 | .write(function (err) { 89 | t.error(err); 90 | 91 | var i = 0; 92 | Store(db).createReadStream('o', { gte: 2, lt: 3 }).on('data', function (d) { 93 | t.equal(d, '2'); 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /test/replace.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('replace', function (t, db) { 5 | var store = Store(db); 6 | 7 | var first = store.createWriteStream('key'); 8 | first.write('foo'); 9 | first.on('close', function () { 10 | 11 | var second = store.createWriteStream('key'); 12 | second.write('bar'); 13 | second.on('close', function () { 14 | 15 | var data = ''; 16 | store.createReadStream('key') 17 | .on('data', function (d) { data += d }) 18 | .on('end', function () { 19 | t.equal(data, 'bar', 'deleted'); 20 | t.end(); 21 | }) 22 | }); 23 | second.end(); 24 | }); 25 | first.end(); 26 | }); 27 | -------------------------------------------------------------------------------- /test/reset.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('reset existing stream', function (t, db) { 5 | t.plan(4); 6 | var store = Store(db); 7 | 8 | store.append('foo', 'bar', function (err) { 9 | t.error(err); 10 | store.reset('foo', function (err) { 11 | t.error(err); 12 | store.exists('foo', function (err, exists) { 13 | t.error(err); 14 | t.notOk(exists); 15 | }); 16 | }); 17 | }); 18 | }); 19 | 20 | test('reset non existing stream', function (t, db) { 21 | t.plan(1); 22 | Store(db).reset('foo', function (err) { 23 | t.notOk(err); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/resume.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var fs = require('fs'); 3 | var through = require('through'); 4 | var Store = require('..'); 5 | 6 | var fixture = fs.readFileSync(__dirname + '/fixtures/file.txt', 'utf8'); 7 | 8 | test('resume', function (t, db) { 9 | t.plan(1); 10 | 11 | var store = Store(db); 12 | var ws = store.createWriteStream('file'); 13 | ws.write('foo'); 14 | ws.write('bar'); 15 | ws.end(); 16 | 17 | ws.on('close', function () { 18 | 19 | store.createReadStream('file', { index: true }) 20 | .once('data', function (chunk) { 21 | 22 | store.createReadStream('file', { gt: chunk.index }) 23 | .on('data', function (chunk) { 24 | 25 | t.ok(chunk, 'received data'); 26 | }); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/reverse.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('reverse', function (t, db) { 5 | t.plan(2); 6 | var store = Store(db); 7 | var ws = store.createWriteStream('reverse'); 8 | ws.write('first'); 9 | ws.write('last'); 10 | ws.end(); 11 | ws.on('close', function () { 12 | console.log('written') 13 | var i = 0; 14 | store.createReadStream('reverse', { reverse: true }).on('data', function (d) { 15 | t.equal(d, [ 'last', 'first' ][i++]); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/set.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var Store = require('..'); 3 | 4 | test('set stream', function (t, db) { 5 | t.plan(6); 6 | var store = Store(db); 7 | 8 | store.set('foo', 'bar', function (err) { 9 | t.error(err); 10 | store.get('foo', function (err, data) { 11 | t.error(err); 12 | t.equal(data, 'bar'); 13 | store.set('foo', 'baz', function (err) { 14 | t.error(err); 15 | store.get('foo', function (err, data) { 16 | t.error(err); 17 | t.equal(data, 'baz'); 18 | }); 19 | }); 20 | }) 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/store.js: -------------------------------------------------------------------------------- 1 | var test = require('./util'); 2 | var fs = require('fs'); 3 | var through = require('through'); 4 | var Store = require('..'); 5 | 6 | var fixture = fs.readFileSync(__dirname + '/fixtures/file.txt', 'utf8'); 7 | 8 | test('level-store', function (t, db) { 9 | var store = Store(db); 10 | fs.createReadStream(__dirname + '/fixtures/file.txt') 11 | .pipe(store.createWriteStream('file')) 12 | .on('close', function () { 13 | var data = ''; 14 | store.createReadStream('file') 15 | .pipe(through(function (chunk) { 16 | data += chunk; 17 | })) 18 | .on('end', function () { 19 | t.equal(data, fixture); 20 | t.end(); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | var tap = require('tap'); 2 | var os = require('os'); 3 | var levelup = require('level'); 4 | 5 | module.exports = test; 6 | 7 | function test (name, cb) { 8 | if (!cb) return tap.test(name); 9 | tap.test(name, function (t) { 10 | var path = os.tmpDir() + '/' 11 | path += Math.random().toString(16).slice(2) 12 | path += '-level-store-test'; 13 | 14 | levelup(path, function (err, db) { 15 | if (err) throw err; 16 | cb(t, db); 17 | }); 18 | }); 19 | } 20 | --------------------------------------------------------------------------------