or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-level

Universal abstract-level database for Node.js and browsers

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/level@10.0.x

To install, run

npx @tessl/cli install tessl/npm-level@10.0.0

index.mddocs/

Level

Level is a universal database abstraction layer that automatically selects the appropriate implementation based on the runtime environment. It provides a consistent API for creating lexicographically sorted key-value databases that work seamlessly across Node.js (using LevelDB) and browsers (using IndexedDB).

Package Information

  • Package Name: level
  • Package Type: npm
  • Language: JavaScript with TypeScript definitions
  • Installation: npm install level

Core Imports

const { Level } = require('level');

For ES modules:

import { Level } from 'level';

TypeScript with generics:

import { Level } from 'level';

Basic Usage

const { Level } = require('level');

// Create a database
const db = new Level('example', { valueEncoding: 'json' });

// Add an entry with key 'a' and value 1
await db.put('a', 1);

// Add multiple entries
await db.batch([{ type: 'put', key: 'b', value: 2 }]);

// Get value of key 'a': 1
const value = await db.get('a');

// Iterate entries with keys that are greater than 'a'
for await (const [key, value] of db.iterator({ gt: 'a' })) {
  console.log(value); // 2
}

TypeScript usage with generic type parameters:

import { Level } from 'level';

// Specify types of keys and values (any, in the case of json).
// The generic type parameters default to Level<string, string>.
const db = new Level<string, any>('./db', { valueEncoding: 'json' });

// All relevant methods then use those types
await db.put('a', { x: 123 });

// Specify different types when overriding encoding per operation
await db.get<string, string>('a', { valueEncoding: 'utf8' });

// It works the same for sublevels
const abc = db.sublevel('abc');
const xyz = db.sublevel<string, any>('xyz', { valueEncoding: 'json' });

Architecture

Level implements a unified interface that automatically chooses the appropriate backend:

  • Node.js Runtime: Uses classic-level which provides LevelDB-based persistent storage
  • Browser Runtime: Uses browser-level which provides IndexedDB-based storage
  • Unified API: Both implementations extend abstract-level providing consistent methods
  • Type Safety: Full TypeScript support with generic type parameters for keys and values
  • Encoding Support: Multiple encoding options including JSON, UTF-8, and binary formats

The Level class extends AbstractLevel<string | Buffer | Uint8Array, KDefault, VDefault> and provides all the functionality from the abstract-level specification.

Capabilities

Database Constructor

Creates a new database instance or opens an existing one.

/**
 * Database constructor.
 * @param location Directory path (relative or absolute) where LevelDB will store its
 * files, or in browsers, the name of the IDBDatabase to be opened.
 * @param options Options, of which some will be forwarded to open.
 */
constructor(location: string, options?: DatabaseOptions<KDefault, VDefault>);

type DatabaseOptions<K, V> = {
  // Encoding options
  keyEncoding?: string;
  valueEncoding?: string;
  
  // Database-specific options (varies by implementation)
  createIfMissing?: boolean;
  errorIfExists?: boolean;
  compression?: boolean;
  cacheSize?: number;
  
  // Additional options from classic-level and browser-level
  [key: string]: any;
};

Database Properties

Access to database metadata and configuration.

/**
 * Location that was passed to the constructor.
 */
get location(): string;

Database Lifecycle

Control database opening and closing.

/**
 * Open the database. Called automatically by other methods if not already open.
 */
open(): Promise<void>;
open(options: OpenOptions): Promise<void>;

/**
 * Close the database. This is an async operation.
 */
close(): Promise<void>;

/**
 * Database status property.
 */
get status(): string;

type OpenOptions = {
  createIfMissing?: boolean;
  errorIfExists?: boolean;
  [key: string]: any;
};

Single Value Operations

Store, retrieve, and delete individual key-value pairs.

/**
 * Get the value of a key from the database.
 * @param key The key to retrieve
 * @returns Promise resolving to the value
 */
get(key: KDefault): Promise<VDefault>;
get<K = KDefault, V = VDefault>(key: K, options: GetOptions<K, V>): Promise<V>;

/**
 * Check if a key exists in the database.
 * @param key The key to check
 * @returns Promise resolving to boolean indicating existence
 */
