AI agents: fetch the documentation index at llms.txt. Markdown versions are available by appending .md to any page URL, including this page's markdown.
@stratasync/storage-idb
IndexedDB storage adapter for offline-first client-side persistence in Strata Sync.
Implements StorageAdapter using IndexedDB. Persists model data, the outbox, sync metadata, partial indexes, and sync actions for offline-first operation and instant startup.
What it provides
createIndexedDbStorage: factory function that creates a configuredStorageAdapterinstanceIndexedDbStorageAdapter: the class implementing the fullStorageAdapterinterfaceDatabaseManager: manages a registry of workspace databases for multi-user/multi-version support- Store operations: typed functions for model rows, metadata, outbox transactions, partial indexes, and sync actions
- Schema migrations: automatic IndexedDB schema upgrades when the model schema changes
- Store name utilities: deterministic hashing for store and database names
Installation
npm install @stratasync/storage-idb@stratasync/storage-idb has no peer dependencies. It uses the idb library (^8.0.0) internally for a promise-based IndexedDB wrapper.
Quick start
import { createSyncClient } from "@stratasync/client";
import { createIndexedDbStorage } from "@stratasync/storage-idb";
const storage = createIndexedDbStorage();
const client = createSyncClient({
storage,
// ... transport, reactivity
});The sync client opens the adapter during initialization. Call open() manually only when using the adapter outside the sync client.
StorageAdapter interface
Lifecycle
| Method | Signature | Description |
|---|---|---|
open | (options: StorageOptions) => Promise<void> | Opens the database, creates object stores, initializes metadata. |
close | () => Promise<void> | Closes all database connections. |
clear | () => Promise<void> | Clears all data from all stores (for testing or logout). |
Model row operations
| Method | Signature | Description |
|---|---|---|
get | <T>(modelName: string, id: string) => Promise<T | null> | Gets a single row by primary key. |
getAll | <T>(modelName: string) => Promise<T[]> | Gets all rows for a model. |
put | <T>(modelName: string, row: T) => Promise<void> | Inserts or updates a row. |
delete | (modelName: string, id: string) => Promise<void> | Deletes a row by primary key. |
getByIndex | <T>(modelName: string, indexName: string, key: string) => Promise<T[]> | Gets rows by an indexed field value. |
count | (modelName: string) => Promise<number> | Counts rows in a model store. |
writeBatch | (ops: BatchOperation[]) => Promise<void> | Executes multiple put/delete operations atomically in a single transaction. |
Metadata operations
| Method | Signature | Description |
|---|---|---|
getMeta | () => Promise<StorageMeta> | Gets the sync metadata (lastSyncId, schemaHash, and more). |
setMeta | (updates: Partial<StorageMeta>) => Promise<void> | Updates sync metadata fields. |
getModelPersistence | (modelName: string) => Promise<ModelPersistenceMeta> | Gets persistence status for a model. |
setModelPersistence | (modelName: string, persisted: boolean) => Promise<void> | Sets whether a model's data is fully persisted locally. |
Outbox operations
| Method | Signature | Description |
|---|---|---|
getOutbox | () => Promise<Transaction[]> | Gets all pending transactions from the outbox. |
addToOutbox | (tx: Transaction) => Promise<void> | Adds a transaction to the outbox. |
removeFromOutbox | (clientTxId: string) => Promise<void> | Removes a transaction by client transaction ID. |
updateOutboxTransaction | (clientTxId: string, updates: Partial<Transaction>) => Promise<void> | Updates fields on an outbox transaction. |
Partial index operations
| Method | Signature | Description |
|---|---|---|
hasPartialIndex | (modelName: string, indexedKey: string, keyValue: string) => Promise<boolean> | Checks if a partial index key has been fetched. |
setPartialIndex | (modelName: string, indexedKey: string, keyValue: string) => Promise<void> | Marks a partial index key as fetched. |
Sync action operations
| Method | Signature | Description |
|---|---|---|
addSyncActions | (actions: SyncAction[]) => Promise<void> | Persists sync actions for local replay/debugging. |
getSyncActions | (afterSyncId?: number, limit?: number) => Promise<SyncAction[]> | Reads stored sync actions after a given sync ID. |
clearSyncActions | () => Promise<void> | Clears all stored sync actions. |
Configuration
StorageOptions
Passed to open() by the sync client:
interface StorageOptions {
/** Database name override (auto-generated if omitted) */
name?: string;
/** Client version used to derive the workspace database name */
version?: number;
/** Logged-in user ID (used to derive the workspace database name) */
userId?: string;
/** Per-user version used to derive the workspace database name */
userVersion?: number;
/** Schema definition or registry snapshot */
schema?: SchemaDefinition | ModelRegistrySnapshot;
}When you don't provide name, the adapter computes the database name deterministically from userId, version, and userVersion using a hash function (for example, ss_<hash>).
Types
StorageMeta
Sync metadata stored in the _meta object store.
interface StorageMeta {
schemaHash?: string;
lastSyncId: number;
firstSyncId?: number;
subscribedSyncGroups?: string[];
clientId?: string;
bootstrapComplete?: boolean;
lastSyncAt?: number;
databaseVersion?: number;
updatedAt?: number;
}ModelPersistenceMeta
Per-model persistence tracking.
interface ModelPersistenceMeta {
modelName: string;
persisted: boolean;
updatedAt?: number;
}BatchOperation
Used with writeBatch for atomic multi-store writes.
type BatchOperationType = "put" | "delete";
interface BatchOperation {
type: BatchOperationType;
modelName: string;
id?: string; // Required for "delete"
data?: Record<string, unknown>; // Required for "put"
}PartialIndexEntry
Tracks which partial index keys have been fetched from the server.
interface PartialIndexEntry {
modelName: string;
indexedKey: string;
keyValue: string;
updatedAt?: number;
}Internal database structure
Expand to see the internal IndexedDB database layout
The adapter manages multiple IndexedDB databases.
Workspace database
The main database contains these object stores:
| Store | Key | Description |
|---|---|---|
_meta | string | Sync metadata and per-model persistence flags. |
_transaction | clientTxId | Outbox of pending transactions. Indexes: byState, byCreatedAt, byBatchIndex. |
_sync_action | id | Persisted sync actions for debugging. |
<model_hash> | Model primary key | One store per registered model. Store names are deterministic hashes. |
Partial databases
For models with "partial" load strategy, a separate database stores partial index entries:
| Store | Key | Description |
|---|---|---|
partial_index | Composite key | Tracks which index key/value pairs have been fetched. |
Database registry
A top-level stratasync_databases database tracks all workspace databases:
| Store | Key | Description |
|---|---|---|
databases | name | Maps database names to DatabaseInfo (userId, version, schemaHash, and more). |
Schema migrations
When the schema changes (via computeSchemaHash), the adapter increments the IndexedDB version, creates new object stores, adds indexes for @Property({ indexed: true }) fields, and preserves unchanged stores. This happens transparently during open().
Multi-workspace support
Deterministic database naming supports multiple users and versions.
import { computeWorkspaceDatabaseName } from "@stratasync/storage-idb";
const dbName = computeWorkspaceDatabaseName({
userId: "user-abc",
version: 1,
userVersion: 1,
});
// Returns: "ss_<32-char-hash>"Each user/version combination gets its own database, so switching accounts doesn't require clearing data.
Utility exports
Store name functions
Deterministic names for stores and databases:
import {
computeModelStoreName,
computeWorkspaceDatabaseName,
computePartialDatabaseName,
} from "@stratasync/storage-idb";| Function | Description |
|---|---|
computeModelStoreName(modelName, schemaVersion, registry) | Deterministic hash-based store name for a model. |
computeWorkspaceDatabaseName({ userId, version, userVersion }) | Deterministic database name for a workspace. |
computePartialDatabaseName(storeName) | Derives the partial index database name from a store name. |
Granular store operations
For testing and advanced use:
import {
// Model rows
getAllModelRows,
putManyModelRows,
deleteManyModelRows,
countModelRows,
iterateModelRows,
// Metadata
getMetadata,
setMetadata,
getLastSyncId,
setLastSyncId,
isBootstrapComplete,
setBootstrapComplete,
// Outbox
addTransaction,
getAllTransactions,
getTransactionsByState,
markTransactionSent,
markTransactionCompleted,
markTransactionFailed,
removeTransaction,
clearOutbox,
// Partial indexes
getPartialIndex,
setPartialIndex,
hasPartialIndex,
clearPartialIndexes,
} from "@stratasync/storage-idb";Additional granular operations for metadata, outbox, and partial indexes are also available.
Architecture role
Provides client-side persistence.
sync-core (defines Transaction, SyncAction types)
^-- sync-storage-idb (implements StorageAdapter with IndexedDB)
^-- sync-client (uses adapter for offline persistence)
The same interface can be implemented with other backends (SQLite, in-memory).