or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdcore-observables.mdhelper-functions.mdindex.mdpersistence.mdreact-integration.md
tile.json

persistence.mddocs/

0

# Persistence

1

2

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.

3

4

## Capabilities

5

6

### Core Persistence Functions

7

8

#### Persist Observable

9

10

Makes observables persistent with automatic synchronization.

11

12

```typescript { .api }

13

/**

14

* Makes an observable persistent with configurable storage and sync

15

* @param obs - Observable to make persistent

16

* @param config - Persistence configuration

17

*/

18

function persistObservable<T>(

19

obs: Observable<T>,

20

config: ObservablePersistenceConfig<T>

21

): void;

22

23

/**

24

* Creates a new persistent observable with initial state

25

* @param state - Initial state value

26

* @param config - Persistence configuration

27

* @returns Persistent observable

28

*/

29

function persistState<T>(

30

state: T,

31

config: ObservablePersistenceConfig<T>

32

): Observable<T>;

33

```

34

35

#### Configuration

36

37

Global persistence configuration and setup.

38

39

```typescript { .api }

40

/**

41

* Configures global persistence settings

42

* @param config - Global persistence configuration

43

*/

44

function configureObservablePersistence(

45

config: ObservablePersistenceConfig

46

): void;

47

48

/**

49

* Maps multiple persistence configurations

50

* @param persistences - Array of persistence configs to apply

51

*/

52

function mapPersistences<T>(

53

persistences: ObservablePersistenceConfig<T>[]

54

): void;

55

```

56

57

### Remote Change Detection

58

59

Functions for detecting and handling remote data changes.

60

61

```typescript { .api }

62

/**

63

* Checks if currently processing a remote change

64

* @returns True if in remote change context

65

*/

66

function isInRemoteChange(): boolean;

67

68

/**

69

* Listens for changes originating from remote sources

70

* @param obs - Observable to listen to

71

* @param callback - Function called for remote changes

72

*/

73

function onChangeRemote<T>(

74

obs: Observable<T>,

75

callback: (value: T) => void

76

): void;

77

```

78

79

### Field Transformation

80

81

Utilities for transforming field names during persistence operations.

82

83

```typescript { .api }

84

/**

85

* Inverts a field mapping object (keys become values, values become keys)

86

* @param fieldMap - Field mapping to invert

87

* @returns Inverted field mapping

88

*/

89

function invertFieldMap(

90

fieldMap: Record<string, string>

91

): Record<string, string>;

92

93

/**

94

* Transforms object field names using a field mapping

95

* @param obj - Object to transform

96

* @param fieldMap - Mapping of old field names to new field names

97

* @returns Object with transformed field names

98

*/

99

function transformObject<T>(

100

obj: T,

101

fieldMap: Record<string, string>

102

): T;

103

104

/**

105

* Transforms a property path using field mapping

106

* @param path - Property path as string array

107

* @param fieldMap - Field name mapping

108

* @returns Transformed path

109

*/

110

function transformPath(

111

path: string[],

112

fieldMap: Record<string, string>

113

): string[];

114

```

115

116

## Persistence Configuration

117

118

```typescript { .api }

119

interface ObservablePersistenceConfig<T = any> {

120

/** Unique key for this persistence */

121

name?: string;

122

/** Local storage plugin configuration */

123

local?: LocalPersistenceConfig;

124

/** Remote storage plugin configuration */

125

remote?: RemotePersistenceConfig;

126

/** Field transformations for data mapping */

127

transform?: TransformConfig;

128

/** Initial data to use if no persisted data exists */

129

initial?: T;

130

/** Whether to persist immediately on changes */

131

persistImmediately?: boolean;

132

/** Debounce time for persistence operations */

133

debounceTime?: number;

134

/** Function to generate unique IDs for new items */

135

generateId?: () => string;

136

/** Custom retry configuration */

137

retry?: RetryConfig;

138

}

139

140

interface LocalPersistenceConfig {

141

/** Storage plugin to use */

142

plugin: StoragePlugin;

143

/** Storage key prefix */

144

prefix?: string;

145

/** Whether to persist metadata */

146

persistMetadata?: boolean;

147

}

148

149

interface RemotePersistenceConfig {

150

/** Remote sync plugin configuration */

151

plugin: RemotePlugin;

152

/** URL or endpoint for remote sync */

153

url?: string;

154

/** Headers to include in requests */

155

headers?: Record<string, string>;

156

/** Sync mode: 'auto' | 'manual' */

157

mode?: 'auto' | 'manual';

158

/** Conflict resolution strategy */

159

conflictResolution?: 'client' | 'server' | 'merge';

160

}

161

162

interface TransformConfig {

163

/** Field name mappings */

164

fields?: Record<string, string>;

165

/** Value transformation functions */

166

values?: {

167

[field: string]: {

168

parse: (value: any) => any;

169

stringify: (value: any) => any;

170

};

171

};

172

}

173

174

interface RetryConfig {

175

/** Maximum number of retry attempts */

176

maxAttempts?: number;

177

/** Delay between retries in milliseconds */

178

delay?: number;

179

/** Exponential backoff multiplier */

180

backoff?: number;

181

}

182

```

