├── .travis.yml ├── example.js ├── README.md ├── test └── all.js ├── appveyor.yml ├── package.json ├── LICENSE ├── .gitignore └── index.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '12' 5 | - '14' 6 | - 'lts/*' 7 | 8 | env: 9 | - NODE_ENV=test 10 | 11 | os: 12 | - linux 13 | 14 | cache: 15 | npm: false 16 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | const HyperbeeDown = require('.') 2 | const Hyperbee = require('hyperbee') 3 | const ram = require('random-access-memory') 4 | const hypercore = require('hypercore') 5 | const levelup = require('levelup') 6 | 7 | start() 8 | 9 | async function start () { 10 | const core = hypercore(ram) 11 | const tree = new Hyperbee(core) 12 | const down = new HyperbeeDown(tree) 13 | const db = levelup(down) 14 | 15 | await db.put('hello', 'world') 16 | console.log(await db.get('hello')) 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HyperbeeDown 2 | [![Build Status](https://travis-ci.org/andrewosh/hyperbeedown.svg?branch=master)](https://travis-ci.org/andrewosh/hyperbeedown) 3 | 4 | A Leveldown for Hyperbee. 5 | 6 | ## Installation 7 | `npm i hyperbeedown --save` 8 | 9 | ## Usage 10 | ```js 11 | const core = hypercore(ram) 12 | const tree = new Hyperbee(core) 13 | const down = new HyperbeeDown(tree) 14 | const db = levelup(down) 15 | 16 | await db.put('hello', 'world') 17 | console.log(await db.get('hello')) 18 | ``` 19 | 20 | ## License 21 | [MIT](./LICENSE) 22 | -------------------------------------------------------------------------------- /test/all.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const ram = require('random-access-memory') 3 | const hypercore = require('hypercore') 4 | const Hyperbee = require('hyperbee') 5 | const suite = require('abstract-leveldown/test') 6 | 7 | const HyperbeeDown = require('..') 8 | 9 | suite({ 10 | test, 11 | seek: false, 12 | createIfMissing: false, 13 | errorIfExists: false, 14 | factory: () => { 15 | const tree = new Hyperbee(hypercore(ram), { 16 | keyEncoding: 'utf8' 17 | }) 18 | return new HyperbeeDown(tree) 19 | } 20 | }) 21 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | init: 2 | # Get the latest stable version of Node.js 3 | - ps: Install-Product node $env:nodejs_version 4 | 5 | image: 6 | - Visual Studio 2017 7 | 8 | matrix: 9 | fast_finish: true 10 | 11 | environment: 12 | matrix: 13 | - nodejs_version: "7" 14 | - nodejs_version: "8" 15 | - nodejs_version: "9" 16 | 17 | install: 18 | # install modules 19 | - set PATH=%APPDATA%\npm;%APPVEYOR_BUILD_FOLDER%\node_modules\.bin;%PATH% 20 | - npm i 21 | 22 | # Post-install test scripts. 23 | test_script: 24 | # Output useful info for debugging. 25 | - node --version 26 | - npm --version 27 | - npm test 28 | 29 | build: off 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyperbeedown", 3 | "version": "2.0.1", 4 | "description": "A levelDOWN-compliant backend for Hyperbee", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "standard && node test/all.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/andrewosh/hyperbeedown.git" 12 | }, 13 | "keywords": [ 14 | "leveldown", 15 | "hyperdb", 16 | "hyperarray", 17 | "hyperbtree" 18 | ], 19 | "author": "Andrew Osheroff ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/andrewosh/hyperbeedown/issues" 23 | }, 24 | "homepage": "https://github.com/andrewosh/hyperbeedown#readme", 25 | "devDependencies": { 26 | "hypercore": "^9.5.0", 27 | "levelup": "^4.4.0", 28 | "random-access-memory": "^3.1.1", 29 | "standard": "^10.0.3", 30 | "tape": "^4.8.0" 31 | }, 32 | "dependencies": { 33 | "abstract-leveldown": "^6.3.0", 34 | "hyperbee": "0.0.14", 35 | "inherits": "^2.0.4" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Andrew Osheroff 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { 2 | AbstractLevelDOWN, 3 | AbstractIterator 4 | } = require('abstract-leveldown') 5 | 6 | const EMPTY = Buffer.alloc(0) 7 | 8 | module.exports = class HyperDown extends AbstractLevelDOWN { 9 | constructor (tree) { 10 | super() 11 | // Set in _open. 12 | this.tree = tree 13 | } 14 | 15 | _serializeKey (key) { 16 | if (key === null) throw new Error('key cannot be `null` or `undefined`') 17 | else if (key === undefined) throw new Error('key cannot be `null` or `undefined`') 18 | else if (key === '') throw new Error('key cannot be an empty string') 19 | // Quick check that will pass for valid keys. 20 | else if (typeof key === 'string') return key 21 | else if (Buffer.isBuffer(key) && EMPTY.equals(key)) throw new Error('key cannot be an empty Buffer') 22 | else if ((key instanceof Array) && key.length === 0) throw new Error('key cannot be an empty string') 23 | else if (typeof key === 'number' || typeof key === 'boolean') key = String(key) 24 | else if (Array.isArray(key)) key = key.join(',') 25 | return key 26 | } 27 | 28 | _serializeValue (value) { 29 | if (value && Array.isArray(value)) return value.join(',') 30 | else if (typeof value === 'number' || typeof value === 'boolean') value = String(value) 31 | return value 32 | } 33 | 34 | _open (_, cb) { 35 | // Open options are not handled, because a Hyperbee must be passed in as a constructor arg. 36 | if (!this.tree) return process.nextTick(cb, new Error('A Hyperbee must be provided as a constructor argument')) 37 | 38 | return this.tree.ready().then(cb, err => cb(err)) 39 | } 40 | 41 | _get (key, opts, cb) { 42 | this.tree.get(key, opts).then(node => { 43 | if (!node || !node.value) { 44 | const err = new Error('NotFound') 45 | err.notFound = true 46 | return cb(err) 47 | } 48 | return cb(null, !opts.asBuffer ? node.value.toString('utf-8') : node.value) 49 | }, err => cb(err)) 50 | } 51 | 52 | _put (key, value, _, cb) { 53 | return this.tree.put(key, value).then(() => cb(null), err => cb(err)) 54 | } 55 | 56 | _del (key, _, cb) { 57 | return this.tree.del(key).then(() => cb(null), err => cb(err)) 58 | } 59 | 60 | async _batchPromise (array, opts) { 61 | const batch = this.tree.batch(opts) 62 | for (let { type, key, value } of array) { 63 | key = this._serializeKey(key) 64 | value = value && this._serializeValue(value) 65 | if (type === 'put') await batch.put(key, value) 66 | else if (type === 'del') await batch.del(key) 67 | } 68 | return batch.flush() 69 | } 70 | 71 | _batch (array, opts, cb) { 72 | return this._batchPromise(array, opts).then(() => cb(null), err => cb(err)) 73 | } 74 | 75 | _iterator (opts) { 76 | if (opts.start) { 77 | if (opts.reverse) opts.lte = opts.start 78 | else opts.gte = opts.start 79 | } 80 | if (opts.end) { 81 | if (opts.reverse) opts.gte = opts.end 82 | else opts.lte = opts.end 83 | } 84 | return new HyperIterator(this, opts) 85 | } 86 | } 87 | 88 | class HyperIterator extends AbstractIterator { 89 | constructor (db, opts) { 90 | super(db) 91 | this.tree = db.tree.snapshot() 92 | this.opts = opts 93 | 94 | // Set in first next 95 | 96 | this._keyAsBuffer = opts.keyAsBuffer 97 | this._valueAsBuffer = opts.valueAsBuffer 98 | } 99 | 100 | _next (cb) { 101 | if (!cb) throw new Error('next() requires a callback argument') 102 | if (!this.ite) { 103 | const stream = this.tree.createReadStream(this.opts) 104 | this.ite = stream[Symbol.asyncIterator]() 105 | } 106 | this.ite.next().then(({ value: node, done }) => { 107 | if (done) return cb(null) 108 | if (!node || !node.value) return this._next(cb) 109 | // TODO: Better buffer conversion for key? 110 | return cb(null, 111 | !this._keyAsBuffer ? node.key : Buffer.from(node.key), 112 | !this._valueAsBuffer ? node.value.toString('utf-8') : Buffer.from(node.value)) 113 | }, err => cb(err)) 114 | } 115 | } 116 | --------------------------------------------------------------------------------