Skip to content
Strata Sync

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 configured StorageAdapter instance
  • IndexedDbStorageAdapter: the class implementing the full StorageAdapter interface
  • DatabaseManager: 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

MethodSignatureDescription
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

MethodSignatureDescription
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

MethodSignatureDescription
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

MethodSignatureDescription
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

MethodSignatureDescription
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

MethodSignatureDescription
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:

StoreKeyDescription
_metastringSync metadata and per-model persistence flags.
_transactionclientTxIdOutbox of pending transactions. Indexes: byState, byCreatedAt, byBatchIndex.
_sync_actionidPersisted sync actions for debugging.
<model_hash>Model primary keyOne store per registered model. Store names are deterministic hashes.

Partial databases

For models with "partial" load strategy, a separate database stores partial index entries:

StoreKeyDescription
partial_indexComposite keyTracks which index key/value pairs have been fetched.

Database registry

A top-level stratasync_databases database tracks all workspace databases:

StoreKeyDescription
databasesnameMaps 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";
FunctionDescription
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).