diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 92df3c5..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index be6de95..c1f7def 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,5 @@ node_modules lib dist test/test-repo/datastore +init-default +datastore-test \ No newline at end of file diff --git a/README.md b/README.md index 16884e5..4940ea7 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ - [Install](#install) - [Usage](#usage) + - [Browser Shimming Leveldown](#browser-shimming-leveldown) - [Contribute](#contribute) - [License](#license) @@ -39,6 +40,9 @@ const browserStore = new LevelStore('my/db/name', {db: require('level-js')}) const memStore = new LevelStore('my/mem/store', {db: require('memdown')}) ``` +### Browser Shimming Leveldown +As `leveldown` does not work in the browser, `LevelStore` takes advantage of the browser property in package.json to shim `level-js` in its place. Most modern bundlers such as webpack, will see the shim and replace it for use in the browser. If you are using a bundler that does not support pkg.browser, you will need to handle the shimming yourself, as was the case with versions of `LevelStore` 0.7.0 and earlier. + ## Contribute PRs accepted. diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile index a7da2e5..6f4483f 100644 --- a/ci/Jenkinsfile +++ b/ci/Jenkinsfile @@ -1,2 +1,2 @@ // Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories. -javascript() +javascript(['nodejs_versions': ['8.11.1','9.2.0','10.0.0']]) diff --git a/package.json b/package.json index 80c7ae8..c6b7f01 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "version": "0.7.0", "description": "Datastore implementation with level(up|down) backend", "main": "src/index.js", + "browser": { + "leveldown": "level-js" + }, "scripts": { "lint": "aegir lint", "build": "aegir build", @@ -38,17 +41,18 @@ "homepage": "https://github.com/ipfs/js-datastore-level#readme", "dependencies": { "datastore-core": "~0.4.0", + "encoding-down": "^5.0.2", "interface-datastore": "~0.4.1", - "level-js": "^2.2.4", - "leveldown": "^1.9.0", - "levelup": "^1.3.9", + "level-js": "github:timkuijsten/level.js#idbunwrapper", + "leveldown": "^3.0.2", + "levelup": "^2.0.2", "pull-stream": "^3.6.1" }, "devDependencies": { - "aegir": "^12.1.3", - "async": "^2.5.0", + "aegir": "^13.1.0", + "async": "^2.6.0", "chai": "^4.1.2", - "cids": "~0.5.2", + "cids": "~0.5.3", "dirty-chai": "^2.0.1", "flow-bin": "^0.58.0", "memdown": "^1.4.1", diff --git a/src/index.js b/src/index.js index ece43a3..19a823e 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,7 @@ const levelup = require('levelup') const asyncFilter = require('interface-datastore').utils.asyncFilter const asyncSort = require('interface-datastore').utils.asyncSort const Key = require('interface-datastore').Key +const encode = require('encoding-down') /** * A datastore backed by leveldb. @@ -24,10 +25,28 @@ class LevelDatastore { /* :: db: levelup */ constructor (path /* : string */, opts /* : ?LevelOptions */) { - this.db = levelup(path, Object.assign({}, opts, { - compression: false, // same default as go - valueEncoding: 'binary' - })) + let database + + if (opts && opts.db) { + database = opts.db + delete opts.db + } else { + // Default to leveldown db + database = require('leveldown') + } + + this.db = levelup( + encode(database(path), { valueEncoding: 'binary' }), + Object.assign({}, opts, { + compression: false // same default as go + }), + (err) => { + // Prevent an uncaught exception error on duplicate locks + if (err) { + throw err + } + } + ) } open (callback /* : Callback */) /* : void */ { @@ -58,7 +77,10 @@ class LevelDatastore { } delete (key /* : Key */, callback /* : Callback */) /* : void */ { - this.db.del(key.toString(), callback) + this.db.del(key.toString(), (err) => { + // Avoid level passing additional arguments to callback, we dont need them + callback(err) + }) } close (callback /* : Callback */) /* : void */ { diff --git a/test/browser.js b/test/browser.js index 4dab658..c049d3f 100644 --- a/test/browser.js +++ b/test/browser.js @@ -5,7 +5,9 @@ const each = require('async/each') const MountStore = require('datastore-core').MountDatastore const Key = require('interface-datastore').Key -const leveljs = require('level-js') + +// leveldown will be swapped for level-js +const leveljs = require('leveldown') const LevelStore = require('../src') diff --git a/test/index.spec.js b/test/index.spec.js index ec559a0..fdd62ef 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -2,20 +2,69 @@ /* eslint-env mocha */ 'use strict' +const chai = require('chai') +chai.use(require('dirty-chai')) +const expect = chai.expect const memdown = require('memdown') +const LevelDown = require('leveldown') +const eachSeries = require('async/eachSeries') const LevelStore = require('../src') describe('LevelDatastore', () => { - describe('interface-datastore (memdown)', () => { - require('interface-datastore/src/tests')({ - setup (callback) { - callback(null, new LevelStore('hello', {db: memdown})) - }, - teardown (callback) { - memdown.clearGlobalStore() - callback() - } + describe('initialization', () => { + it('should default to a leveldown database', (done) => { + const levelStore = new LevelStore('init-default') + + levelStore.open((err) => { + expect(err).to.not.exist() + expect(levelStore.db.db.db instanceof LevelDown).to.equal(true) + expect(levelStore.db.options).to.include({ + createIfMissing: true, + errorIfExists: false + }) + expect(levelStore.db.db.codec.opts).to.include({ + valueEncoding: 'binary' + }) + done() + }) + }) + + it('should be able to override the database', (done) => { + const levelStore = new LevelStore('init-default', { + db: memdown, + createIfMissing: true, + errorIfExists: true + }) + + levelStore.open((err) => { + expect(err).to.not.exist() + expect(levelStore.db.db.db instanceof memdown).to.equal(true) + expect(levelStore.db.options).to.include({ + createIfMissing: true, + errorIfExists: true + }) + done() + }) + }) + }) + + eachSeries([ + memdown, + LevelDown + ], (database) => { + describe(`interface-datastore ${database.name}`, () => { + require('interface-datastore/src/tests')({ + setup (callback) { + callback(null, new LevelStore('datastore-test', {db: database})) + }, + teardown (callback) { + memdown.clearGlobalStore() + callback() + } + }) }) + }, (err) => { + expect(err).to.not.exist() }) }) diff --git a/test/node.js b/test/node.js index c37d968..b6f8b02 100644 --- a/test/node.js +++ b/test/node.js @@ -21,7 +21,9 @@ describe('LevelDatastore', () => { const dir = utils.tmpdir() require('interface-datastore/src/tests')({ setup (callback) { - callback(null, new LevelStore(dir)) + callback(null, new LevelStore(dir, { + db: require('leveldown') + })) }, teardown (callback) { rimraf(dir, callback) @@ -40,13 +42,19 @@ describe('LevelDatastore', () => { setup (callback) { callback(null, new MountStore([{ prefix: new Key('/a'), - datastore: new LevelStore(dirs[0]) + datastore: new LevelStore(dirs[0], { + db: require('leveldown') + }) }, { prefix: new Key('/q'), - datastore: new LevelStore(dirs[1]) + datastore: new LevelStore(dirs[1], { + db: require('leveldown') + }) }, { prefix: new Key('/z'), - datastore: new LevelStore(dirs[2]) + datastore: new LevelStore(dirs[2], { + db: require('leveldown') + }) }])) }, teardown (callback) { @@ -56,7 +64,9 @@ describe('LevelDatastore', () => { }) it.skip('interop with go', (done) => { - const store = new LevelStore(path.join(__dirname, 'test-repo', 'datastore')) + const store = new LevelStore(path.join(__dirname, 'test-repo', 'datastore'), { + db: require('leveldown') + }) pull( store.query({}),