has(key: KDefault): Promise<boolean>;
has<K = KDefault>(key: K, options: HasOptions<K>): Promise<boolean>;

/**
 * Put a key-value pair to the database.
 * @param key The key to store
 * @param value The value to store
 */
put(key: KDefault, value: VDefault): Promise<void>;
put<K = KDefault, V = VDefault>(key: K, value: V, options: PutOptions<K, V>): Promise<void>;

/**
 * Delete a key-value pair from the database.
 * @param key The key to delete
 */
del(key: KDefault): Promise<void>;
del<K = KDefault>(key: K, options: DelOptions<K>): Promise<void>;

type GetOptions<K, V> = {
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

type HasOptions<K> = {
  keyEncoding?: string;
  [key: string]: any;
};

type PutOptions<K, V> = {
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

type DelOptions<K> = {
  keyEncoding?: string;
  [key: string]: any;
};

Batch Operations

Retrieve multiple values or perform multiple operations atomically.

/**
 * Get the values of multiple keys from the database.
 * @param keys Array of keys to retrieve
 * @returns Promise resolving to array of values
 */
getMany(keys: KDefault[]): Promise<VDefault[]>;
getMany<K = KDefault, V = VDefault>(keys: K[], options: GetManyOptions<K, V>): Promise<V[]>;

/**
 * Check if multiple keys exist in the database.
 * @param keys Array of keys to check
 * @returns Promise resolving to array of booleans indicating existence
 */
hasMany(keys: KDefault[]): Promise<boolean[]>;
hasMany<K = KDefault>(keys: K[], options: HasManyOptions<K>): Promise<boolean[]>;

/**
 * Perform multiple operations atomically.
 * @param operations Array of operations to perform
 */
batch(operations: Array<BatchOperation<typeof this, KDefault, VDefault>>): Promise<void>;
batch<K = KDefault, V = VDefault>(operations: Array<BatchOperation<typeof this, K, V>>, options: BatchOptions<K, V>): Promise<void>;

/**
 * Create a chained batch for building multiple operations.
 * @returns ChainedBatch instance for adding operations
 */
batch(): ChainedBatch<typeof this, KDefault, VDefault>;

type GetManyOptions<K, V> = {
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

type HasManyOptions<K> = {
  keyEncoding?: string;
  [key: string]: any;
};

type BatchOptions<K, V> = {
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

type BatchOperation<TDatabase, K, V> = {
  type: 'put' | 'del';
  key: K;
  value?: V; // Required for 'put', ignored for 'del'
};

interface ChainedBatch<TDatabase, K, V> {
  put(key: K, value: V): ChainedBatch<TDatabase, K, V>;
  del(key: K): ChainedBatch<TDatabase, K, V>;
  clear(): ChainedBatch<TDatabase, K, V>;
  write(): Promise<void>;
  close(): Promise<void>;
}

Sublevel Operations

Create isolated database sections that share the same underlying storage but have separate key spaces.

/**
 * Create a sublevel that acts like a separate database with its own key space.
 * @param name Sublevel name/prefix
 * @param options Optional configuration for the sublevel
 * @returns New Level instance scoped to the sublevel
 */
sublevel<SK = KDefault, SV = VDefault>(name: string, options?: DatabaseOptions<SK, SV>): Level<SK, SV>;

Iterator Operations

Create iterators for traversing database entries with range queries and filtering.

/**
 * Create an iterator for key-value pairs.
 * @param options Iterator configuration options
 * @returns Iterator instance
 */
iterator(): Iterator<typeof this, KDefault, VDefault>;
iterator<K = KDefault, V = VDefault>(options: IteratorOptions<K, V>): Iterator<typeof this, K, V>;

/**
 * Create an iterator for keys only.
 * @param options Iterator configuration options
 * @returns KeyIterator instance
 */
keys(): KeyIterator<typeof this, KDefault>;
keys<K = KDefault>(options: KeyIteratorOptions<K>): KeyIterator<typeof this, K>;

/**
 * Create an iterator for values only.
 * @param options Iterator configuration options
 * @returns ValueIterator instance
 */
values(): ValueIterator<typeof this, KDefault, VDefault>;
values<K = KDefault, V = VDefault>(options: ValueIteratorOptions<K, V>): ValueIterator<typeof this, K, V>;

type IteratorOptions<K, V> = {
  gt?: K;        // Greater than
  gte?: K;       // Greater than or equal
  lt?: K;        // Less than
  lte?: K;       // Less than or equal
  reverse?: boolean;
  limit?: number;
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

type KeyIteratorOptions<K> = {
  gt?: K;
  gte?: K;
  lt?: K;
  lte?: K;
  reverse?: boolean;
  limit?: number;
  keyEncoding?: string;
  [key: string]: any;
};

type ValueIteratorOptions<K, V> = {
  gt?: K;
  gte?: K;
  lt?: K;
  lte?: K;
  reverse?: boolean;
  limit?: number;
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

interface Iterator<TDatabase, K, V> {
  next(): Promise<[K, V] | undefined>;
  all(): Promise<Array<[K, V]>>;
  close(): Promise<void>;
  [Symbol.asyncIterator](): AsyncIterableIterator<[K, V]>;
}

interface KeyIterator<TDatabase, K> {
  next(): Promise<K | undefined>;
  all(): Promise<K[]>;
  close(): Promise<void>;
  [Symbol.asyncIterator](): AsyncIterableIterator<K>;
}

interface ValueIterator<TDatabase, K, V> {
  next(): Promise<V | undefined>;
  all(): Promise<V[]>;
  close(): Promise<void>;
  [Symbol.asyncIterator](): AsyncIterableIterator<V>;
}

Clear Operations

Delete all entries or a range of entries from the database.

/**
 * Delete all entries or a range of entries from the database.
 * @param options Range and encoding options
 */
clear(): Promise<void>;
clear<K = KDefault>(options: ClearOptions<K>): Promise<void>;

type ClearOptions<K> = {
  gt?: K;        // Greater than
  gte?: K;       // Greater than or equal
  lt?: K;        // Less than
  lte?: K;       // Less than or equal
  reverse?: boolean;
  limit?: number;
  keyEncoding?: string;
  [key: string]: any;
};

Resource Management

Attach and detach resources that should be managed with the database lifecycle.

/**
 * Attach a resource to be managed by the database lifecycle.
 * @param resource Resource to attach (must have close method)
 */
attachResource(resource: any): void;

/**
 * Detach a previously attached resource.
 * @param resource Resource to detach
 */
detachResource(resource: any): void;

Feature Detection

Access to database capabilities and supported features.

/**
 * Database features and capabilities.
 */
get supports(): {
  [key: string]: boolean;
};

Encoding Utilities

Utility methods for working with encodings.

/**
 * Get or normalize key encoding.
 * @param encoding Encoding name or options
 * @returns Normalized encoding
 */
keyEncoding(): any;
keyEncoding(encoding: string | any): any;

/**
 * Get or normalize value encoding.
 * @param encoding Encoding name or options
 * @returns Normalized encoding
 */
valueEncoding(): any;
valueEncoding(encoding: string | any): any;

Advanced Operations

Advanced database operations and deferred execution.

/**
 * Defer function execution until database is in the correct state.
 * @param fn Function to defer
 * @param options Defer options
 */
defer(fn: () => void): void;
defer(fn: () => void, options: DeferOptions): void;

type DeferOptions = {
  [key: string]: any;
};

Event System

The Level class extends EventEmitter and emits various events during database operations.

/**
 * Add event listener for database events.
 * @param event Event name
 * @param listener Event handler function
 */
on(event: 'open' | 'opening' | 'close' | 'closing' | 'put' | 'del' | 'batch' | 'clear', listener: (...args: any[]) => void): this;

/**
 * Remove event listener.
 * @param event Event name
 * @param listener Event handler function
 */
off(event: 'open' | 'opening' | 'close' | 'closing' | 'put' | 'del' | 'batch' | 'clear', listener: (...args: any[]) => void): this;

/**
 * Add one-time event listener.
 * @param event Event name
 * @param listener Event handler function
 */
once(event: 'open' | 'opening' | 'close' | 'closing' | 'put' | 'del' | 'batch' | 'clear', listener: (...args: any[]) => void): this;

/**
 * Emit an event.
 * @param event Event name
 * @param args Event arguments
 */
emit(event: string, ...args: any[]): boolean;

Usage Examples

JSON Encoding

const db = new Level('my-db', { valueEncoding: 'json' });

await db.put('user:1', { name: 'Alice', age: 30 });
const user = await db.get('user:1'); // { name: 'Alice', age: 30 }

Batch Operations

// Atomic batch operations
await db.batch([
  { type: 'put', key: 'user:1', value: { name: 'Alice' } },
  { type: 'put', key: 'user:2', value: { name: 'Bob' } },
  { type: 'del', key: 'user:3' }
]);

// Chained batch operations
const batch = db.batch();
batch.put('key1', 'value1');
batch.put('key2', 'value2');
batch.del('key3');
await batch.write();

Range Queries

// Iterate over a range of keys
for await (const [key, value] of db.iterator({ gte: 'user:1', lt: 'user:9' })) {
  console.log(`${key}: ${JSON.stringify(value)}`);
}

// Get all keys starting with 'user:'
const userKeys = await db.keys({ gte: 'user:', lt: 'user;\xFF' }).all();

// Get values in reverse order
const recentValues = await db.values({ reverse: true, limit: 10 }).all();

TypeScript with Generics

interface User {
  name: string;
  email: string;
  age: number;
}

// Create typed database
const userDb = new Level<string, User>('users', { valueEncoding: 'json' });

// Type-safe operations
await userDb.put('alice', { name: 'Alice', email: 'alice@example.com', age: 30 });
const alice: User = await userDb.get('alice');

// Override types for specific operations
const rawData = await userDb.get<string, string>('alice', { valueEncoding: 'utf8' });

Sublevel Usage

const db = new Level('my-db', { valueEncoding: 'json' });

// Create sublevels for different data types
const users = db.sublevel('users');
const posts = db.sublevel('posts');

// Each sublevel acts like a separate database
await users.put('alice', { name: 'Alice', age: 30 });
await posts.put('post1', { title: 'Hello World', author: 'alice' });

// TypeScript with sublevel typing
const typedUsers = db.sublevel<string, User>('users', { valueEncoding: 'json' });

Key Existence Checking

// Check if a single key exists
const exists = await db.has('user:1');
console.log(exists); // true or false

// Check multiple keys at once
const keysExist = await db.hasMany(['user:1', 'user:2', 'user:3']);
console.log(keysExist); // [true, false, true]

Clear Operations

// Clear all entries
await db.clear();

// Clear a range of entries
await db.clear({ gte: 'user:', lt: 'user;\xFF' });

// Clear with limit
await db.clear({ limit: 100 });

Event Handling

const db = new Level('my-db', { valueEncoding: 'json' });

// Listen for database events
db.on('open', () => console.log('Database opened'));
db.on('put', (key, value) => console.log(`Put ${key}: ${JSON.stringify(value)}`));
db.on('del', (key) => console.log(`Deleted ${key}`));

// One-time listeners
db.once('close', () => console.log('Database closed'));

// Operations will emit events
await db.put('test', { data: 'example' }); // Emits 'put' event
await db.del('test'); // Emits 'del' event
await db.close(); // Emits 'close' event

Resource Management

const db = new Level('my-db');

// Create a resource that needs cleanup
const customResource = {
  close: async () => console.log('Custom resource closed')
};

// Attach resource to database lifecycle
db.attachResource(customResource);

// When database closes, attached resources are also closed
await db.close(); // Also closes customResource

Error Handling

Level operations may throw various errors:

  • NotFoundError: Thrown by get() when a key doesn't exist
  • OpenError: Thrown when database cannot be opened
  • WriteError: Thrown when write operations fail
  • IteratorError: Thrown during iterator operations
try {
  const value = await db.get('nonexistent-key');
} catch (error) {
  if (error.code === 'LEVEL_NOT_FOUND') {
    console.log('Key not found');
  }
}

Environment Compatibility

  • Node.js: 18+ with native LevelDB bindings
  • Browsers: Modern browsers with IndexedDB support (Chrome, Firefox, Safari, Edge)
  • Electron: 30+ with both Node.js and browser contexts
  • Platform Support: Linux, macOS, Windows, including ARM platforms
  • Key/Value Types: string, Buffer, Uint8Array for keys; any serializable type for values