From 3506602f775041fd8928274bd22ad8ce330e7d65 Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Sun, 30 Dec 2018 13:53:37 +0100 Subject: [PATCH 1/5] Integrate level-js for browser support --- README.md | 40 ++++++++++++++++++++++--------- browser.js | 1 + package.json | 6 +++++ test-browser.js | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 browser.js create mode 100644 test-browser.js diff --git a/README.md b/README.md index 5217757..05a7327 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # level -> Fast & simple storage. A Node.js-style `LevelDB` wrapper. +> Fast & simple storage. A Node.js-style `LevelDB` wrapper for Node.js, Electron and browsers. [![level badge][level-badge]](https://github.com/level/awesome) [![Backers on Open Collective](https://opencollective.com/level/backers/badge.svg)](#backers) @@ -14,19 +14,14 @@ [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) [![npm](https://img.shields.io/npm/dm/level.svg)](https://www.npmjs.com/package/level) -A convenience package that: +## Table of Contents -* exports a function that returns a [`levelup instance`](https://github.com/level/levelup#ctor) when invoked -* bundles the current release of [`levelup`][levelup] and [`leveldown`][leveldown] -* leverages encodings using [`encoding-down`][encoding-down] - -Use this package to avoid having to explicitly install `leveldown` when you just want plain old `LevelDB` from `levelup`. - -**Note** that `level` only supports Node.js. There is ongoing work to make it work in both Node.js and in the browser. In the meantime, we suggest you to use [`level-browserify`](https://github.com/level/level-browserify). - -**If you are upgrading:** please see [`UPGRADING.md`](UPGRADING.md). +
+Click to expand +* [Introduction](#introduction) * [Usage](#usage) +* [Supported Platforms](#supported-platforms) * [API](#api) * [Promise Support](#promise-support) * [Events](#events) @@ -36,8 +31,22 @@ Use this package to avoid having to explicitly install `leveldown` when you just * [Sponsors](#sponsors) * [License](#license) +
+ +## Introduction + +This is a convenience package that: + +* exports a function that returns a [`levelup`](https://github.com/level/levelup#ctor) instance when invoked +* bundles the current release of [`leveldown`][leveldown] and [`level-js`][level-js] +* leverages encodings using [`encoding-down`][encoding-down]. + +Use this package to avoid having to explicitly install `leveldown` and/or `level-js` when you just want to use `levelup`. It uses `leveldown` in Node.js or Electron and `level-js` in browsers (when bundled by [`browserify`](https://github.com/browserify/browserify) or similar). + ## Usage +**If you are upgrading:** please see [`UPGRADING.md`](UPGRADING.md). + ```js var level = require('level') @@ -59,6 +68,14 @@ db.put('name', 'Level', function (err) { }) ``` +## Supported Platforms + +At the time of writing, `level` works in Node.js 8+ and Electron 3+ on Linux, Mac OS, Windows and FreeBSD, including any future Node.js and Electron release thanks to [N-API](https://nodejs.org/api/n-api.html), including ARM platforms like Raspberry Pi and Android, as well as in Chrome, Firefox, IE 11, Edge, Safari, iPhone and Chrome for Android. For details, see [Supported Platforms](https://github.com/Level/leveldown#supported-platforms) of `leveldown` and [Browser Support](https://github.com/Level/level-js#browser-support) of `level-js`. + +Though `leveldown` and `level-js` have subtle differences in type handling, `level` negates this by transparently wrapping both with [`encoding-down`](https://github.com/Level/encoding-down). If you want to support all possible runtime environments, stick to string keys with encodings like the default `utf8`. Binary values are supported across the board; in browsers that support [IndexedDB Second Edition](https://www.w3.org/TR/IndexedDB-2/) (like Chrome and Firefox) you can also use binary keys. + +If you want to use [Promises](#promise-support), you will need a polyfill like [`pinkie`](https://github.com/floatdrop/pinkie) in older browsers like IE. + ## API * [level()](#ctor) @@ -454,4 +471,5 @@ Copyright (c) 2012-present `level` [contributors](https://github.com/level/commu [level-badge]: http://leveldb.org/img/badge.svg [levelup]: https://github.com/level/levelup [leveldown]: https://github.com/level/leveldown +[level-js]: https://github.com/level/level-js [encoding-down]: https://github.com/level/encoding-down diff --git a/browser.js b/browser.js new file mode 100644 index 0000000..5974f4d --- /dev/null +++ b/browser.js @@ -0,0 +1 @@ +module.exports = require('level-packager')(require('level-js')) diff --git a/package.json b/package.json index be14d98..be6b154 100644 --- a/package.json +++ b/package.json @@ -17,20 +17,26 @@ "json" ], "main": "level.js", + "browser": "browser.js", "dependencies": { + "level-js": "^4.0.0", "level-packager": "^5.0.0", "leveldown": "^5.0.0-1", "opencollective-postinstall": "^2.0.0" }, "devDependencies": { + "airtap": "^2.0.1", "coveralls": "^3.0.2", "nyc": "^12.0.2", + "pinkie": "^2.0.4", "standard": "^12.0.0", "tape": "^4.2.2", + "uuid": "^3.3.2", "verify-travis-appveyor": "^3.0.0" }, "scripts": { "test": "standard && nyc node test.js && verify-travis-appveyor", + "test-browser-local": "airtap --local test-browser.js", "coverage": "nyc report --reporter=text-lcov | coveralls", "postinstall": "opencollective-postinstall || exit 0" }, diff --git a/test-browser.js b/test-browser.js new file mode 100644 index 0000000..dd281b9 --- /dev/null +++ b/test-browser.js @@ -0,0 +1,64 @@ +'use strict' + +// Promise polyfill for IE and others. +if (typeof Promise !== 'function') { + global.Promise = require('pinkie') +} + +var test = require('tape') +var uuid = require('uuid/v4') +var level = require('.') + +require('level-packager/abstract/base-test')(test, level) +require('level-packager/abstract/db-values-test')(test, level) + +function factory (opts) { + return level(uuid(), opts) +} + +test('level put', function (t) { + t.plan(4) + + var db = factory() + + db.put('name', 'level', function (err) { + t.ifError(err, 'no put error') + + db.get('name', function (err, value) { + t.ifError(err, 'no get error') + t.is(value, 'level') + + db.close(function (err) { + t.ifError(err, 'no close error') + }) + }) + }) +}) + +test('level binary value', function (t) { + t.plan(9) + + var db = factory({ valueEncoding: 'binary' }) + var buf = Buffer.from('00ff', 'hex') + + db.put('binary', buf, function (err) { + t.ifError(err, 'no put error') + + db.get('binary', function (err, value) { + t.ifError(err, 'no get error') + t.ok(Buffer.isBuffer(value), 'is a buffer') + t.same(value, buf) + + db.get('binary', { valueEncoding: 'id' }, function (err, value) { + t.ifError(err, 'no get error') + t.notOk(Buffer.isBuffer(value), 'is not a buffer') + t.ok(value instanceof Uint8Array, 'is a Uint8Array') + t.same(Buffer.from(value), buf) + + db.close(function (err) { + t.ifError(err, 'no close error') + }) + }) + }) + }) +}) From e2d90a6ccadc6fc58f4902584dc1797e92bad814 Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Sun, 30 Dec 2018 17:26:49 +0100 Subject: [PATCH 2/5] Tweak README --- README.md | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 05a7327..7cb6602 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ If you want to use [Promises](#promise-support), you will need a polyfill like [ ## API +For options specific to [`leveldown`][leveldown] and [`level-js`][level-js] ("underlying store" from here on out), please see their respective READMEs. + * [level()](#ctor) * [db.open()](#open) * [db.close()](#close) @@ -92,13 +94,11 @@ If you want to use [Promises](#promise-support), you will need a polyfill like [ * [db.createKeyStream()](#createKeyStream) * [db.createValueStream()](#createValueStream) -See [`levelup`][levelup] and [`leveldown`][leveldown] for more details. - ### `db = level(location[, options[, callback]])` The main entry point for creating a new `levelup` instance. -- `location` path to the underlying `LevelDB`. +- `location` is a string pointing to the LevelDB location to be opened or in browsers, the name of the [`IDBDatabase`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase) to be opened. - `options` is passed on to the underlying store. - `options.keyEncoding` and `options.valueEncoding` are passed to [`encoding-down`][encoding-down], default encoding is `'utf8'` @@ -139,6 +139,8 @@ level('./db', { createIfMissing: false }, function (err, db) { }) ``` +Note that `createIfMissing` is an option specific to [`leveldown`][leveldown]. + ### `db.open([callback])` Opens the underlying store. In general you should never need to call this method directly as it's automatically called by levelup(). @@ -159,7 +161,8 @@ If no callback is passed, a promise is returned. ### `db.put(key, value[, options][, callback])` put() is the primary method for inserting data into the store. Both `key` and `value` can be of any type as far as `levelup` is concerned. -`options` is passed on to the underlying store. +- `options` is passed on to the underlying store +- `options.keyEncoding` and `options.valueEncoding` are passed to [`encoding-down`][encoding-down], allowing you to override the key- and/or value encoding for this `put` operation. If no callback is passed, a promise is returned. @@ -182,7 +185,8 @@ db.get('foo', function (err, value) { }) ``` -`options` is passed on to the underlying store. +- `options` is passed on to the underlying store. +- `options.keyEncoding` and `options.valueEncoding` are passed to [`encoding-down`][encoding-down], allowing you to override the key- and/or value encoding for this `get` operation. If no callback is passed, a promise is returned. @@ -196,7 +200,8 @@ db.del('foo', function (err) { }); ``` -`options` is passed on to the underlying store. +- `options` is passed on to the underlying store. +- `options.keyEncoding` is passed to [`encoding-down`][encoding-down], allowing you to override the key encoding for this `del` operation. If no callback is passed, a promise is returned. @@ -223,7 +228,8 @@ db.batch(ops, function (err) { }) ``` -`options` is passed on to the underlying store. +- `options` is passed on to the underlying store. +- `options.keyEncoding` and `options.valueEncoding` are passed to [`encoding-down`][encoding-down], allowing you to override the key- and/or value encoding of operations in this batch. If no callback is passed, a promise is returned. @@ -261,10 +267,13 @@ Clear all queued operations on the current batch, any previous operations will b The number of queued operations on the current batch. -batch.write([callback]) +batch.write([options][, callback]) Commit the queued operations for this batch. All operations not *cleared* will be written to the underlying store atomically, that is, they will either all succeed or fail with no partial commits. +- `options` is passed on to the underlying store. +- `options.keyEncoding` and `options.valueEncoding` are not supported here. + If no callback is passed, a promise is returned. @@ -283,8 +292,6 @@ A `levelup` instance can be in one of the following states: ### `db.isClosed()` -*See isOpen()* - `isClosed()` will return `true` only when the state is "closing" *or* "closed", it can be useful for determining if read and write operations are permissible. @@ -328,6 +335,8 @@ Legacy options: * `end`: instead use `lte` +Underlying stores may have additional options. + ### `db.createKeyStream([options])` @@ -370,7 +379,7 @@ db.createReadStream({ keys: false, values: true }) ## Promise Support -`level` ships with native `Promise` support out of the box. +`level(up)` ships with native `Promise` support out of the box. Each function taking a callback also can be used as a promise, if the callback is omitted. This applies for: From 56f41ca9edaa2832e99f0004a2a6a6f2a187be73 Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Sun, 30 Dec 2018 17:33:10 +0100 Subject: [PATCH 3/5] underlying LevelDB store -> underlying store --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cb6602..6c747fc 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Use this package to avoid having to explicitly install `leveldown` and/or `level var level = require('level') // 1) Create our database, supply location and options. -// This will create or open the underlying LevelDB store. +// This will create or open the underlying store. var db = level('./mydb') // 2) Put a key & value From 1c8c1e01ca35c9e20e75b8e0aa0f7770c4b9aeec Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Sun, 30 Dec 2018 17:37:17 +0100 Subject: [PATCH 4/5] remove 'type defaults to put' (not true since levelup@3) --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 6c747fc..9efcce8 100644 --- a/README.md +++ b/README.md @@ -211,8 +211,6 @@ If no callback is passed, a promise is returned. Each operation is contained in an object having the following properties: `type`, `key`, `value`, where the *type* is either `'put'` or `'del'`. In the case of `'del'` the `value` property is ignored. Any entries with a `key` of `null` or `undefined` will cause an error to be returned on the `callback` and any `type: 'put'` entry with a `value` of `null` or `undefined` will return an error. -If `key` and `value` are defined but `type` is not, it will default to `'put'`. - ```js var ops = [ { type: 'del', key: 'father' }, From cb73c7589de48876e07109b012732ac9bdd598c3 Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Mon, 31 Dec 2018 12:50:29 +0100 Subject: [PATCH 5/5] ./my-db -> my-db --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9efcce8..75e91a4 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ var level = require('level') // 1) Create our database, supply location and options. // This will create or open the underlying store. -var db = level('./mydb') +var db = level('my-db') // 2) Put a key & value db.put('name', 'Level', function (err) { @@ -102,7 +102,7 @@ The main entry point for creating a new `levelup` instance. - `options` is passed on to the underlying store. - `options.keyEncoding` and `options.valueEncoding` are passed to [`encoding-down`][encoding-down], default encoding is `'utf8'` -Calling `level('./db')` will also open the underlying store. This is an asynchronous operation which will trigger your callback if you provide one. The callback should take the form `function (err, db) {}` where `db` is the `levelup` instance. If you don't provide a callback, any read & write operations are simply queued internally until the store is fully opened. +Calling `level('my-db')` will also open the underlying store. This is an asynchronous operation which will trigger your callback if you provide one. The callback should take the form `function (err, db) {}` where `db` is the `levelup` instance. If you don't provide a callback, any read & write operations are simply queued internally until the store is fully opened. This leads to two alternative ways of managing a `levelup` instance: @@ -132,7 +132,7 @@ db.get('foo', function (err, value) { The constructor function has a `.errors` property which provides access to the different error types from [`level-errors`](https://github.com/Level/errors#api). See example below on how to use it: ```js -level('./db', { createIfMissing: false }, function (err, db) { +level('my-db', { createIfMissing: false }, function (err, db) { if (err instanceof level.errors.OpenError) { console.log('failed to open database') } @@ -392,7 +392,7 @@ The only exception is the `level` constructor itself, which if no callback is pa Example: ```js -var db = level('./my-db') +var db = level('my-db') db.put('foo', 'bar') .then(function () { return db.get('foo') }) @@ -404,7 +404,7 @@ Or using `async/await`: ```js var main = async () => { - var db = level('./my-db') + var db = level('my-db') await db.put('foo', 'bar') console.log(await db.get('foo'))