A super fast and powerful state management library for JavaScript and React applications with proxy-based observables and fine-grained reactivity
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Comprehensive persistence system for offline-first applications with local storage and remote sync capabilities. Legend State's persistence layer provides automatic synchronization, conflict resolution, and supports multiple storage backends.
Makes observables persistent with automatic synchronization.
/**
* Makes an observable persistent with configurable storage and sync
* @param obs - Observable to make persistent
* @param config - Persistence configuration
*/
function persistObservable<T>(
obs: Observable<T>,
config: ObservablePersistenceConfig<T>
): void;
/**
* Creates a new persistent observable with initial state
* @param state - Initial state value
* @param config - Persistence configuration
* @returns Persistent observable
*/
function persistState<T>(
state: T,
config: ObservablePersistenceConfig<T>
): Observable<T>;Global persistence configuration and setup.
/**
* Configures global persistence settings
* @param config - Global persistence configuration
*/
function configureObservablePersistence(
config: ObservablePersistenceConfig
): void;
/**
* Maps multiple persistence configurations
* @param persistences - Array of persistence configs to apply
*/
function mapPersistences<T>(
persistences: ObservablePersistenceConfig<T>[]
): void;Functions for detecting and handling remote data changes.
/**
* Checks if currently processing a remote change
* @returns True if in remote change context
*/
function isInRemoteChange(): boolean;
/**
* Listens for changes originating from remote sources
* @param obs - Observable to listen to
* @param callback - Function called for remote changes
*/
function onChangeRemote<T>(
obs: Observable<T>,
callback: (value: T) => void
): void;Utilities for transforming field names during persistence operations.
/**
* Inverts a field mapping object (keys become values, values become keys)
* @param fieldMap - Field mapping to invert
* @returns Inverted field mapping
*/
function invertFieldMap(
fieldMap: Record<string, string>
): Record<string, string>;
/**
* Transforms object field names using a field mapping
* @param obj - Object to transform
* @param fieldMap - Mapping of old field names to new field names
* @returns Object with transformed field names
*/
function transformObject<T>(
obj: T,
fieldMap: Record<string, string>
): T;
/**
* Transforms a property path using field mapping
* @param path - Property path as string array
* @param fieldMap - Field name mapping
* @returns Transformed path
*/
function transformPath(
path: string[],
fieldMap: Record<string, string>
): string[];interface ObservablePersistenceConfig<T = any> {
/** Unique key for this persistence */
name?: string;
/** Local storage plugin configuration */
local?: LocalPersistenceConfig;
/** Remote storage plugin configuration */
remote?: RemotePersistenceConfig;
/** Field transformations for data mapping */
transform?: TransformConfig;
/** Initial data to use if no persisted data exists */
initial?: T;
/** Whether to persist immediately on changes */
persistImmediately?: boolean;
/** Debounce time for persistence operations */
debounceTime?: number;
/** Function to generate unique IDs for new items */
generateId?: () => string;
/** Custom retry configuration */
retry?: RetryConfig;
}
interface LocalPersistenceConfig {
/** Storage plugin to use */
plugin: StoragePlugin;
/** Storage key prefix */
prefix?: string;
/** Whether to persist metadata */
persistMetadata?: boolean;
}
interface RemotePersistenceConfig {
/** Remote sync plugin configuration */
plugin: RemotePlugin;
/** URL or endpoint for remote sync */
url?: string;
/** Headers to include in requests */
headers?: Record<string, string>;
/** Sync mode: 'auto' | 'manual' */
mode?: 'auto' | 'manual';
/** Conflict resolution strategy */
conflictResolution?: 'client' | 'server' | 'merge';
}
interface TransformConfig {
/** Field name mappings */
fields?: Record<string, string>;
/** Value transformation functions */
values?: {
[field: string]: {
parse: (value: any) => any;
stringify: (value: any) => any;
};
};
}
interface RetryConfig {
/** Maximum number of retry attempts */
maxAttempts?: number;
/** Delay between retries in milliseconds */
delay?: number;
/** Exponential backoff multiplier */
backoff?: number;
}Pre-built storage plugins for different platforms and use cases.
/** Local Storage plugin for web browsers */
declare const localStoragePlugin: StoragePlugin;
/** IndexedDB plugin for web browsers with larger storage */
declare const indexedDBPlugin: StoragePlugin;
/** AsyncStorage plugin for React Native */
declare const asyncStoragePlugin: StoragePlugin;
/** MMKV plugin for React Native high-performance storage */
declare const mmkvPlugin: StoragePlugin;
/** Firebase Realtime Database plugin */
declare const firebasePlugin: RemotePlugin;
/** Fetch-based remote plugin for REST APIs */
declare const fetchPlugin: RemotePlugin;
/** Query plugin for URL-based state persistence */
declare const queryPlugin: StoragePlugin;
interface StoragePlugin {
get(key: string): Promise<any>;
set(key: string, value: any): Promise<void>;
delete(key: string): Promise<void>;
getMetadata?(key: string): Promise<any>;
setMetadata?(key: string, metadata: any): Promise<void>;
}
interface RemotePlugin {
load(config: RemotePersistenceConfig): Promise<any>;
save(value: any, config: RemotePersistenceConfig): Promise<void>;
subscribe?(
config: RemotePersistenceConfig,
callback: (value: any) => void
): () => void;
}Usage Examples:
import { observable, persistObservable } from "@legendapp/state";
import { persistState } from "@legendapp/state/persist";
import {
localStoragePlugin,
fetchPlugin,
firebasePlugin
} from "@legendapp/state/persist-plugins";
// Simple local persistence
const user$ = observable({ name: "", email: "", preferences: {} });
persistObservable(user$, {
name: "user",
local: {
plugin: localStoragePlugin
}
});
// Local + Remote sync with conflict resolution
const todos$ = persistState([], {
name: "todos",
local: {
plugin: localStoragePlugin,
prefix: "myapp"
},
remote: {
plugin: fetchPlugin,
url: "https://api.example.com/todos",
mode: "auto",
conflictResolution: "merge",
headers: {
"Authorization": "Bearer token123"
}
},
transform: {
fields: {
"isCompleted": "done",
"description": "text"
}
},
retry: {
maxAttempts: 3,
delay: 1000,
backoff: 2
}
});
// Firebase real-time sync
const chat$ = persistState({ messages: [], users: [] }, {
name: "chat",
remote: {
plugin: firebasePlugin,
url: "https://myapp.firebaseio.com/chat"
}
});
// Field transformation example
const apiData$ = persistState({ user_id: 1, full_name: "John" }, {
name: "user",
local: {
plugin: localStoragePlugin
},
transform: {
fields: {
"user_id": "userId",
"full_name": "name"
},
values: {
"createdAt": {
parse: (dateString) => new Date(dateString),
stringify: (date) => date.toISOString()
}
}
}
});
// Listening for remote changes
onChangeRemote(todos$, (newTodos) => {
console.log("Todos updated from remote source:", newTodos);
// Handle remote updates (show notification, etc.)
});
// Manual sync control
const settings$ = persistState({ theme: "light" }, {
name: "settings",
local: { plugin: localStoragePlugin },
remote: {
plugin: fetchPlugin,
url: "/api/settings",
mode: "manual"
}
});
// Trigger manual sync
settings$.theme.set("dark");
// Will persist locally immediately, but won't sync to remote until:
// (Manual sync would be triggered by the app as needed)