Skip to main content
The SyncStore is the single source of truth for all CL Sync state. It owns the IndexedDB connection for your scope, manages the in-memory record cache, coordinates the outbox, and notifies subscribers whenever state changes. You create it once with createSyncStore and share the instance across your component tree.

Creating a store

Configuration

SyncScope
required
Identifies the IndexedDB database for this session. Must include appId; environment, userId, and orgId are optional but strongly recommended to isolate data per user and organization.
SyncSchemaMetadata
Declares the current schema version and optional collection metadata. CL Sync compares schema.version against the persisted version during hydrate() to decide whether to run migrations.
readonly SyncMigration[]
Ordered list of migration definitions. Each migration runs once when the persisted schema version is behind the configured version. See the Migrations page for details.
readonly MutationDefinition[]
Mutations to register immediately on store creation. Registered mutations are available for outbox replay without calling registerMutation separately.
() => number
Override the timestamp function. Defaults to Date.now. Useful for deterministic tests.
"indexeddb" | "memory"
Storage backend. Defaults to "indexeddb". Use "memory" for SSR or unit tests where IndexedDB is unavailable.

Lifecycle methods

store.hydrate()

Loads all persisted records from IndexedDB, runs any pending migrations, restores the outbox, and sets status.hydrated = true. Always call hydrate() before reading data.
If the persisted schema version is newer than schema.version, hydrate() throws a version mismatch error to prevent data corruption from a downgrade. Bump schema.version before deploying schema changes.

store.clearScope()

Deletes all records, collection states, outbox items, and metadata for the active scope, then clears the in-memory cache.

Reading data

store.getStatus()

Returns the current SyncStatus snapshot. Does not subscribe — use store.subscribe or useSyncStatus() for reactive updates.

store.getCollection(definition, args?)

Returns the cached record array for a collection slice, or undefined if the slice has never been loaded.

store.getRecord(collection, id)

Returns a single cached record by collection name and ID, or undefined if not found.

store.getCollectionState(definition, args?)

Returns the CollectionState metadata for a collection slice — including updatedAt, staleAt, and any error from the last fetch.

Writing data

store.upsertCollection(definition, args, records, options?)

Replaces the entire record set for a collection slice. Merges records into the shared record map, updates the collection state, and persists to IndexedDB.
Use this method inside a mutation’s reducer for optimistic updates, or after a server response to sync the latest snapshot.

store.patchRecord(collection, id, patch)

Merges a partial patch into a single cached record and persists the change. The patch is shallow-merged using Object.assign.

Mutation methods

store.enqueueMutation(definition, args, clientMutationId?)

The main entry point for mutations. Runs the reducer immediately, writes to the outbox, then calls flush.

store.registerMutation(definition) / store.registerMutations(definitions)

Connects a mutation definition to its outbox rows so they can be flushed. Returns an unsubscribe function.

store.flushPendingMutations(options?)

Attempts to flush all pending (and optionally failed) outbox items. Returns a result describing what happened.
string[]
IDs of outbox items that flushed successfully.
Array<{ id, mutation, error }>
Items that threw during flush.
Array<{ id, mutation, reason }>
Items skipped due to "missing_definition", "flushing" (already in flight), or "filtered" (predicate returned false).
number
Total count of all remaining outbox items (across "pending", "flushing", and "failed" statuses) after this call.

store.flushMutation(definition, item)

Flushes a single outbox item directly. Useful for targeted retries.

store.getOutbox()

Returns all outbox items for the active scope.

Subscriptions

store.subscribe(listener)

Registers a listener that fires whenever in-memory state changes. Returns an unsubscribe function. All React hooks use this internally.

store.emit()

Manually triggers all listeners. Useful if you modify state through a method that doesn’t automatically notify subscribers.

Schema methods

store.getSchema()

Returns the SyncSchemaMetadata passed at construction.

store.getPersistedSchemaVersion()

Returns the schema version last written to IndexedDB, or undefined before hydrate() runs.

store.getMeta<T>(key)

Returns a typed metadata value stored in IndexedDB. Metadata persists across page loads and is scoped to the active scope key.