API reference / @evolu/common / local-first / ClientStorage

Interface: ClientStorage

Defined in: packages/common/src/local-first/Sync.ts:440

Evolu Storage

Evolu protocol using Storage is agnostic to storage implementation details—any storage can be plugged in, as long as it implements this interface. Implementations must handle their own errors; return values only indicate overall success or failure.

The Storage API is synchronous because SQLite's synchronous API is the fastest way to use SQLite. Synchronous bindings (like better-sqlite3) call SQLite's C API directly with no context switching between the event loop and native code, and no promise microtasks or await overhead.

The only exception is Storage#writeMessages, which is async to allow for async validation logic before writing to storage. The write operation itself remains synchronous.

Extends

Properties

PropertyModifierTypeDescriptionInherited fromDefined in
deleteOwnerreadonly(ownerId) => booleanDelete all data for the given Owner. Returns true on success, false on failure.Storage.deleteOwnerpackages/common/src/local-first/Storage.ts:167
findLowerBoundreadonly(ownerId, begin, end, upperBound) => | number & Brand<"Int"> & Brand<"NonNegative"> | null-Storage.findLowerBoundpackages/common/src/local-first/Storage.ts:113
fingerprintreadonly(ownerId, begin, end) => | Fingerprint | null-Storage.fingerprintpackages/common/src/local-first/Storage.ts:94
fingerprintRangesreadonly(ownerId, buckets, upperBound?) => | readonly FingerprintRange[] | nullComputes fingerprints with their upper bounds in one call. This function can be replaced with many fingerprint/findLowerBound calls, but implementations can leverage it for batching and more efficient fingerprint computation.Storage.fingerprintRangespackages/common/src/local-first/Storage.ts:107
getExistingTimestampsreadonly(ownerIdBytes, timestampsBytes) => Result<readonly Uint8Array<ArrayBufferLike> & Brand<"TimestampBytes">[], SqliteError>Efficiently checks which timestamps already exist in the database using a single CTE query instead of N individual queries.BaseSqliteStorage.getExistingTimestampspackages/common/src/local-first/Storage.ts:353
getSizereadonly(ownerId) => | number & Brand<"Int"> & Brand<"NonNegative"> | null-Storage.getSizepackages/common/src/local-first/Storage.ts:92
insertTimestampreadonly(ownerId, timestamp, strategy) => Result<void, SqliteError>Inserts a timestamp for an owner into the skiplist-based storage.BaseSqliteStorage.insertTimestamppackages/common/src/local-first/Storage.ts:343
iteratereadonly(ownerId, begin, end, callback) => void-Storage.iteratepackages/common/src/local-first/Storage.ts:120
readDbChangereadonly(ownerId, timestamp) => | EncryptedDbChange | nullRead encrypted DbChanges from storage.Storage.readDbChangepackages/common/src/local-first/Storage.ts:157
setWriteKeyreadonly(ownerId, writeKey) => booleanSets the OwnerWriteKey for the given Owner.Storage.setWriteKeypackages/common/src/local-first/Storage.ts:138
validateWriteKeyreadonly(ownerId, writeKey) => booleanValidates the OwnerWriteKey for the given Owner. Returns true if the write key is valid, false otherwise.Storage.validateWriteKeypackages/common/src/local-first/Storage.ts:132
writeMessagesreadonly(ownerIdBytes, messages) => MaybeAsync<Result<void, | StorageWriteError | StorageQuotaError>>Write encrypted CrdtMessages to storage. Must use a mutex per ownerId to ensure sequential processing and proper protocol logic handling during sync operations. TODO: Use MaybeAsyncStorage.writeMessagespackages/common/src/local-first/Storage.ts:151

Was this page helpful?