levelplane
v0.1.1
Published
Tiny isomorphic LevelDB wrapper with sublevels, msgpack encoding, async iterators, and browser-ready builds
Maintainers
Readme
levelplane
Tiny isomorphic LevelDB wrapper with sublevels, msgpack encoding, async iterators, and browser-ready builds.
levelplane is a small convenience layer over level that provides the same basic database pattern in Node and the browser. In Node it stores data in a filesystem-backed Level database. In the browser it uses the browser-backed Level implementation through the package/browser build path.
Install
npm i levelplaneRuntime usage supports Node 16 or newer.
Node usage
const Levelplane = require('levelplane')
async function main() {
const db = await Levelplane.init({ dir: '.data', name: 'main' })
await db.put('hello', { text: 'world' })
console.log(await db.get('hello'))
await db.close()
}
main().catch(console.error)In Node, a string argument is treated as the filesystem database location.
const db = await Levelplane.init('.data/main')Browser usage with a bundler
const Levelplane = require('levelplane/browser')
async function main() {
const db = await Levelplane.init('my-browser-db')
await db.put('hello', { text: 'world' })
console.log(await db.get('hello'))
}
main().catch(console.error)In the browser, a string argument is treated as the browser database name.
Browser usage from CDN
The browser bundle can be loaded from UNPKG or jsDelivr.
<script src="https://unpkg.com/levelplane"></script>
<script>
async function main() {
const db = await Levelplane.init('my-browser-db')
await db.put('hello', { text: 'world' })
console.log(await db.get('hello'))
}
main().catch(console.error)
</script>The unpkg and jsdelivr package fields point to dist/levelplane.min.js.
Examples
node examples/node.jsexamples/browser.html loads ../dist/levelplane.js, so run npm run build before opening it locally.
Options
const db = await Levelplane.init({
dir: '.data',
name: 'main',
reset: false,
keyEncoding: 'utf8',
valueEncoding: 'msgpack',
cache: false,
createIfMissing: true,
errorIfExists: false
})Core options
dir is the parent directory for Node database files. It defaults to .data.
name is the database name. In Node, it is joined with dir when location is not provided. In the browser, it is the database name.
location is an explicit database location. In Node, it is resolved as a filesystem path. In the browser, it is used as the database name.
reset clears the database during Levelplane.init().
keyEncoding defaults to utf8.
valueEncoding defaults to msgpack, backed by notepack.io.
cache enables cached sublevel instances for repeated sub() calls with the same arguments.
createIfMissing and errorIfExists are passed through to the underlying Level database.
Basic API
await db.put('a', 1)
await db.get('a')
await db.has('a')
await db.del('a')
await db.clear()
await db.close()Missing keys return null instead of throwing.
put(), del(), clear(), and close() return the database instance where appropriate so they are easy to use in small flows.
Sublevels
const users = db.sub('users')
const posts = db.sub('posts')
await users.put('a', { name: 'Aaron' })
await posts.put('a', { title: 'Hello' })
console.log(await users.get('a'))
console.log(await posts.get('a'))Sublevels are isolated from each other and from the root database.
Nested sublevels can be created with strings or arrays.
const one = db.sub('app', 'settings')
const two = db.sub(['app', 'settings'])Cached sublevels can be enabled per call.
const one = db.sub('app', 'settings', { cache: true })
const two = db.sub(['app', 'settings'], { cache: true })
console.log(one === two)Iteration and collection
await db.put('a', 1)
await db.put('b', 2)
console.log(await db.entries())
console.log(await db.keys())
console.log(await db.values())entries() returns pairs:
[
['a', 1],
['b', 2]
]collect() is a small convenience wrapper.
await db.collect()
await db.collect('entries')
await db.collect('keys')
await db.collect('values')
await db.collect('records')First and last helpers
await db.first()
await db.last()
await db.firstKey()
await db.lastKey()These use the underlying iterator order and return null when no matching item exists.
Async iterable helpers
iterator(), pairIterator(), entryIterator(), keyIterator(), and valueIterator() return async iterables with a few small chain helpers.
const items = await db.iterator()
.filter(([key]) => key !== 'b')
.map(([key, value]) => [key.toUpperCase(), value * 10])
.take(2)
.toArray()const total = await db.valueIterator()
.reduce((sum, value) => sum + value, 0)Supported helpers:
map(fn) maps each item.
filter(fn) keeps matching items.
take(n) stops after n items.
reduce(fn, initial) reduces the iterable.
toArray() collects the iterable into an array.
Batch writes
Array batch:
await db.batch([
{ type: 'put', key: 'a', value: 1 },
{ type: 'put', key: 'b', value: 2 },
{ type: 'del', key: 'c' }
])Chain batch:
await db.batch()
.put('a', 1)
.put('b', 2)
.del('c')
.write()Events
levelplane exposes small write events for convenience.
db.on('put', event => console.log(event))
db.on('del', event => console.log(event))
db.once('batch', event => console.log(event))
await db.put('a', 1)
await db.del('a')
await db.batch([{ type: 'put', key: 'b', value: 2 }])Write events are emitted after the underlying write succeeds.
Transform hooks
Transform hooks make it possible to wrap keys and values without changing calling code. This can be used for encoding, encryption, namespacing, or custom read/write behavior.
const coded = db.sub('coded', {
writeKey: key => `key:${key}`,
readKey: key => key.replace(/^key:/, ''),
write: value => ({ wrapped: value }),
read: value => value.wrapped
})
await coded.put('a', { ok: true })
console.log(await coded.get('a'))
console.log(await coded.keys())Hooks:
writeKey(key) runs before keys are written or queried.
readKey(key) runs when keys are read from iterators.
write(value, key) runs before values are stored.
read(value, key) runs after values are loaded.
Utility methods
await db.length()
await db.size()
db.debug()
db.toString()length() counts keys by iterating.
size() returns an approximate logical size summary based on iterated keys and decoded values.
debug() returns the current location, sublevel path, status, and encoding options.
toString() returns a readable label such as [Levelplane] or [Levelplane:users].
Package entry points
Default Node entry:
const Levelplane = require('levelplane')Browser-safe entry:
const Levelplane = require('levelplane/browser')Explicit Node entry:
const Levelplane = require('levelplane/node')The shared browser-safe core lives in levelplane.js. The Node entry lives in levelplane-node.js and injects the Node filesystem/path platform adapter. browser.js and node.js are small compatibility shims for older bundlers that do not read package exports.
Scripts
npm test
npm run build
npm run pack:dry
npm run checknpm test runs the Mocha test suite.
npm run build creates dist/levelplane.js with Rollup and dist/levelplane.min.js with Terser.
npm run pack:dry inspects the current package contents with npm pack --dry-run.
npm run check runs tests, builds the browser bundles, and dry-runs the package contents.
npm run prepack runs tests and builds before packing or publishing.
The package runtime supports Node 16+. The Rollup 4 browser build toolchain requires Node 18+.
License
MIT