183

184

## Storage Plugins

185

186

Pre-built storage plugins for different platforms and use cases.

187

188

```typescript { .api }

189

/** Local Storage plugin for web browsers */

190

declare const localStoragePlugin: StoragePlugin;

191

192

/** IndexedDB plugin for web browsers with larger storage */

193

declare const indexedDBPlugin: StoragePlugin;

194

195

/** AsyncStorage plugin for React Native */

196

declare const asyncStoragePlugin: StoragePlugin;

197

198

/** MMKV plugin for React Native high-performance storage */

199

declare const mmkvPlugin: StoragePlugin;

200

201

/** Firebase Realtime Database plugin */

202

declare const firebasePlugin: RemotePlugin;

203

204

/** Fetch-based remote plugin for REST APIs */

205

declare const fetchPlugin: RemotePlugin;

206

207

/** Query plugin for URL-based state persistence */

208

declare const queryPlugin: StoragePlugin;

209

210

interface StoragePlugin {

211

get(key: string): Promise<any>;

212

set(key: string, value: any): Promise<void>;

213

delete(key: string): Promise<void>;

214

getMetadata?(key: string): Promise<any>;

215

setMetadata?(key: string, metadata: any): Promise<void>;

216

}

217

218

interface RemotePlugin {

219

load(config: RemotePersistenceConfig): Promise<any>;

220

save(value: any, config: RemotePersistenceConfig): Promise<void>;

221

subscribe?(

222

config: RemotePersistenceConfig,

223

callback: (value: any) => void

224

): () => void;

225

}

226

```

227

228

**Usage Examples:**

229

230

```typescript

231

import { observable, persistObservable } from "@legendapp/state";

232

import { persistState } from "@legendapp/state/persist";

233

import {

234

localStoragePlugin,

235

fetchPlugin,

236

firebasePlugin

237

} from "@legendapp/state/persist-plugins";

238

239

// Simple local persistence

240

const user$ = observable({ name: "", email: "", preferences: {} });

241

242

persistObservable(user$, {

243

name: "user",

244

local: {

245

plugin: localStoragePlugin

246

}

247

});

248

249

// Local + Remote sync with conflict resolution

250

const todos$ = persistState([], {

251

name: "todos",

252

local: {

253

plugin: localStoragePlugin,

254

prefix: "myapp"

255

},

256

remote: {

257

plugin: fetchPlugin,

258

url: "https://api.example.com/todos",

259

mode: "auto",

260

conflictResolution: "merge",

261

headers: {

262

"Authorization": "Bearer token123"

263

}

264

},

265

transform: {

266

fields: {

267

"isCompleted": "done",

268

"description": "text"

269

}

270

},

271

retry: {

272

maxAttempts: 3,

273

delay: 1000,

274

backoff: 2

275

}

276

});

277

278

// Firebase real-time sync

279

const chat$ = persistState({ messages: [], users: [] }, {

280

name: "chat",

281

remote: {

282

plugin: firebasePlugin,

283

url: "https://myapp.firebaseio.com/chat"

284

}

285

});

286

287

// Field transformation example

288

const apiData$ = persistState({ user_id: 1, full_name: "John" }, {

289

name: "user",

290

local: {

291

plugin: localStoragePlugin

292

},

293

transform: {

294

fields: {

295

"user_id": "userId",

296

"full_name": "name"

297

},

298

values: {

299

"createdAt": {

300

parse: (dateString) => new Date(dateString),

301

stringify: (date) => date.toISOString()

302

}

303

}

304

}

305

});

306

307

// Listening for remote changes

308

onChangeRemote(todos$, (newTodos) => {

309

console.log("Todos updated from remote source:", newTodos);

310

// Handle remote updates (show notification, etc.)

311

});

312

313

// Manual sync control

314

const settings$ = persistState({ theme: "light" }, {

315

name: "settings",

316

local: { plugin: localStoragePlugin },

317

remote: {

318

plugin: fetchPlugin,

319

url: "/api/settings",

320

mode: "manual"

321

}

322

});

323

324

// Trigger manual sync

325

settings$.theme.set("dark");

326

// Will persist locally immediately, but won't sync to remote until:

327

// (Manual sync would be triggered by the app as needed)

328

```