Yjs LevelDB Persistence Adapter
y-leveldb is a persistence adapter for Yjs, the CRDT framework for real-time collaboration. It enables storing Yjs document updates persistently using LevelDB and its compatible implementations (like RocksDB, LMDB, or `level-mem`). The current stable version is `0.2.0`. Releases are infrequent but address compatibility, dependency updates, and bug fixes. Key differentiators include its flexibility to use various LevelUP-compatible storage backends, its direct integration into Yjs ecosystems (e.g., `y-websocket`), and its ability to manage multiple Yjs documents within a single database instance. The library provides a granular API for storing and retrieving document updates, state vectors, and custom metadata without necessarily hydrating a full Y.Doc object, which is crucial for efficient server-side synchronization and data management.
Common errors
-
TypeError: LeveldbPersistence is not a constructor
cause Attempting to import `LeveldbPersistence` using CommonJS `require()` syntax in an environment expecting ESM, or using incorrect destructuring.fixEnsure you are using the correct ESM import: `import { LeveldbPersistence } from 'y-leveldb'`. If running in a pure CommonJS environment, ensure Node.js can resolve ESM modules or consider transpilation. -
Error: Peer dependency yjs@^13.0.0 not met
cause The `yjs` package is either not installed or an incompatible version is installed in the project.fixInstall the correct version of `yjs`: `npm install yjs@^13.0.0`. Verify with `npm ls yjs`. -
Error: EACCES: permission denied, open './storage-location/manifest'
cause The Node.js process does not have sufficient read/write permissions for the specified `storageLocation` directory or its parent. This is common on servers when trying to write to `/` or system-protected directories.fixChange the `storageLocation` to a path where the Node.js process has full read/write permissions (e.g., a subdirectory within your application's data folder) or adjust the file system permissions for the target directory. -
Error: Could not find module 'level-mem'
cause When providing a custom `level` adapter (e.g., `level-mem`), the adapter package itself must be installed as a dependency.fixInstall the missing `level` adapter package: `npm install level-mem` (or the specific adapter you intend to use).
Warnings
- breaking Version `0.2.0` upgraded the underlying `level` dependency from version 6.x to 8.x. Users who directly interact with or pass custom `level` instances might encounter breaking changes due to API differences in `level` itself. Ensure compatibility if you rely on specific `level` versions or APIs.
- gotcha The `getAllDocStateVectors` method returns state vectors that "might be outdated if the associated document is not yet flushed." Relying on these state vectors for critical synchronization without understanding the internal flushing mechanism can lead to inconsistent states. Use with caution for real-time synchronization.
- gotcha The `flushDocument` method is explicitly marked as 'dev only' and generally not recommended for regular use. Calling it unnecessarily could impact performance or lead to unexpected behavior if not fully understood.
- gotcha The package has a peer dependency on `yjs` version `^13.0.0`. Incompatible versions of `yjs` can lead to runtime errors, unexpected behavior, or subtle data corruption due to mismatches in update formats or CRDT logic.
Install
-
npm install y-leveldb -
yarn add y-leveldb -
pnpm add y-leveldb
Imports
- LeveldbPersistence
const { LeveldbPersistence } = require('y-leveldb')import { LeveldbPersistence } from 'y-leveldb' - Y
import Y from 'yjs'
import * as Y from 'yjs'
- level
const level = require('level-mem')import level from 'level-mem'
Quickstart
import * as Y from 'yjs'
import { LeveldbPersistence } from 'y-leveldb'
// Create a persistence instance pointing to a storage location.
// For in-memory, you could pass { level: require('level-mem') } as the second argument.
const persistence = new LeveldbPersistence('./yjs-storage')
async function runExample() {
// Create a new Yjs document and make some changes.
const ydoc = new Y.Doc()
const yarray = ydoc.getArray('my-array')
yarray.insert(0, ['item 1', 'item 2'])
yarray.push(['item 3'])
console.log('Initial Y.Doc content:', yarray.toArray())
// Store the current state of 'my-doc' to LevelDB.
await persistence.storeUpdate('my-doc', Y.encodeStateAsUpdate(ydoc))
console.log('Document state stored.')
// To retrieve data or sync, create a temporary Y.Doc from persistence.
const persistedYDoc = await persistence.getYDoc('my-doc')
const persistedYarray = persistedYDoc.getArray('my-array')
console.log('Retrieved Y.Doc content:', persistedYarray.toArray())
// Example of retrieving metadata
await persistence.setMeta('my-doc', 'owner', 'Alice')
const owner = await persistence.getMeta('my-doc', 'owner')
console.log('Document owner:', owner)
// Clean up example data (optional)
// await persistence.clearDocument('my-doc')
// console.log('Document cleared.')
}
runExample().catch(console.error)