@tldraw/store 4.1.0-canary.af5f4bce7236 → 4.1.0-canary.b34d5b101192

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist-cjs/index.d.ts +1880 -153
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/AtomMap.js +241 -1
  4. package/dist-cjs/lib/AtomMap.js.map +2 -2
  5. package/dist-cjs/lib/BaseRecord.js.map +2 -2
  6. package/dist-cjs/lib/ImmutableMap.js +141 -0
  7. package/dist-cjs/lib/ImmutableMap.js.map +2 -2
  8. package/dist-cjs/lib/IncrementalSetConstructor.js +45 -5
  9. package/dist-cjs/lib/IncrementalSetConstructor.js.map +2 -2
  10. package/dist-cjs/lib/RecordType.js +116 -21
  11. package/dist-cjs/lib/RecordType.js.map +2 -2
  12. package/dist-cjs/lib/RecordsDiff.js.map +2 -2
  13. package/dist-cjs/lib/Store.js +233 -39
  14. package/dist-cjs/lib/Store.js.map +2 -2
  15. package/dist-cjs/lib/StoreQueries.js +135 -22
  16. package/dist-cjs/lib/StoreQueries.js.map +2 -2
  17. package/dist-cjs/lib/StoreSchema.js +207 -2
  18. package/dist-cjs/lib/StoreSchema.js.map +2 -2
  19. package/dist-cjs/lib/StoreSideEffects.js +102 -10
  20. package/dist-cjs/lib/StoreSideEffects.js.map +2 -2
  21. package/dist-cjs/lib/executeQuery.js.map +2 -2
  22. package/dist-cjs/lib/migrate.js.map +2 -2
  23. package/dist-cjs/lib/setUtils.js.map +2 -2
  24. package/dist-esm/index.d.mts +1880 -153
  25. package/dist-esm/index.mjs +1 -1
  26. package/dist-esm/lib/AtomMap.mjs +241 -1
  27. package/dist-esm/lib/AtomMap.mjs.map +2 -2
  28. package/dist-esm/lib/BaseRecord.mjs.map +2 -2
  29. package/dist-esm/lib/ImmutableMap.mjs +141 -0
  30. package/dist-esm/lib/ImmutableMap.mjs.map +2 -2
  31. package/dist-esm/lib/IncrementalSetConstructor.mjs +45 -5
  32. package/dist-esm/lib/IncrementalSetConstructor.mjs.map +2 -2
  33. package/dist-esm/lib/RecordType.mjs +116 -21
  34. package/dist-esm/lib/RecordType.mjs.map +2 -2
  35. package/dist-esm/lib/RecordsDiff.mjs.map +2 -2
  36. package/dist-esm/lib/Store.mjs +233 -39
  37. package/dist-esm/lib/Store.mjs.map +2 -2
  38. package/dist-esm/lib/StoreQueries.mjs +135 -22
  39. package/dist-esm/lib/StoreQueries.mjs.map +2 -2
  40. package/dist-esm/lib/StoreSchema.mjs +207 -2
  41. package/dist-esm/lib/StoreSchema.mjs.map +2 -2
  42. package/dist-esm/lib/StoreSideEffects.mjs +102 -10
  43. package/dist-esm/lib/StoreSideEffects.mjs.map +2 -2
  44. package/dist-esm/lib/executeQuery.mjs.map +2 -2
  45. package/dist-esm/lib/migrate.mjs.map +2 -2
  46. package/dist-esm/lib/setUtils.mjs.map +2 -2
  47. package/package.json +3 -3
  48. package/src/lib/AtomMap.ts +241 -1
  49. package/src/lib/BaseRecord.test.ts +44 -0
  50. package/src/lib/BaseRecord.ts +118 -4
  51. package/src/lib/ImmutableMap.test.ts +103 -0
  52. package/src/lib/ImmutableMap.ts +212 -0
  53. package/src/lib/IncrementalSetConstructor.test.ts +111 -0
  54. package/src/lib/IncrementalSetConstructor.ts +63 -6
  55. package/src/lib/RecordType.ts +149 -25
  56. package/src/lib/RecordsDiff.test.ts +144 -0
  57. package/src/lib/RecordsDiff.ts +144 -9
  58. package/src/lib/Store.test.ts +827 -0
  59. package/src/lib/Store.ts +533 -67
  60. package/src/lib/StoreQueries.test.ts +627 -0
  61. package/src/lib/StoreQueries.ts +194 -27
  62. package/src/lib/StoreSchema.test.ts +226 -0
  63. package/src/lib/StoreSchema.ts +386 -8
  64. package/src/lib/StoreSideEffects.test.ts +239 -19
  65. package/src/lib/StoreSideEffects.ts +266 -19
  66. package/src/lib/devFreeze.test.ts +137 -0
  67. package/src/lib/executeQuery.test.ts +481 -0
  68. package/src/lib/executeQuery.ts +80 -2
  69. package/src/lib/migrate.test.ts +400 -0
  70. package/src/lib/migrate.ts +187 -14
  71. package/src/lib/setUtils.test.ts +105 -0
  72. package/src/lib/setUtils.ts +44 -4
@@ -27,28 +27,289 @@ export declare function assertIdType<R extends UnknownRecord>(id: string | undef
27
27
  export declare class AtomMap<K, V> implements Map<K, V> {
28
28
  private readonly name;
29
29
  private atoms;
30
+ /**
31
+ * Creates a new AtomMap instance.
32
+ *
33
+ * name - A unique name for this map, used for atom identification
34
+ * entries - Optional initial entries to populate the map with
35
+ * @example
36
+ * ```ts
37
+ * // Create an empty map
38
+ * const map = new AtomMap('userMap')
39
+ *
40
+ * // Create a map with initial data
41
+ * const initialData: [string, number][] = [['a', 1], ['b', 2]]
42
+ * const mapWithData = new AtomMap('numbersMap', initialData)
43
+ * ```
44
+ */
30
45
  constructor(name: string, entries?: Iterable<readonly [K, V]>);
31
46
  /* Excluded from this release type: getAtom */
47
+ /**
48
+ * Gets the value associated with a key. Returns undefined if the key doesn't exist.
49
+ * This method is reactive and will cause reactive contexts to update when the value changes.
50
+ *
51
+ * @param key - The key to retrieve the value for
52
+ * @returns The value associated with the key, or undefined if not found
53
+ * @example
54
+ * ```ts
55
+ * const map = new AtomMap('myMap')
56
+ * map.set('name', 'Alice')
57
+ * console.log(map.get('name')) // 'Alice'
58
+ * console.log(map.get('missing')) // undefined
59
+ * ```
60
+ */
32
61
  get(key: K): undefined | V;
62
+ /**
63
+ * Gets the value associated with a key without creating reactive dependencies.
64
+ * This method will not cause reactive contexts to update when the value changes.
65
+ *
66
+ * @param key - The key to retrieve the value for
67
+ * @returns The value associated with the key, or undefined if not found
68
+ * @example
69
+ * ```ts
70
+ * const map = new AtomMap('myMap')
71
+ * map.set('count', 42)
72
+ * const value = map.__unsafe__getWithoutCapture('count') // No reactive subscription
73
+ * ```
74
+ */
33
75
  __unsafe__getWithoutCapture(key: K): undefined | V;
76
+ /**
77
+ * Checks whether a key exists in the map.
78
+ * This method is reactive and will cause reactive contexts to update when keys are added or removed.
79
+ *
80
+ * @param key - The key to check for
81
+ * @returns True if the key exists in the map, false otherwise
82
+ * @example
83
+ * ```ts
84
+ * const map = new AtomMap('myMap')
85
+ * console.log(map.has('name')) // false
86
+ * map.set('name', 'Alice')
87
+ * console.log(map.has('name')) // true
88
+ * ```
89
+ */
34
90
  has(key: K): boolean;
91
+ /**
92
+ * Checks whether a key exists in the map without creating reactive dependencies.
93
+ * This method will not cause reactive contexts to update when keys are added or removed.
94
+ *
95
+ * @param key - The key to check for
96
+ * @returns True if the key exists in the map, false otherwise
97
+ * @example
98
+ * ```ts
99
+ * const map = new AtomMap('myMap')
100
+ * map.set('active', true)
101
+ * const exists = map.__unsafe__hasWithoutCapture('active') // No reactive subscription
102
+ * ```
103
+ */
35
104
  __unsafe__hasWithoutCapture(key: K): boolean;
105
+ /**
106
+ * Sets a value for the given key. If the key already exists, its value is updated.
107
+ * If the key doesn't exist, a new entry is created.
108
+ *
109
+ * @param key - The key to set the value for
110
+ * @param value - The value to associate with the key
111
+ * @returns This AtomMap instance for method chaining
112
+ * @example
113
+ * ```ts
114
+ * const map = new AtomMap('myMap')
115
+ * map.set('name', 'Alice').set('age', 30)
116
+ * ```
117
+ */
36
118
  set(key: K, value: V): this;
119
+ /**
120
+ * Updates an existing value using an updater function.
121
+ *
122
+ * @param key - The key of the value to update
123
+ * @param updater - A function that receives the current value and returns the new value
124
+ * @throws Error if the key doesn't exist in the map
125
+ * @example
126
+ * ```ts
127
+ * const map = new AtomMap('myMap')
128
+ * map.set('count', 5)
129
+ * map.update('count', count => count + 1) // count is now 6
130
+ * ```
131
+ */
37
132
  update(key: K, updater: (value: V) => V): void;
133
+ /**
134
+ * Removes a key-value pair from the map.
135
+ *
136
+ * @param key - The key to remove
137
+ * @returns True if the key existed and was removed, false if it didn't exist
138
+ * @example
139
+ * ```ts
140
+ * const map = new AtomMap('myMap')
141
+ * map.set('temp', 'value')
142
+ * console.log(map.delete('temp')) // true
143
+ * console.log(map.delete('missing')) // false
144
+ * ```
145
+ */
38
146
  delete(key: K): boolean;
147
+ /**
148
+ * Removes multiple key-value pairs from the map in a single transaction.
149
+ *
150
+ * @param keys - An iterable of keys to remove
151
+ * @returns An array of [key, value] pairs that were actually deleted
152
+ * @example
153
+ * ```ts
154
+ * const map = new AtomMap('myMap')
155
+ * map.set('a', 1).set('b', 2).set('c', 3)
156
+ * const deleted = map.deleteMany(['a', 'c', 'missing'])
157
+ * console.log(deleted) // [['a', 1], ['c', 3]]
158
+ * ```
159
+ */
39
160
  deleteMany(keys: Iterable<K>): [K, V][];
161
+ /**
162
+ * Removes all key-value pairs from the map.
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * const map = new AtomMap('myMap')
167
+ * map.set('a', 1).set('b', 2)
168
+ * map.clear()
169
+ * console.log(map.size) // 0
170
+ * ```
171
+ */
40
172
  clear(): void;
173
+ /**
174
+ * Returns an iterator that yields [key, value] pairs for each entry in the map.
175
+ * This method is reactive and will cause reactive contexts to update when entries change.
176
+ *
177
+ * @returns A generator that yields [key, value] tuples
178
+ * @example
179
+ * ```ts
180
+ * const map = new AtomMap('myMap')
181
+ * map.set('a', 1).set('b', 2)
182
+ * for (const [key, value] of map.entries()) {
183
+ * console.log(`${key}: ${value}`)
184
+ * }
185
+ * ```
186
+ */
41
187
  entries(): Generator<[K, V], undefined, unknown>;
188
+ /**
189
+ * Returns an iterator that yields all keys in the map.
190
+ * This method is reactive and will cause reactive contexts to update when keys change.
191
+ *
192
+ * @returns A generator that yields keys
193
+ * @example
194
+ * ```ts
195
+ * const map = new AtomMap('myMap')
196
+ * map.set('name', 'Alice').set('age', 30)
197
+ * for (const key of map.keys()) {
198
+ * console.log(key) // 'name', 'age'
199
+ * }
200
+ * ```
201
+ */
42
202
  keys(): Generator<K, undefined, unknown>;
203
+ /**
204
+ * Returns an iterator that yields all values in the map.
205
+ * This method is reactive and will cause reactive contexts to update when values change.
206
+ *
207
+ * @returns A generator that yields values
208
+ * @example
209
+ * ```ts
210
+ * const map = new AtomMap('myMap')
211
+ * map.set('name', 'Alice').set('age', 30)
212
+ * for (const value of map.values()) {
213
+ * console.log(value) // 'Alice', 30
214
+ * }
215
+ * ```
216
+ */
43
217
  values(): Generator<V, undefined, unknown>;
218
+ /**
219
+ * The number of key-value pairs in the map.
220
+ * This property is reactive and will cause reactive contexts to update when the size changes.
221
+ *
222
+ * @returns The number of entries in the map
223
+ * @example
224
+ * ```ts
225
+ * const map = new AtomMap('myMap')
226
+ * console.log(map.size) // 0
227
+ * map.set('a', 1)
228
+ * console.log(map.size) // 1
229
+ * ```
230
+ */
44
231
  get size(): number;
232
+ /**
233
+ * Executes a provided function once for each key-value pair in the map.
234
+ * This method is reactive and will cause reactive contexts to update when entries change.
235
+ *
236
+ * @param callbackfn - Function to execute for each entry
237
+ * - value - The value of the current entry
238
+ * - key - The key of the current entry
239
+ * - map - The AtomMap being traversed
240
+ * @param thisArg - Value to use as `this` when executing the callback
241
+ * @example
242
+ * ```ts
243
+ * const map = new AtomMap('myMap')
244
+ * map.set('a', 1).set('b', 2)
245
+ * map.forEach((value, key) => {
246
+ * console.log(`${key} = ${value}`)
247
+ * })
248
+ * ```
249
+ */
45
250
  forEach(callbackfn: (value: V, key: K, map: AtomMap<K, V>) => void, thisArg?: any): void;
251
+ /**
252
+ * Returns the default iterator for the map, which is the same as entries().
253
+ * This allows the map to be used in for...of loops and other iterable contexts.
254
+ *
255
+ * @returns The same iterator as entries()
256
+ * @example
257
+ * ```ts
258
+ * const map = new AtomMap('myMap')
259
+ * map.set('a', 1).set('b', 2)
260
+ *
261
+ * // These are equivalent:
262
+ * for (const [key, value] of map) {
263
+ * console.log(`${key}: ${value}`)
264
+ * }
265
+ *
266
+ * for (const [key, value] of map.entries()) {
267
+ * console.log(`${key}: ${value}`)
268
+ * }
269
+ * ```
270
+ */
46
271
  [Symbol.iterator](): Generator<[K, V], undefined, unknown>;
272
+ /**
273
+ * The string tag used by Object.prototype.toString for this class.
274
+ *
275
+ * @example
276
+ * ```ts
277
+ * const map = new AtomMap('myMap')
278
+ * console.log(Object.prototype.toString.call(map)) // '[object AtomMap]'
279
+ * ```
280
+ */
47
281
  [Symbol.toStringTag]: string;
48
282
  }
49
283
 
50
284
  /**
51
- * The base record that all records must extend.
285
+ * The base record interface that all records in the store must extend.
286
+ * This interface provides the fundamental structure required for all records: a unique ID and a type name.
287
+ * The type parameters ensure type safety and prevent mixing of different record types.
288
+ *
289
+ * @example
290
+ * ```ts
291
+ * // Define a Book record that extends BaseRecord
292
+ * interface Book extends BaseRecord<'book', RecordId<Book>> {
293
+ * title: string
294
+ * author: string
295
+ * publishedYear: number
296
+ * }
297
+ *
298
+ * // Define an Author record
299
+ * interface Author extends BaseRecord<'author', RecordId<Author>> {
300
+ * name: string
301
+ * birthYear: number
302
+ * }
303
+ *
304
+ * // Usage with RecordType
305
+ * const Book = createRecordType<Book>('book', { scope: 'document' })
306
+ * const book = Book.create({
307
+ * title: '1984',
308
+ * author: 'George Orwell',
309
+ * publishedYear: 1949
310
+ * })
311
+ * // Results in: { id: 'book:abc123', typeName: 'book', title: '1984', ... }
312
+ * ```
52
313
  *
53
314
  * @public
54
315
  */
@@ -57,59 +318,135 @@ export declare interface BaseRecord<TypeName extends string, Id extends RecordId
57
318
  readonly typeName: TypeName;
58
319
  }
59
320
 
60
- /** @public */
321
+ /**
322
+ * The source of a change to the store.
323
+ * - `'user'` - Changes originating from local user actions
324
+ * - `'remote'` - Changes originating from remote synchronization
325
+ *
326
+ * @public
327
+ */
61
328
  export declare type ChangeSource = 'remote' | 'user';
62
329
 
63
330
  /**
64
331
  * A diff describing the changes to a collection.
65
332
  *
333
+ * @example
334
+ * ```ts
335
+ * const diff: CollectionDiff<string> = {
336
+ * added: new Set(['newItem']),
337
+ * removed: new Set(['oldItem'])
338
+ * }
339
+ * ```
340
+ *
66
341
  * @public
67
342
  */
68
343
  export declare interface CollectionDiff<T> {
344
+ /** Items that were added to the collection */
69
345
  added?: Set<T>;
346
+ /** Items that were removed from the collection */
70
347
  removed?: Set<T>;
71
348
  }
72
349
 
73
350
  /**
74
- * A record store is a collection of records of different types.
351
+ * A computed cache that stores derived data for records.
352
+ * The cache automatically updates when underlying records change and cleans up when records are deleted.
353
+ *
354
+ * @example
355
+ * ```ts
356
+ * const expensiveCache = store.createComputedCache(
357
+ * 'expensive',
358
+ * (book: Book) => performExpensiveCalculation(book)
359
+ * )
360
+ *
361
+ * const result = expensiveCache.get(bookId)
362
+ * ```
75
363
  *
76
364
  * @public
77
365
  */
78
366
  export declare interface ComputedCache<Data, R extends UnknownRecord> {
367
+ /**
368
+ * Get the cached data for a record by its ID.
369
+ *
370
+ * @param id - The ID of the record
371
+ * @returns The cached data or undefined if the record doesn't exist
372
+ */
79
373
  get(id: IdOf<R>): Data | undefined;
80
374
  }
81
375
 
82
376
  /**
83
- * Free version of {@link Store.createComputedCache}.
377
+ * Create a computed cache that works with any StoreObject (store or object containing a store).
378
+ * This is a standalone version of Store.createComputedCache that can work with multiple store instances.
84
379
  *
85
380
  * @example
86
381
  * ```ts
87
- * const myCache = createComputedCache('myCache', (editor: Editor, shape: TLShape) => {
88
- * return editor.getSomethingExpensive(shape)
89
- * })
382
+ * const expensiveCache = createComputedCache(
383
+ * 'expensiveData',
384
+ * (context: { store: Store<Book> }, book: Book) => {
385
+ * return performExpensiveCalculation(book)
386
+ * }
387
+ * )
90
388
  *
91
- * myCache.get(editor, shape.id)
389
+ * // Use with different store instances
390
+ * const result1 = expensiveCache.get(storeObject1, bookId)
391
+ * const result2 = expensiveCache.get(storeObject2, bookId)
92
392
  * ```
93
393
  *
394
+ * @param name - A unique name for the cache (used for debugging)
395
+ * @param derive - Function that derives a value from the context and record
396
+ * @param opts - Optional configuration for equality checks
397
+ * @returns A cache that can be used with multiple store instances
94
398
  * @public
95
399
  */
96
400
  export declare function createComputedCache<Context extends StoreObject<any>, Result, Record extends StoreObjectRecordType<Context> = StoreObjectRecordType<Context>>(name: string, derive: (context: Context, record: Record) => Result | undefined, opts?: CreateComputedCacheOpts<Result, Record>): {
97
401
  get(context: Context, id: IdOf<Record>): Result | undefined;
98
402
  };
99
403
 
100
- /** @public */
404
+ /**
405
+ * Options for creating a computed cache.
406
+ *
407
+ * @example
408
+ * ```ts
409
+ * const options: CreateComputedCacheOpts<string[], Book> = {
410
+ * areRecordsEqual: (a, b) => a.title === b.title,
411
+ * areResultsEqual: (a, b) => JSON.stringify(a) === JSON.stringify(b)
412
+ * }
413
+ * ```
414
+ *
415
+ * @public
416
+ */
101
417
  export declare interface CreateComputedCacheOpts<Data, R extends UnknownRecord> {
418
+ /** Custom equality function for comparing records */
102
419
  areRecordsEqual?(a: R, b: R): boolean;
420
+ /** Custom equality function for comparing results */
103
421
  areResultsEqual?(a: Data, b: Data): boolean;
104
422
  }
105
423
 
106
424
  /* Excluded from this release type: createEmptyRecordsDiff */
107
425
 
108
426
  /**
109
- * Creates a named set of migration ids given a named set of version numbers and a sequence id.
427
+ * Creates a named set of migration IDs from version numbers and a sequence ID.
428
+ *
429
+ * This utility function helps generate properly formatted migration IDs that follow
430
+ * the required `sequenceId/version` pattern. It takes a sequence ID and a record
431
+ * of named versions, returning migration IDs that can be used in migration definitions.
110
432
  *
111
433
  * See the [migration guide](https://tldraw.dev/docs/persistence#Migrations) for more info on how to use this API.
112
- * @public
434
+ * @param sequenceId - The sequence identifier (e.g., 'com.myapp.book')
435
+ * @param versions - Record mapping version names to numbers
436
+ * @returns Record mapping version names to properly formatted migration IDs
437
+ * @example
438
+ * ```ts
439
+ * const migrationIds = createMigrationIds('com.myapp.book', {
440
+ * addGenre: 1,
441
+ * addPublisher: 2,
442
+ * removeOldField: 3
443
+ * })
444
+ * // Result: {
445
+ * // addGenre: 'com.myapp.book/1',
446
+ * // addPublisher: 'com.myapp.book/2',
447
+ * // removeOldField: 'com.myapp.book/3'
448
+ * // }
449
+ * ```
113
450
  * @public
114
451
  */
115
452
  export declare function createMigrationIds<const ID extends string, const Versions extends Record<string, number>>(sequenceId: ID, versions: Versions): {
@@ -117,8 +454,32 @@ export declare function createMigrationIds<const ID extends string, const Versio
117
454
  };
118
455
 
119
456
  /**
120
- * Creates a migration sequence.
457
+ * Creates a migration sequence that defines how to transform data as your schema evolves.
458
+ *
459
+ * A migration sequence contains a series of migrations that are applied in order to transform
460
+ * data from older versions to newer versions. Each migration is identified by a unique ID
461
+ * and can operate at either the record level (transforming individual records) or store level
462
+ * (transforming the entire store structure).
463
+ *
121
464
  * See the [migration guide](https://tldraw.dev/docs/persistence#Migrations) for more info on how to use this API.
465
+ * @param options - Configuration for the migration sequence
466
+ * - sequenceId - Unique identifier for this migration sequence (e.g., 'com.myapp.book')
467
+ * - sequence - Array of migrations or dependency declarations to include in the sequence
468
+ * - retroactive - Whether migrations should apply to snapshots created before this sequence was added (defaults to true)
469
+ * @returns A validated migration sequence that can be included in a store schema
470
+ * @example
471
+ * ```ts
472
+ * const bookMigrations = createMigrationSequence({
473
+ * sequenceId: 'com.myapp.book',
474
+ * sequence: [
475
+ * {
476
+ * id: 'com.myapp.book/1',
477
+ * scope: 'record',
478
+ * up: (record) => ({ ...record, newField: 'default' })
479
+ * }
480
+ * ]
481
+ * })
482
+ * ```
122
483
  * @public
123
484
  */
124
485
  export declare function createMigrationSequence({ sequence, sequenceId, retroactive, }: {
@@ -130,15 +491,29 @@ export declare function createMigrationSequence({ sequence, sequenceId, retroact
130
491
  /* Excluded from this release type: createRecordMigrationSequence */
131
492
 
132
493
  /**
133
- * Create a record type.
494
+ * Creates a new RecordType with the specified configuration.
134
495
  *
135
- * @example
496
+ * This factory function creates a RecordType that can be used to create, validate, and manage
497
+ * records of a specific type within a store. The resulting RecordType can be extended with
498
+ * default properties using the withDefaultProperties method.
136
499
  *
500
+ * @example
137
501
  * ```ts
138
- * const Book = createRecordType<Book>('book')
502
+ * interface BookRecord extends BaseRecord<'book', RecordId<BookRecord>> {
503
+ * title: string
504
+ * author: string
505
+ * inStock: boolean
506
+ * }
507
+ *
508
+ * const Book = createRecordType<BookRecord>('book', {
509
+ * scope: 'document',
510
+ * validator: bookValidator
511
+ * })
139
512
  * ```
140
513
  *
141
- * @param typeName - The name of the type to create.
514
+ * @param typeName - The unique type name for this record type
515
+ * @param config - Configuration object containing validator, scope, and ephemeral keys
516
+ * @returns A new RecordType instance for creating and managing records
142
517
  * @public
143
518
  */
144
519
  export declare function createRecordType<R extends UnknownRecord>(typeName: R['typeName'], config: {
@@ -167,26 +542,85 @@ export declare function devFreeze<T>(object: T): T;
167
542
 
168
543
  /**
169
544
  * An entry containing changes that originated either by user actions or remote changes.
545
+ * History entries are used to track and replay changes to the store.
546
+ *
547
+ * @example
548
+ * ```ts
549
+ * const entry: HistoryEntry<Book> = {
550
+ * changes: {
551
+ * added: { 'book:123': bookRecord },
552
+ * updated: {},
553
+ * removed: {}
554
+ * },
555
+ * source: 'user'
556
+ * }
557
+ * ```
170
558
  *
171
559
  * @public
172
560
  */
173
561
  export declare interface HistoryEntry<R extends UnknownRecord = UnknownRecord> {
562
+ /** The changes that occurred in this history entry */
174
563
  changes: RecordsDiff<R>;
564
+ /** The source of these changes */
175
565
  source: ChangeSource;
176
566
  }
177
567
 
178
- /** @public */
568
+ /**
569
+ * Utility type that extracts the ID type from a record type.
570
+ * This is useful when you need to work with record IDs without having the full record type.
571
+ *
572
+ * @example
573
+ * ```ts
574
+ * interface Book extends BaseRecord<'book', RecordId<Book>> {
575
+ * title: string
576
+ * author: string
577
+ * }
578
+ *
579
+ * // Extract the ID type from the Book record
580
+ * type BookId = IdOf<Book> // RecordId<Book>
581
+ *
582
+ * function findBook(id: IdOf<Book>): Book | undefined {
583
+ * return store.get(id)
584
+ * }
585
+ * ```
586
+ *
587
+ * @public
588
+ */
179
589
  export declare type IdOf<R extends UnknownRecord> = R['id'];
180
590
 
181
591
  /* Excluded from this release type: IncrementalSetConstructor */
182
592
 
183
593
  /**
184
- * Is a records diff empty?
594
+ * Checks whether a RecordsDiff contains any changes. A diff is considered empty
595
+ * if it has no added, updated, or removed records.
596
+ *
597
+ * @param diff - The diff to check
598
+ * @returns True if the diff contains no changes, false otherwise
599
+ * @example
600
+ * ```ts
601
+ * const emptyDiff = createEmptyRecordsDiff<Book>()
602
+ * console.log(isRecordsDiffEmpty(emptyDiff)) // true
603
+ *
604
+ * const nonEmptyDiff: RecordsDiff<Book> = {
605
+ * added: { 'book:1': someBook },
606
+ * updated: {},
607
+ * removed: {}
608
+ * }
609
+ * console.log(isRecordsDiffEmpty(nonEmptyDiff)) // false
610
+ * ```
611
+ *
185
612
  * @public
186
613
  */
187
614
  export declare function isRecordsDiffEmpty<T extends UnknownRecord>(diff: RecordsDiff<T>): boolean;
188
615
 
189
- /** @public */
616
+ /**
617
+ * Base interface for legacy migration information.
618
+ *
619
+ * Contains the basic structure used by the legacy migration system, including version
620
+ * range information and the migration functions indexed by version number. This is
621
+ * maintained for backward compatibility with older migration definitions.
622
+ * @public
623
+ */
190
624
  export declare interface LegacyBaseMigrationsInfo {
191
625
  firstVersion: number;
192
626
  currentVersion: number;
@@ -195,19 +629,45 @@ export declare interface LegacyBaseMigrationsInfo {
195
629
  };
196
630
  }
197
631
 
198
- /** @public */
632
+ /**
633
+ * Legacy migration interface for backward compatibility.
634
+ *
635
+ * This interface represents the old migration format that included both `up` and `down`
636
+ * transformation functions. While still supported, new code should use the `Migration`
637
+ * type which provides more flexibility and better integration with the current system.
638
+ * @public
639
+ */
199
640
  export declare interface LegacyMigration<Before = any, After = any> {
200
641
  up: (oldState: Before) => After;
201
642
  down: (newState: After) => Before;
202
643
  }
203
644
 
204
- /** @public */
645
+ /**
646
+ * Legacy migration configuration with support for sub-type migrations.
647
+ *
648
+ * This interface extends the base legacy migration info to support migrations that
649
+ * vary based on a sub-type key within records. This allows different migration paths
650
+ * for different variants of the same record type, which was useful in older migration
651
+ * systems but is now handled more elegantly by the current Migration system.
652
+ * @public
653
+ */
205
654
  export declare interface LegacyMigrations extends LegacyBaseMigrationsInfo {
206
655
  subTypeKey?: string;
207
656
  subTypeMigrations?: Record<string, LegacyBaseMigrationsInfo>;
208
657
  }
209
658
 
210
- /** @public */
659
+ /**
660
+ * Defines a single migration that transforms data from one schema version to another.
661
+ *
662
+ * A migration can operate at two different scopes:
663
+ * - `record`: Transforms individual records, with optional filtering to target specific records
664
+ * - `store`: Transforms the entire serialized store structure
665
+ *
666
+ * Each migration has a unique ID and can declare dependencies on other migrations that must
667
+ * be applied first. The `up` function performs the forward transformation, while the optional
668
+ * `down` function can reverse the migration if needed.
669
+ * @public
670
+ */
211
671
  export declare type Migration = {
212
672
  readonly dependsOn?: readonly MigrationId[] | undefined;
213
673
  readonly id: MigrationId;
@@ -222,7 +682,15 @@ export declare type Migration = {
222
682
  readonly up: (oldState: UnknownRecord) => UnknownRecord | void;
223
683
  });
224
684
 
225
- /** @public */
685
+ /**
686
+ * Enumeration of possible reasons why a migration might fail.
687
+ *
688
+ * These reasons help identify what went wrong during migration processing,
689
+ * allowing applications to handle different failure scenarios appropriately.
690
+ * Common failures include incompatible data formats, unknown record types,
691
+ * and version mismatches between the data and available migrations.
692
+ * @public
693
+ */
226
694
  export declare enum MigrationFailureReason {
227
695
  IncompatibleSubtype = "incompatible-subtype",
228
696
  UnknownType = "unknown-type",
@@ -232,10 +700,24 @@ export declare enum MigrationFailureReason {
232
700
  UnrecognizedSubtype = "unrecognized-subtype"
233
701
  }
234
702
 
235
- /** @public */
703
+ /**
704
+ * Unique identifier for a migration in the format `sequenceId/version`.
705
+ *
706
+ * Migration IDs follow a specific pattern where the sequence ID identifies the migration
707
+ * sequence and the version number indicates the order within that sequence. For example:
708
+ * 'com.myapp.book/1', 'com.myapp.book/2', etc.
709
+ * @public
710
+ */
236
711
  export declare type MigrationId = `${string}/${number}`;
237
712
 
238
- /** @public */
713
+ /**
714
+ * Result type returned by migration operations.
715
+ *
716
+ * Migration operations can either succeed and return the transformed value,
717
+ * or fail with a specific reason. This discriminated union type allows for
718
+ * safe handling of both success and error cases when applying migrations.
719
+ * @public
720
+ */
239
721
  export declare type MigrationResult<T> = {
240
722
  reason: MigrationFailureReason;
241
723
  type: 'error';
@@ -244,7 +726,15 @@ export declare type MigrationResult<T> = {
244
726
  value: T;
245
727
  };
246
728
 
247
- /** @public */
729
+ /**
730
+ * A complete sequence of migrations that can be applied to transform data.
731
+ *
732
+ * A migration sequence represents a series of ordered migrations that belong together,
733
+ * typically for a specific part of your schema. The sequence includes metadata about
734
+ * whether it should be applied retroactively to existing data and contains the actual
735
+ * migration definitions in execution order.
736
+ * @public
737
+ */
248
738
  export declare interface MigrationSequence {
249
739
  sequenceId: string;
250
740
  /**
@@ -262,12 +752,47 @@ export declare interface MigrationSequence {
262
752
 
263
753
  /* Excluded from this release type: parseMigrationId */
264
754
 
265
- /** @public */
755
+ /**
756
+ * Query expression for filtering records by their property values. Maps record property names
757
+ * to matching criteria.
758
+ *
759
+ * @example
760
+ * ```ts
761
+ * // Query for books published after 2020 that are in stock
762
+ * const bookQuery: QueryExpression<Book> = {
763
+ * publishedYear: { gt: 2020 },
764
+ * inStock: { eq: true }
765
+ * }
766
+ *
767
+ * // Query for books not by a specific author
768
+ * const notByAuthor: QueryExpression<Book> = {
769
+ * authorId: { neq: 'author:tolkien' }
770
+ * }
771
+ * ```
772
+ *
773
+ * @public
774
+ */
266
775
  export declare type QueryExpression<R extends object> = {
267
776
  [k in keyof R & string]?: QueryValueMatcher<R[k]>;
268
777
  };
269
778
 
270
- /** @public */
779
+ /**
780
+ * Defines matching criteria for query values. Supports equality, inequality, and greater-than comparisons.
781
+ *
782
+ * @example
783
+ * ```ts
784
+ * // Exact match
785
+ * const exactMatch: QueryValueMatcher<string> = { eq: 'Science Fiction' }
786
+ *
787
+ * // Not equal to
788
+ * const notMatch: QueryValueMatcher<string> = { neq: 'Romance' }
789
+ *
790
+ * // Greater than (numeric values only)
791
+ * const greaterThan: QueryValueMatcher<number> = { gt: 2020 }
792
+ * ```
793
+ *
794
+ * @public
795
+ */
271
796
  export declare type QueryValueMatcher<T> = {
272
797
  eq: T;
273
798
  } | {
@@ -276,10 +801,40 @@ export declare type QueryValueMatcher<T> = {
276
801
  neq: T;
277
802
  };
278
803
 
279
- /** @public */
804
+ /**
805
+ * Extracts the record type from a record ID type.
806
+ *
807
+ * @example
808
+ * ```ts
809
+ * type BookId = RecordId<Book>
810
+ * type BookType = RecordFromId<BookId> // Book
811
+ * ```
812
+ *
813
+ * @public
814
+ */
280
815
  export declare type RecordFromId<K extends RecordId<UnknownRecord>> = K extends RecordId<infer R> ? R : never;
281
816
 
282
- /** @public */
817
+ /**
818
+ * A branded string type that represents a unique identifier for a record.
819
+ * The brand ensures type safety by preventing mixing of IDs between different record types.
820
+ *
821
+ * @example
822
+ * ```ts
823
+ * // Define a Book record
824
+ * interface Book extends BaseRecord<'book', RecordId<Book>> {
825
+ * title: string
826
+ * author: string
827
+ * }
828
+ *
829
+ * const bookId: RecordId<Book> = 'book:abc123' as RecordId<Book>
830
+ * const authorId: RecordId<Author> = 'author:xyz789' as RecordId<Author>
831
+ *
832
+ * // TypeScript prevents mixing different record ID types
833
+ * // bookId = authorId // Type error!
834
+ * ```
835
+ *
836
+ * @public
837
+ */
283
838
  export declare type RecordId<R extends UnknownRecord> = string & {
284
839
  __type__: R;
285
840
  };
@@ -296,13 +851,36 @@ export declare type RecordId<R extends UnknownRecord> = string & {
296
851
  export declare type RecordScope = 'document' | 'presence' | 'session';
297
852
 
298
853
  /**
299
- * A diff describing the changes to a record.
854
+ * A diff describing the changes to records, containing collections of records that were added,
855
+ * updated, or removed. This is the fundamental data structure used throughout the store system
856
+ * to track and communicate changes.
857
+ *
858
+ * @example
859
+ * ```ts
860
+ * const diff: RecordsDiff<Book> = {
861
+ * added: {
862
+ * 'book:1': { id: 'book:1', typeName: 'book', title: 'New Book' }
863
+ * },
864
+ * updated: {
865
+ * 'book:2': [
866
+ * { id: 'book:2', typeName: 'book', title: 'Old Title' }, // from
867
+ * { id: 'book:2', typeName: 'book', title: 'New Title' } // to
868
+ * ]
869
+ * },
870
+ * removed: {
871
+ * 'book:3': { id: 'book:3', typeName: 'book', title: 'Deleted Book' }
872
+ * }
873
+ * }
874
+ * ```
300
875
  *
301
876
  * @public
302
877
  */
303
878
  export declare interface RecordsDiff<R extends UnknownRecord> {
879
+ /** Records that were created, keyed by their ID */
304
880
  added: Record<IdOf<R>, R>;
881
+ /** Records that were modified, keyed by their ID. Each entry contains [from, to] tuple */
305
882
  updated: Record<IdOf<R>, [from: R, to: R]>;
883
+ /** Records that were deleted, keyed by their ID */
306
884
  removed: Record<IdOf<R>, R>;
307
885
  }
308
886
 
@@ -320,13 +898,45 @@ export declare class RecordType<R extends UnknownRecord, RequiredProperties exte
320
898
  * @readonly
321
899
  */
322
900
  readonly typeName: R['typeName'];
901
+ /**
902
+ * Factory function that creates default properties for new records.
903
+ * @public
904
+ */
323
905
  readonly createDefaultProperties: () => Exclude<Omit<R, 'id' | 'typeName'>, RequiredProperties>;
906
+ /**
907
+ * Validator function used to validate records of this type.
908
+ * @public
909
+ */
324
910
  readonly validator: StoreValidator<R>;
911
+ /**
912
+ * Optional configuration specifying which record properties are ephemeral.
913
+ * Ephemeral properties are not included in snapshots or synchronization.
914
+ * @public
915
+ */
325
916
  readonly ephemeralKeys?: {
326
917
  readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean;
327
918
  };
919
+ /**
920
+ * Set of property names that are marked as ephemeral for efficient lookup.
921
+ * @public
922
+ */
328
923
  readonly ephemeralKeySet: ReadonlySet<string>;
924
+ /**
925
+ * The scope that determines how records of this type are persisted and synchronized.
926
+ * @public
927
+ */
329
928
  readonly scope: RecordScope;
929
+ /**
930
+ * Creates a new RecordType instance.
931
+ *
932
+ * typeName - The unique type name for records created by this RecordType
933
+ * config - Configuration object for the RecordType
934
+ * - createDefaultProperties - Function that returns default properties for new records
935
+ * - validator - Optional validator function for record validation
936
+ * - scope - Optional scope determining persistence behavior (defaults to 'document')
937
+ * - ephemeralKeys - Optional mapping of property names to ephemeral status
938
+ * @public
939
+ */
330
940
  constructor(
331
941
  /**
332
942
  * The unique type associated with this record.
@@ -343,17 +953,40 @@ export declare class RecordType<R extends UnknownRecord, RequiredProperties exte
343
953
  readonly validator?: StoreValidator<R>;
344
954
  });
345
955
  /**
346
- * Create a new record of this type.
956
+ * Creates a new record of this type with the given properties.
957
+ *
958
+ * Properties are merged with default properties from the RecordType configuration.
959
+ * If no id is provided, a unique id will be generated automatically.
347
960
  *
348
- * @param properties - The properties of the record.
349
- * @returns The new record.
961
+ * @example
962
+ * ```ts
963
+ * const book = Book.create({
964
+ * title: 'The Great Gatsby',
965
+ * author: 'F. Scott Fitzgerald'
966
+ * })
967
+ * // Result: { id: 'book:abc123', typeName: 'book', title: 'The Great Gatsby', author: 'F. Scott Fitzgerald', inStock: true }
968
+ * ```
969
+ *
970
+ * @param properties - The properties for the new record, including both required and optional fields
971
+ * @returns The newly created record with generated id and typeName
972
+ * @public
350
973
  */
351
974
  create(properties: Expand<Pick<R, RequiredProperties> & Omit<Partial<R>, RequiredProperties>>): R;
352
975
  /**
353
- * Clone a record of this type.
976
+ * Creates a deep copy of an existing record with a new unique id.
977
+ *
978
+ * This method performs a deep clone of all properties while generating a fresh id,
979
+ * making it useful for duplicating records without id conflicts.
980
+ *
981
+ * @example
982
+ * ```ts
983
+ * const originalBook = Book.create({ title: '1984', author: 'George Orwell' })
984
+ * const duplicatedBook = Book.clone(originalBook)
985
+ * // duplicatedBook has same properties but different id
986
+ * ```
354
987
  *
355
- * @param record - The record to clone.
356
- * @returns The cloned record.
988
+ * @param record - The record to clone
989
+ * @returns A new record with the same properties but a different id
357
990
  * @public
358
991
  */
359
992
  clone(record: R): R;
@@ -371,36 +1004,58 @@ export declare class RecordType<R extends UnknownRecord, RequiredProperties exte
371
1004
  */
372
1005
  createId(customUniquePart?: string): IdOf<R>;
373
1006
  /**
374
- * Takes an id like `user:123` and returns the part after the colon `123`
1007
+ * Extracts the unique identifier part from a full record id.
375
1008
  *
376
- * @param id - The id
377
- * @returns
1009
+ * Record ids have the format `typeName:uniquePart`. This method returns just the unique part.
1010
+ *
1011
+ * @example
1012
+ * ```ts
1013
+ * const bookId = Book.createId() // 'book:abc123'
1014
+ * const uniquePart = Book.parseId(bookId) // 'abc123'
1015
+ * ```
1016
+ *
1017
+ * @param id - The full record id to parse
1018
+ * @returns The unique identifier portion after the colon
1019
+ * @throws Error if the id is not valid for this record type
1020
+ * @public
378
1021
  */
379
1022
  parseId(id: IdOf<R>): string;
380
1023
  /**
381
- * Check whether a record is an instance of this record type.
1024
+ * Type guard that checks whether a record belongs to this RecordType.
382
1025
  *
383
- * @example
1026
+ * This method performs a runtime check by comparing the record's typeName
1027
+ * against this RecordType's typeName.
384
1028
  *
1029
+ * @example
385
1030
  * ```ts
386
- * const result = recordType.isInstance(someRecord)
1031
+ * if (Book.isInstance(someRecord)) {
1032
+ * // someRecord is now typed as a book record
1033
+ * console.log(someRecord.title)
1034
+ * }
387
1035
  * ```
388
1036
  *
389
- * @param record - The record to check.
390
- * @returns Whether the record is an instance of this record type.
1037
+ * @param record - The record to check, may be undefined
1038
+ * @returns True if the record is an instance of this record type
1039
+ * @public
391
1040
  */
392
1041
  isInstance(record?: UnknownRecord): record is R;
393
1042
  /**
394
- * Check whether an id is an id of this type.
1043
+ * Type guard that checks whether an id string belongs to this RecordType.
395
1044
  *
396
- * @example
1045
+ * Validates that the id starts with this RecordType's typeName followed by a colon.
1046
+ * This is more efficient than parsing the full id when you only need to verify the type.
397
1047
  *
1048
+ * @example
398
1049
  * ```ts
399
- * const result = recordType.isIn('someId')
1050
+ * if (Book.isId(someId)) {
1051
+ * // someId is now typed as IdOf<BookRecord>
1052
+ * const book = store.get(someId)
1053
+ * }
400
1054
  * ```
401
1055
  *
402
- * @param id - The id to check.
403
- * @returns Whether the id is an id of this type.
1056
+ * @param id - The id string to check, may be undefined
1057
+ * @returns True if the id belongs to this record type
1058
+ * @public
404
1059
  */
405
1060
  isId(id?: string): id is IdOf<R>;
406
1061
  /**
@@ -419,28 +1074,156 @@ export declare class RecordType<R extends UnknownRecord, RequiredProperties exte
419
1074
  */
420
1075
  withDefaultProperties<DefaultProps extends Omit<Partial<R>, 'id' | 'typeName'>>(createDefaultProperties: () => DefaultProps): RecordType<R, Exclude<RequiredProperties, keyof DefaultProps>>;
421
1076
  /**
422
- * Check that the passed in record passes the validations for this type. Returns its input
423
- * correctly typed if it does, but throws an error otherwise.
1077
+ * Validates a record against this RecordType's validator and returns it with proper typing.
1078
+ *
1079
+ * This method runs the configured validator function and throws an error if validation fails.
1080
+ * If a previous version of the record is provided, it may use optimized validation.
1081
+ *
1082
+ * @example
1083
+ * ```ts
1084
+ * try {
1085
+ * const validBook = Book.validate(untrustedData)
1086
+ * // validBook is now properly typed and validated
1087
+ * } catch (error) {
1088
+ * console.log('Validation failed:', error.message)
1089
+ * }
1090
+ * ```
1091
+ *
1092
+ * @param record - The unknown record data to validate
1093
+ * @param recordBefore - Optional previous version for optimized validation
1094
+ * @returns The validated and properly typed record
1095
+ * @throws Error if validation fails
1096
+ * @public
424
1097
  */
425
1098
  validate(record: unknown, recordBefore?: R): R;
426
1099
  }
427
1100
 
428
- /** @public */
1101
+ /**
1102
+ * Creates the inverse of a RecordsDiff, effectively reversing all changes.
1103
+ * Added records become removed, removed records become added, and updated records
1104
+ * have their from/to values swapped. This is useful for implementing undo operations.
1105
+ *
1106
+ * @param diff - The diff to reverse
1107
+ * @returns A new RecordsDiff that represents the inverse of the input diff
1108
+ * @example
1109
+ * ```ts
1110
+ * const originalDiff: RecordsDiff<Book> = {
1111
+ * added: { 'book:1': newBook },
1112
+ * updated: { 'book:2': [oldBook, updatedBook] },
1113
+ * removed: { 'book:3': deletedBook }
1114
+ * }
1115
+ *
1116
+ * const reversedDiff = reverseRecordsDiff(originalDiff)
1117
+ * // Result: {
1118
+ * // added: { 'book:3': deletedBook },
1119
+ * // updated: { 'book:2': [updatedBook, oldBook] },
1120
+ * // removed: { 'book:1': newBook }
1121
+ * // }
1122
+ * ```
1123
+ *
1124
+ * @public
1125
+ */
429
1126
  export declare function reverseRecordsDiff(diff: RecordsDiff<any>): RecordsDiff<any>;
430
1127
 
431
- /** @public */
1128
+ /**
1129
+ * A reactive computed index that provides efficient lookups of records by property values.
1130
+ * Returns a computed value containing an RSIndexMap with diffs for change tracking.
1131
+ *
1132
+ * @example
1133
+ * ```ts
1134
+ * // Create an index on book authors
1135
+ * const authorIndex: RSIndex<Book, 'authorId'> = store.query.index('book', 'authorId')
1136
+ *
1137
+ * // Get all books by a specific author
1138
+ * const leguinBooks = authorIndex.get().get('author:leguin')
1139
+ * ```
1140
+ *
1141
+ * @public
1142
+ */
432
1143
  export declare type RSIndex<R extends UnknownRecord, Property extends string & keyof R = string & keyof R> = Computed<RSIndexMap<R, Property>, RSIndexDiff<R, Property>>;
433
1144
 
434
- /** @public */
1145
+ /**
1146
+ * A type representing the diff of changes to a reactive store index.
1147
+ * Maps property values to the collection differences for record IDs that have that property value.
1148
+ *
1149
+ * @example
1150
+ * ```ts
1151
+ * // For an index on book titles, the diff might look like:
1152
+ * const titleIndexDiff: RSIndexDiff<Book, 'title'> = new Map([
1153
+ * ['The Lathe of Heaven', { added: new Set(['book:1']), removed: new Set() }],
1154
+ * ['Animal Farm', { added: new Set(), removed: new Set(['book:2']) }]
1155
+ * ])
1156
+ * ```
1157
+ *
1158
+ * @public
1159
+ */
435
1160
  export declare type RSIndexDiff<R extends UnknownRecord, Property extends string & keyof R = string & keyof R> = Map<R[Property], CollectionDiff<IdOf<R>>>;
436
1161
 
437
- /** @public */
1162
+ /**
1163
+ * A type representing a reactive store index as a map from property values to sets of record IDs.
1164
+ * This is used to efficiently look up records by a specific property value.
1165
+ *
1166
+ * @example
1167
+ * ```ts
1168
+ * // Index mapping book titles to the IDs of books with that title
1169
+ * const titleIndex: RSIndexMap<Book, 'title'> = new Map([
1170
+ * ['The Lathe of Heaven', new Set(['book:1'])],
1171
+ * ['Animal Farm', new Set(['book:2', 'book:3'])]
1172
+ * ])
1173
+ * ```
1174
+ *
1175
+ * @public
1176
+ */
438
1177
  export declare type RSIndexMap<R extends UnknownRecord, Property extends string & keyof R = string & keyof R> = Map<R[Property], Set<IdOf<R>>>;
439
1178
 
440
- /** @public */
1179
+ /**
1180
+ * Union type representing all supported serialized schema formats.
1181
+ *
1182
+ * This type allows the store to handle both legacy (V1) and current (V2)
1183
+ * schema formats during deserialization and migration.
1184
+ *
1185
+ * @example
1186
+ * ```ts
1187
+ * function handleSchema(schema: SerializedSchema) {
1188
+ * if (schema.schemaVersion === 1) {
1189
+ * // Handle V1 format
1190
+ * console.log('Store version:', schema.storeVersion)
1191
+ * } else {
1192
+ * // Handle V2 format
1193
+ * console.log('Sequences:', schema.sequences)
1194
+ * }
1195
+ * }
1196
+ * ```
1197
+ *
1198
+ * @public
1199
+ */
441
1200
  export declare type SerializedSchema = SerializedSchemaV1 | SerializedSchemaV2;
442
1201
 
443
- /** @public */
1202
+ /**
1203
+ * Version 1 format for serialized store schema information.
1204
+ *
1205
+ * This is the legacy format used before schema version 2. Version 1 schemas
1206
+ * separate store-level versioning from record-level versioning, and support
1207
+ * subtypes for complex record types like shapes.
1208
+ *
1209
+ * @example
1210
+ * ```ts
1211
+ * const schemaV1: SerializedSchemaV1 = {
1212
+ * schemaVersion: 1,
1213
+ * storeVersion: 2,
1214
+ * recordVersions: {
1215
+ * book: { version: 3 },
1216
+ * shape: {
1217
+ * version: 2,
1218
+ * subTypeVersions: { rectangle: 1, circle: 2 },
1219
+ * subTypeKey: 'type'
1220
+ * }
1221
+ * }
1222
+ * }
1223
+ * ```
1224
+ *
1225
+ * @public
1226
+ */
444
1227
  export declare interface SerializedSchemaV1 {
445
1228
  /** Schema version is the version for this type you're looking at right now */
446
1229
  schemaVersion: 1;
@@ -459,7 +1242,28 @@ export declare interface SerializedSchemaV1 {
459
1242
  }>;
460
1243
  }
461
1244
 
462
- /** @public */
1245
+ /**
1246
+ * Version 2 format for serialized store schema information.
1247
+ *
1248
+ * This is the current format that uses a unified sequence-based approach
1249
+ * for tracking versions across all migration sequences. Each sequence ID
1250
+ * maps to the latest version number for that sequence.
1251
+ *
1252
+ * @example
1253
+ * ```ts
1254
+ * const schemaV2: SerializedSchemaV2 = {
1255
+ * schemaVersion: 2,
1256
+ * sequences: {
1257
+ * 'com.tldraw.store': 3,
1258
+ * 'com.tldraw.book': 2,
1259
+ * 'com.tldraw.shape': 4,
1260
+ * 'com.tldraw.shape.rectangle': 1
1261
+ * }
1262
+ * }
1263
+ * ```
1264
+ *
1265
+ * @public
1266
+ */
463
1267
  export declare interface SerializedSchemaV2 {
464
1268
  schemaVersion: 2;
465
1269
  sequences: {
@@ -469,17 +1273,53 @@ export declare interface SerializedSchemaV2 {
469
1273
 
470
1274
  /**
471
1275
  * A serialized snapshot of the record store's values.
1276
+ * This is a plain JavaScript object that can be saved to storage or transmitted over the network.
1277
+ *
1278
+ * @example
1279
+ * ```ts
1280
+ * const serialized: SerializedStore<Book> = {
1281
+ * 'book:123': { id: 'book:123', typeName: 'book', title: 'The Lathe of Heaven' },
1282
+ * 'book:456': { id: 'book:456', typeName: 'book', title: 'The Left Hand of Darkness' }
1283
+ * }
1284
+ * ```
472
1285
  *
473
1286
  * @public
474
1287
  */
475
1288
  export declare type SerializedStore<R extends UnknownRecord> = Record<IdOf<R>, R>;
476
1289
 
477
1290
  /**
478
- * Squash a collection of diffs into a single diff.
1291
+ * Combines multiple RecordsDiff objects into a single consolidated diff.
1292
+ * This function intelligently merges changes, handling cases where the same record
1293
+ * is modified multiple times across different diffs. For example, if a record is
1294
+ * added in one diff and then updated in another, the result will show it as added
1295
+ * with the final state.
1296
+ *
1297
+ * @param diffs - An array of diffs to combine into a single diff
1298
+ * @param options - Configuration options for the squashing operation
1299
+ * - mutateFirstDiff - If true, modifies the first diff in place instead of creating a new one
1300
+ * @returns A single diff that represents the cumulative effect of all input diffs
1301
+ * @example
1302
+ * ```ts
1303
+ * const diff1: RecordsDiff<Book> = {
1304
+ * added: { 'book:1': { id: 'book:1', title: 'New Book' } },
1305
+ * updated: {},
1306
+ * removed: {}
1307
+ * }
1308
+ *
1309
+ * const diff2: RecordsDiff<Book> = {
1310
+ * added: {},
1311
+ * updated: { 'book:1': [{ id: 'book:1', title: 'New Book' }, { id: 'book:1', title: 'Updated Title' }] },
1312
+ * removed: {}
1313
+ * }
1314
+ *
1315
+ * const squashed = squashRecordDiffs([diff1, diff2])
1316
+ * // Result: {
1317
+ * // added: { 'book:1': { id: 'book:1', title: 'Updated Title' } },
1318
+ * // updated: {},
1319
+ * // removed: {}
1320
+ * // }
1321
+ * ```
479
1322
  *
480
- * @param diffs - An array of diffs to squash.
481
- * @param options - An optional object with a `mutateFirstDiff` property. If `mutateFirstDiff` is true, the first diff in the array will be mutated in-place.
482
- * @returns A single diff that represents the squashed diffs.
483
1323
  * @public
484
1324
  */
485
1325
  export declare function squashRecordDiffs<T extends UnknownRecord>(diffs: RecordsDiff<T>[], options?: {
@@ -488,19 +1328,59 @@ export declare function squashRecordDiffs<T extends UnknownRecord>(diffs: Record
488
1328
 
489
1329
  /* Excluded from this release type: squashRecordDiffsMutable */
490
1330
 
491
- /** @public */
1331
+ /**
1332
+ * Declares dependencies for migrations without being a migration itself.
1333
+ *
1334
+ * This interface allows you to specify that future migrations in a sequence depend on
1335
+ * migrations from other sequences, without defining an actual migration transformation.
1336
+ * It's used to establish cross-sequence dependencies in the migration graph.
1337
+ * @public
1338
+ */
492
1339
  export declare interface StandaloneDependsOn {
493
1340
  readonly dependsOn: readonly MigrationId[];
494
1341
  }
495
1342
 
496
1343
  /**
497
- * A store of records.
1344
+ * A reactive store that manages collections of typed records.
1345
+ *
1346
+ * The Store is the central container for your application's data, providing:
1347
+ * - Reactive state management with automatic updates
1348
+ * - Type-safe record operations
1349
+ * - History tracking and change notifications
1350
+ * - Schema validation and migrations
1351
+ * - Side effects and business logic hooks
1352
+ * - Efficient querying and indexing
1353
+ *
1354
+ * @example
1355
+ * ```ts
1356
+ * // Create a store with schema
1357
+ * const schema = StoreSchema.create({
1358
+ * book: Book,
1359
+ * author: Author
1360
+ * })
1361
+ *
1362
+ * const store = new Store({
1363
+ * schema,
1364
+ * props: {}
1365
+ * })
1366
+ *
1367
+ * // Add records
1368
+ * const book = Book.create({ title: 'The Lathe of Heaven', author: 'Le Guin' })
1369
+ * store.put([book])
1370
+ *
1371
+ * // Listen to changes
1372
+ * store.listen((entry) => {
1373
+ * console.log('Changes:', entry.changes)
1374
+ * })
1375
+ * ```
498
1376
  *
499
1377
  * @public
500
1378
  */
501
1379
  export declare class Store<R extends UnknownRecord = UnknownRecord, Props = unknown> {
502
1380
  /**
503
- * The random id of the store.
1381
+ * The unique identifier of the store instance.
1382
+ *
1383
+ * @public
504
1384
  */
505
1385
  readonly id: string;
506
1386
  /* Excluded from this release type: records */
@@ -512,7 +1392,19 @@ export declare class Store<R extends UnknownRecord = UnknownRecord, Props = unkn
512
1392
  */
513
1393
  readonly history: Atom<number, RecordsDiff<R>>;
514
1394
  /**
515
- * A StoreQueries instance for this store.
1395
+ * Reactive queries and indexes for efficiently accessing store data.
1396
+ * Provides methods for filtering, indexing, and subscribing to subsets of records.
1397
+ *
1398
+ * @example
1399
+ * ```ts
1400
+ * // Create an index by a property
1401
+ * const booksByAuthor = store.query.index('book', 'author')
1402
+ *
1403
+ * // Get records matching criteria
1404
+ * const inStockBooks = store.query.records('book', () => ({
1405
+ * inStock: { eq: true }
1406
+ * }))
1407
+ * ```
516
1408
  *
517
1409
  * @public
518
1410
  * @readonly
@@ -522,22 +1414,64 @@ export declare class Store<R extends UnknownRecord = UnknownRecord, Props = unkn
522
1414
  /* Excluded from this release type: historyAccumulator */
523
1415
  /* Excluded from this release type: historyReactor */
524
1416
  /* Excluded from this release type: cancelHistoryReactor */
1417
+ /**
1418
+ * The schema that defines the structure and validation rules for records in this store.
1419
+ *
1420
+ * @public
1421
+ */
525
1422
  readonly schema: StoreSchema<R, Props>;
1423
+ /**
1424
+ * Custom properties associated with this store instance.
1425
+ *
1426
+ * @public
1427
+ */
526
1428
  readonly props: Props;
1429
+ /**
1430
+ * A mapping of record scopes to the set of record type names that belong to each scope.
1431
+ * Used to filter records by their persistence and synchronization behavior.
1432
+ *
1433
+ * @public
1434
+ */
527
1435
  readonly scopedTypes: {
528
1436
  readonly [K in RecordScope]: ReadonlySet<R['typeName']>;
529
1437
  };
1438
+ /**
1439
+ * Side effects manager that handles lifecycle events for record operations.
1440
+ * Allows registration of callbacks for create, update, delete, and validation events.
1441
+ *
1442
+ * @example
1443
+ * ```ts
1444
+ * store.sideEffects.registerAfterCreateHandler('book', (book) => {
1445
+ * console.log('Book created:', book.title)
1446
+ * })
1447
+ * ```
1448
+ *
1449
+ * @public
1450
+ */
530
1451
  readonly sideEffects: StoreSideEffects<R>;
1452
+ /**
1453
+ * Creates a new Store instance.
1454
+ *
1455
+ * @example
1456
+ * ```ts
1457
+ * const store = new Store({
1458
+ * schema: StoreSchema.create({ book: Book }),
1459
+ * props: { appName: 'MyLibrary' },
1460
+ * initialData: savedData
1461
+ * })
1462
+ * ```
1463
+ *
1464
+ * @param config - Configuration object for the store
1465
+ */
531
1466
  constructor(config: {
532
- /**
533
- * A map of validators for each record type. A record's validator will be called when the record
534
- * is created or updated. It should throw an error if the record is invalid.
535
- */
1467
+ /** Custom properties for the store instance */
1468
+ props: Props;
1469
+ /** Optional unique identifier for the store */
1470
+ id?: string;
1471
+ /** The schema defining record types, validation, and migrations */
536
1472
  schema: StoreSchema<R, Props>;
537
- /** The store's initial data. */
1473
+ /** The store's initial data to populate on creation */
538
1474
  initialData?: SerializedStore<R>;
539
- id?: string;
540
- props: Props;
541
1475
  });
542
1476
  _flushHistory(): void;
543
1477
  dispose(): void;
@@ -560,119 +1494,238 @@ export declare class Store<R extends UnknownRecord = UnknownRecord, Props = unkn
560
1494
  private updateHistory;
561
1495
  validate(phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord'): void;
562
1496
  /**
563
- * Add some records to the store. It's an error if they already exist.
1497
+ * Add or update records in the store. If a record with the same ID already exists, it will be updated.
1498
+ * Otherwise, a new record will be created.
1499
+ *
1500
+ * @example
1501
+ * ```ts
1502
+ * // Add new records
1503
+ * const book = Book.create({ title: 'Lathe Of Heaven', author: 'Le Guin' })
1504
+ * store.put([book])
564
1505
  *
565
- * @param records - The records to add.
566
- * @param phaseOverride - The phase override.
1506
+ * // Update existing record
1507
+ * store.put([{ ...book, title: 'The Lathe of Heaven' }])
1508
+ * ```
1509
+ *
1510
+ * @param records - The records to add or update
1511
+ * @param phaseOverride - Override the validation phase (used internally)
567
1512
  * @public
568
1513
  */
569
1514
  put(records: R[], phaseOverride?: 'initialize'): void;
570
1515
  /**
571
- * Remove some records from the store via their ids.
1516
+ * Remove records from the store by their IDs.
1517
+ *
1518
+ * @example
1519
+ * ```ts
1520
+ * // Remove a single record
1521
+ * store.remove([book.id])
1522
+ *
1523
+ * // Remove multiple records
1524
+ * store.remove([book1.id, book2.id, book3.id])
1525
+ * ```
572
1526
  *
573
- * @param ids - The ids of the records to remove.
1527
+ * @param ids - The IDs of the records to remove
574
1528
  * @public
575
1529
  */
576
1530
  remove(ids: IdOf<R>[]): void;
577
1531
  /**
578
- * Get the value of a store record by its id.
1532
+ * Get a record by its ID. This creates a reactive subscription to the record.
1533
+ *
1534
+ * @example
1535
+ * ```ts
1536
+ * const book = store.get(bookId)
1537
+ * if (book) {
1538
+ * console.log(book.title)
1539
+ * }
1540
+ * ```
579
1541
  *
580
- * @param id - The id of the record to get.
1542
+ * @param id - The ID of the record to get
1543
+ * @returns The record if it exists, undefined otherwise
581
1544
  * @public
582
1545
  */
583
1546
  get<K extends IdOf<R>>(id: K): RecordFromId<K> | undefined;
584
1547
  /**
585
- * Get the value of a store record by its id without updating its epoch.
1548
+ * Get a record by its ID without creating a reactive subscription.
1549
+ * Use this when you need to access a record but don't want reactive updates.
1550
+ *
1551
+ * @example
1552
+ * ```ts
1553
+ * // Won't trigger reactive updates when this record changes
1554
+ * const book = store.unsafeGetWithoutCapture(bookId)
1555
+ * ```
586
1556
  *
587
- * @param id - The id of the record to get.
1557
+ * @param id - The ID of the record to get
1558
+ * @returns The record if it exists, undefined otherwise
588
1559
  * @public
589
1560
  */
590
1561
  unsafeGetWithoutCapture<K extends IdOf<R>>(id: K): RecordFromId<K> | undefined;
591
1562
  /**
592
- * Creates a JSON payload from the record store.
1563
+ * Serialize the store's records to a plain JavaScript object.
1564
+ * Only includes records matching the specified scope.
1565
+ *
1566
+ * @example
1567
+ * ```ts
1568
+ * // Serialize only document records (default)
1569
+ * const documentData = store.serialize('document')
1570
+ *
1571
+ * // Serialize all records
1572
+ * const allData = store.serialize('all')
1573
+ * ```
593
1574
  *
594
- * @param scope - The scope of records to serialize. Defaults to 'document'.
595
- * @returns The record store snapshot as a JSON payload.
1575
+ * @param scope - The scope of records to serialize. Defaults to 'document'
1576
+ * @returns The serialized store data
1577
+ * @public
596
1578
  */
597
1579
  serialize(scope?: 'all' | RecordScope): SerializedStore<R>;
598
1580
  /**
599
1581
  * Get a serialized snapshot of the store and its schema.
1582
+ * This includes both the data and schema information needed for proper migration.
600
1583
  *
1584
+ * @example
601
1585
  * ```ts
602
1586
  * const snapshot = store.getStoreSnapshot()
603
- * store.loadStoreSnapshot(snapshot)
604
- * ```
1587
+ * localStorage.setItem('myApp', JSON.stringify(snapshot))
605
1588
  *
606
- * @param scope - The scope of records to serialize. Defaults to 'document'.
1589
+ * // Later...
1590
+ * const saved = JSON.parse(localStorage.getItem('myApp'))
1591
+ * store.loadStoreSnapshot(saved)
1592
+ * ```
607
1593
  *
1594
+ * @param scope - The scope of records to serialize. Defaults to 'document'
1595
+ * @returns A snapshot containing both store data and schema information
608
1596
  * @public
609
1597
  */
610
1598
  getStoreSnapshot(scope?: 'all' | RecordScope): StoreSnapshot<R>;
611
1599
  /**
612
- * Migrate a serialized snapshot of the store and its schema.
1600
+ * Migrate a serialized snapshot to the current schema version.
1601
+ * This applies any necessary migrations to bring old data up to date.
613
1602
  *
1603
+ * @example
614
1604
  * ```ts
615
- * const snapshot = store.getStoreSnapshot()
616
- * store.migrateSnapshot(snapshot)
1605
+ * const oldSnapshot = JSON.parse(localStorage.getItem('myApp'))
1606
+ * const migratedSnapshot = store.migrateSnapshot(oldSnapshot)
617
1607
  * ```
618
1608
  *
619
- * @param snapshot - The snapshot to load.
1609
+ * @param snapshot - The snapshot to migrate
1610
+ * @returns The migrated snapshot with current schema version
1611
+ * @throws Error if migration fails
620
1612
  * @public
621
1613
  */
622
1614
  migrateSnapshot(snapshot: StoreSnapshot<R>): StoreSnapshot<R>;
623
1615
  /**
624
- * Load a serialized snapshot.
1616
+ * Load a serialized snapshot into the store, replacing all current data.
1617
+ * The snapshot will be automatically migrated to the current schema version if needed.
625
1618
  *
1619
+ * @example
626
1620
  * ```ts
627
- * const snapshot = store.getStoreSnapshot()
1621
+ * const snapshot = JSON.parse(localStorage.getItem('myApp'))
628
1622
  * store.loadStoreSnapshot(snapshot)
629
1623
  * ```
630
1624
  *
631
- * @param snapshot - The snapshot to load.
1625
+ * @param snapshot - The snapshot to load
1626
+ * @throws Error if migration fails or snapshot is invalid
632
1627
  * @public
633
1628
  */
634
1629
  loadStoreSnapshot(snapshot: StoreSnapshot<R>): void;
635
1630
  /**
636
- * Get an array of all values in the store.
1631
+ * Get an array of all records in the store.
1632
+ *
1633
+ * @example
1634
+ * ```ts
1635
+ * const allRecords = store.allRecords()
1636
+ * const books = allRecords.filter(r => r.typeName === 'book')
1637
+ * ```
637
1638
  *
638
- * @returns An array of all values in the store.
1639
+ * @returns An array containing all records in the store
639
1640
  * @public
640
1641
  */
641
1642
  allRecords(): R[];
642
1643
  /**
643
- * Removes all records from the store.
1644
+ * Remove all records from the store.
1645
+ *
1646
+ * @example
1647
+ * ```ts
1648
+ * store.clear()
1649
+ * console.log(store.allRecords().length) // 0
1650
+ * ```
644
1651
  *
645
1652
  * @public
646
1653
  */
647
1654
  clear(): void;
648
1655
  /**
649
- * Update a record. To update multiple records at once, use the `update` method of the
650
- * `TypedStore` class.
1656
+ * Update a single record using an updater function. To update multiple records at once,
1657
+ * use the `update` method of the `TypedStore` class.
1658
+ *
1659
+ * @example
1660
+ * ```ts
1661
+ * store.update(book.id, (book) => ({
1662
+ * ...book,
1663
+ * title: 'Updated Title'
1664
+ * }))
1665
+ * ```
651
1666
  *
652
- * @param id - The id of the record to update.
653
- * @param updater - A function that updates the record.
1667
+ * @param id - The ID of the record to update
1668
+ * @param updater - A function that receives the current record and returns the updated record
1669
+ * @public
654
1670
  */
655
1671
  update<K extends IdOf<R>>(id: K, updater: (record: RecordFromId<K>) => RecordFromId<K>): void;
656
1672
  /**
657
- * Get whether the record store has a id.
1673
+ * Check whether a record with the given ID exists in the store.
1674
+ *
1675
+ * @example
1676
+ * ```ts
1677
+ * if (store.has(bookId)) {
1678
+ * console.log('Book exists!')
1679
+ * }
1680
+ * ```
658
1681
  *
659
- * @param id - The id of the record to check.
1682
+ * @param id - The ID of the record to check
1683
+ * @returns True if the record exists, false otherwise
660
1684
  * @public
661
1685
  */
662
1686
  has<K extends IdOf<R>>(id: K): boolean;
663
1687
  /**
664
- * Add a new listener to the store.
1688
+ * Add a listener that will be called when the store changes.
1689
+ * Returns a function to remove the listener.
1690
+ *
1691
+ * @example
1692
+ * ```ts
1693
+ * const removeListener = store.listen((entry) => {
1694
+ * console.log('Changes:', entry.changes)
1695
+ * console.log('Source:', entry.source)
1696
+ * })
1697
+ *
1698
+ * // Listen only to user changes to document records
1699
+ * const removeDocumentListener = store.listen(
1700
+ * (entry) => console.log('Document changed:', entry),
1701
+ * { source: 'user', scope: 'document' }
1702
+ * )
1703
+ *
1704
+ * // Later, remove the listener
1705
+ * removeListener()
1706
+ * ```
665
1707
  *
666
- * @param onHistory - The listener to call when the store updates.
667
- * @param filters - Filters to apply to the listener.
668
- * @returns A function to remove the listener.
1708
+ * @param onHistory - The listener function to call when changes occur
1709
+ * @param filters - Optional filters to control when the listener is called
1710
+ * @returns A function that removes the listener when called
1711
+ * @public
669
1712
  */
670
1713
  listen(onHistory: StoreListener<R>, filters?: Partial<StoreListenerFilters>): () => void;
671
1714
  private isMergingRemoteChanges;
672
1715
  /**
673
- * Merge changes from a remote source
1716
+ * Merge changes from a remote source. Changes made within the provided function
1717
+ * will be marked with source 'remote' instead of 'user'.
1718
+ *
1719
+ * @example
1720
+ * ```ts
1721
+ * // Changes from sync/collaboration
1722
+ * store.mergeRemoteChanges(() => {
1723
+ * store.put(remoteRecords)
1724
+ * store.remove(deletedIds)
1725
+ * })
1726
+ * ```
674
1727
  *
675
- * @param fn - A function that merges the external changes.
1728
+ * @param fn - A function that applies the remote changes
676
1729
  * @public
677
1730
  */
678
1731
  mergeRemoteChanges(fn: () => void): void;
@@ -715,85 +1768,337 @@ export declare class Store<R extends UnknownRecord = UnknownRecord, Props = unkn
715
1768
  /* Excluded from this release type: addHistoryInterceptor */
716
1769
  }
717
1770
 
718
- /** @public */
1771
+ /**
1772
+ * Handler function called after a record has been successfully updated in the store.
1773
+ * Use this for side effects that should happen after record changes, such as
1774
+ * updating related records or maintaining consistency constraints.
1775
+ *
1776
+ * @param prev - The previous version of the record
1777
+ * @param next - The new version of the record that was stored
1778
+ * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
1779
+ *
1780
+ * @example
1781
+ * ```ts
1782
+ * const handler: StoreAfterChangeHandler<ShapeRecord> = (prev, next, source) => {
1783
+ * // Update connected arrows when a shape moves
1784
+ * if (prev.x !== next.x || prev.y !== next.y) {
1785
+ * updateConnectedArrows(next.id)
1786
+ * }
1787
+ * }
1788
+ * ```
1789
+ *
1790
+ * @public
1791
+ */
719
1792
  export declare type StoreAfterChangeHandler<R extends UnknownRecord> = (prev: R, next: R, source: 'remote' | 'user') => void;
720
1793
 
721
- /** @public */
1794
+ /**
1795
+ * Handler function called after a record has been successfully created in the store.
1796
+ * Use this for side effects that should happen after record creation, such as updating
1797
+ * related records or triggering notifications.
1798
+ *
1799
+ * @param record - The record that was created
1800
+ * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
1801
+ *
1802
+ * @example
1803
+ * ```ts
1804
+ * const handler: StoreAfterCreateHandler<BookRecord> = (book, source) => {
1805
+ * if (source === 'user') {
1806
+ * console.log(`New book added: ${book.title}`)
1807
+ * updateAuthorBookCount(book.authorId)
1808
+ * }
1809
+ * }
1810
+ * ```
1811
+ *
1812
+ * @public
1813
+ */
722
1814
  export declare type StoreAfterCreateHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => void;
723
1815
 
724
- /** @public */
1816
+ /**
1817
+ * Handler function called after a record has been successfully deleted from the store.
1818
+ * Use this for cleanup operations and maintaining referential integrity.
1819
+ *
1820
+ * @param record - The record that was deleted
1821
+ * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
1822
+ *
1823
+ * @example
1824
+ * ```ts
1825
+ * const handler: StoreAfterDeleteHandler<ShapeRecord> = (shape, source) => {
1826
+ * // Clean up arrows that were connected to this shape
1827
+ * const connectedArrows = findArrowsConnectedTo(shape.id)
1828
+ * store.remove(connectedArrows.map(arrow => arrow.id))
1829
+ * }
1830
+ * ```
1831
+ *
1832
+ * @public
1833
+ */
725
1834
  export declare type StoreAfterDeleteHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => void;
726
1835
 
727
- /** @public */
1836
+ /**
1837
+ * Handler function called before a record is updated in the store.
1838
+ * The handler receives the current and new versions of the record and can return
1839
+ * a modified version or the original to prevent the change.
1840
+ *
1841
+ * @param prev - The current version of the record in the store
1842
+ * @param next - The proposed new version of the record
1843
+ * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
1844
+ * @returns The record version to actually store (may be modified or the original to block change)
1845
+ *
1846
+ * @example
1847
+ * ```ts
1848
+ * const handler: StoreBeforeChangeHandler<ShapeRecord> = (prev, next, source) => {
1849
+ * // Prevent shapes from being moved outside the canvas bounds
1850
+ * if (next.x < 0 || next.y < 0) {
1851
+ * return prev // Block the change
1852
+ * }
1853
+ * return next
1854
+ * }
1855
+ * ```
1856
+ *
1857
+ * @public
1858
+ */
728
1859
  export declare type StoreBeforeChangeHandler<R extends UnknownRecord> = (prev: R, next: R, source: 'remote' | 'user') => R;
729
1860
 
730
- /** @public */
1861
+ /**
1862
+ * Handler function called before a record is created in the store.
1863
+ * The handler receives the record to be created and can return a modified version.
1864
+ * Use this to validate, transform, or modify records before they are added to the store.
1865
+ *
1866
+ * @param record - The record about to be created
1867
+ * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
1868
+ * @returns The record to actually create (may be modified)
1869
+ *
1870
+ * @example
1871
+ * ```ts
1872
+ * const handler: StoreBeforeCreateHandler<MyRecord> = (record, source) => {
1873
+ * // Ensure all user-created records have a timestamp
1874
+ * if (source === 'user' && !record.createdAt) {
1875
+ * return { ...record, createdAt: Date.now() }
1876
+ * }
1877
+ * return record
1878
+ * }
1879
+ * ```
1880
+ *
1881
+ * @public
1882
+ */
731
1883
  export declare type StoreBeforeCreateHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => R;
732
1884
 
733
- /** @public */
1885
+ /**
1886
+ * Handler function called before a record is deleted from the store.
1887
+ * The handler can return `false` to prevent the deletion from occurring.
1888
+ *
1889
+ * @param record - The record about to be deleted
1890
+ * @param source - Whether the change originated from 'user' interaction or 'remote' synchronization
1891
+ * @returns `false` to prevent deletion, `void` or any other value to allow it
1892
+ *
1893
+ * @example
1894
+ * ```ts
1895
+ * const handler: StoreBeforeDeleteHandler<BookRecord> = (book, source) => {
1896
+ * // Prevent deletion of books that are currently checked out
1897
+ * if (book.isCheckedOut) {
1898
+ * console.warn('Cannot delete checked out book')
1899
+ * return false
1900
+ * }
1901
+ * // Allow deletion for other books
1902
+ * }
1903
+ * ```
1904
+ *
1905
+ * @public
1906
+ */
734
1907
  export declare type StoreBeforeDeleteHandler<R extends UnknownRecord> = (record: R, source: 'remote' | 'user') => false | void;
735
1908
 
736
- /** @public */
1909
+ /**
1910
+ * Information about an error that occurred in the store.
1911
+ *
1912
+ * @example
1913
+ * ```ts
1914
+ * const error: StoreError = {
1915
+ * error: new Error('Validation failed'),
1916
+ * phase: 'updateRecord',
1917
+ * recordBefore: oldRecord,
1918
+ * recordAfter: newRecord,
1919
+ * isExistingValidationIssue: false
1920
+ * }
1921
+ * ```
1922
+ *
1923
+ * @public
1924
+ */
737
1925
  export declare interface StoreError {
1926
+ /** The error that occurred */
738
1927
  error: Error;
1928
+ /** The phase during which the error occurred */
739
1929
  phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord';
1930
+ /** The record state before the operation (if applicable) */
740
1931
  recordBefore?: unknown;
1932
+ /** The record state after the operation */
741
1933
  recordAfter: unknown;
1934
+ /** Whether this is an existing validation issue */
742
1935
  isExistingValidationIssue: boolean;
743
1936
  }
744
1937
 
745
1938
  /**
746
1939
  * A function that will be called when the history changes.
747
1940
  *
1941
+ * @example
1942
+ * ```ts
1943
+ * const listener: StoreListener<Book> = (entry) => {
1944
+ * console.log('Changes:', entry.changes)
1945
+ * console.log('Source:', entry.source)
1946
+ * }
1947
+ *
1948
+ * store.listen(listener)
1949
+ * ```
1950
+ *
1951
+ * @param entry - The history entry containing the changes
1952
+ *
748
1953
  * @public
749
1954
  */
750
1955
  export declare type StoreListener<R extends UnknownRecord> = (entry: HistoryEntry<R>) => void;
751
1956
 
752
- /** @public */
1957
+ /**
1958
+ * Filters for store listeners to control which changes trigger the listener.
1959
+ *
1960
+ * @example
1961
+ * ```ts
1962
+ * const filters: StoreListenerFilters = {
1963
+ * source: 'user', // Only listen to user changes
1964
+ * scope: 'document' // Only listen to document-scoped records
1965
+ * }
1966
+ * ```
1967
+ *
1968
+ * @public
1969
+ */
753
1970
  export declare interface StoreListenerFilters {
1971
+ /** Filter by the source of changes */
754
1972
  source: 'all' | ChangeSource;
1973
+ /** Filter by the scope of records */
755
1974
  scope: 'all' | RecordScope;
756
1975
  }
757
1976
 
758
- /** @public */
1977
+ /**
1978
+ * A store or an object containing a store.
1979
+ * This type is used for APIs that can accept either a store directly or an object with a store property.
1980
+ *
1981
+ * @example
1982
+ * ```ts
1983
+ * function useStore(storeOrObject: StoreObject<MyRecord>) {
1984
+ * const store = storeOrObject instanceof Store ? storeOrObject : storeOrObject.store
1985
+ * return store
1986
+ * }
1987
+ * ```
1988
+ *
1989
+ * @public
1990
+ */
759
1991
  export declare type StoreObject<R extends UnknownRecord> = {
760
1992
  store: Store<R>;
761
1993
  } | Store<R>;
762
1994
 
763
- /** @public */
1995
+ /**
1996
+ * Extract the record type from a StoreObject.
1997
+ *
1998
+ * @example
1999
+ * ```ts
2000
+ * type MyStoreObject = { store: Store<Book | Author> }
2001
+ * type Records = StoreObjectRecordType<MyStoreObject> // Book | Author
2002
+ * ```
2003
+ *
2004
+ * @public
2005
+ */
764
2006
  export declare type StoreObjectRecordType<Context extends StoreObject<any>> = Context extends Store<infer R> ? R : Context extends {
765
2007
  store: Store<infer R>;
766
2008
  } ? R : never;
767
2009
 
768
- /** @public */
2010
+ /**
2011
+ * Handler function called when a store operation (atomic transaction) completes.
2012
+ * This is useful for performing actions after a batch of changes has been applied,
2013
+ * such as triggering saves or sending notifications.
2014
+ *
2015
+ * @param source - Whether the operation originated from 'user' interaction or 'remote' synchronization
2016
+ *
2017
+ * @example
2018
+ * ```ts
2019
+ * const handler: StoreOperationCompleteHandler = (source) => {
2020
+ * if (source === 'user') {
2021
+ * // Auto-save after user operations complete
2022
+ * saveStoreSnapshot()
2023
+ * }
2024
+ * }
2025
+ * ```
2026
+ *
2027
+ * @public
2028
+ */
769
2029
  export declare type StoreOperationCompleteHandler = (source: 'remote' | 'user') => void;
770
2030
 
771
2031
  /**
772
- * A class that provides a 'namespace' for the various kinds of indexes one may wish to derive from
773
- * the record store.
2032
+ * A class that provides reactive querying capabilities for a record store.
2033
+ * Offers methods to create indexes, filter records, and perform efficient lookups with automatic cache management.
2034
+ * All queries are reactive and will automatically update when the underlying store data changes.
2035
+ *
2036
+ * @example
2037
+ * ```ts
2038
+ * // Create a store with books
2039
+ * const store = new Store({ schema: StoreSchema.create({ book: Book, author: Author }) })
2040
+ *
2041
+ * // Get reactive queries for books
2042
+ * const booksByAuthor = store.query.index('book', 'authorId')
2043
+ * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))
2044
+ * ```
2045
+ *
774
2046
  * @public
775
2047
  */
776
2048
  export declare class StoreQueries<R extends UnknownRecord> {
777
2049
  private readonly recordMap;
778
2050
  private readonly history;
779
- constructor(recordMap: AtomMap<IdOf<R>, R>, history: Atom<number, RecordsDiff<R>>);
2051
+ /* Excluded from this release type: __constructor */
780
2052
  /* Excluded from this release type: indexCache */
781
2053
  /* Excluded from this release type: historyCache */
782
2054
  /**
783
- * Create a derivation that contains the history for a given type
2055
+ * Creates a reactive computed that tracks the change history for records of a specific type.
2056
+ * The returned computed provides incremental diffs showing what records of the given type
2057
+ * have been added, updated, or removed.
2058
+ *
2059
+ * @param typeName - The type name to filter the history by
2060
+ * @returns A computed value containing the current epoch and diffs of changes for the specified type
2061
+ *
2062
+ * @example
2063
+ * ```ts
2064
+ * // Track changes to book records only
2065
+ * const bookHistory = store.query.filterHistory('book')
2066
+ *
2067
+ * // React to book changes
2068
+ * react('book-changes', () => {
2069
+ * const currentEpoch = bookHistory.get()
2070
+ * console.log('Books updated at epoch:', currentEpoch)
2071
+ * })
2072
+ * ```
784
2073
  *
785
- * @param typeName - The name of the type to filter by.
786
- * @returns A derivation that returns the ids of all records of the given type.
787
2074
  * @public
788
2075
  */
789
2076
  filterHistory<TypeName extends R['typeName']>(typeName: TypeName): Computed<number, RecordsDiff<Extract<R, {
790
2077
  typeName: TypeName;
791
2078
  }>>>;
792
2079
  /**
793
- * Create a derivation that returns an index on a property for the given type.
2080
+ * Creates a reactive index that maps property values to sets of record IDs for efficient lookups.
2081
+ * The index automatically updates when records are added, updated, or removed, and results are cached
2082
+ * for performance.
2083
+ *
2084
+ * @param typeName - The type name of records to index
2085
+ * @param property - The property name to index by
2086
+ * @returns A reactive computed containing the index map with change diffs
2087
+ *
2088
+ * @example
2089
+ * ```ts
2090
+ * // Create an index of books by author ID
2091
+ * const booksByAuthor = store.query.index('book', 'authorId')
2092
+ *
2093
+ * // Get all books by a specific author
2094
+ * const authorBooks = booksByAuthor.get().get('author:leguin')
2095
+ * console.log(authorBooks) // Set<RecordId<Book>>
2096
+ *
2097
+ * // Index by title for quick title lookups
2098
+ * const booksByTitle = store.query.index('book', 'title')
2099
+ * const booksLatheOfHeaven = booksByTitle.get().get('The Lathe of Heaven')
2100
+ * ```
794
2101
  *
795
- * @param typeName - The name of the type.
796
- * @param property - The name of the property.
797
2102
  * @public
798
2103
  */
799
2104
  index<TypeName extends R['typeName'], Property extends string & keyof Extract<R, {
@@ -803,13 +2108,26 @@ export declare class StoreQueries<R extends UnknownRecord> {
803
2108
  }>, Property>;
804
2109
  /* Excluded from this release type: __uncached_createIndex */
805
2110
  /**
806
- * Create a derivation that will return a signle record matching the given query.
2111
+ * Creates a reactive query that returns the first record matching the given query criteria.
2112
+ * Returns undefined if no matching record is found. The query automatically updates
2113
+ * when records change.
2114
+ *
2115
+ * @param typeName - The type name of records to query
2116
+ * @param queryCreator - Function that returns the query expression object to match against
2117
+ * @param name - Optional name for the query computation (used for debugging)
2118
+ * @returns A computed value containing the first matching record or undefined
2119
+ *
2120
+ * @example
2121
+ * ```ts
2122
+ * // Find the first book with a specific title
2123
+ * const bookLatheOfHeaven = store.query.record('book', () => ({ title: { eq: 'The Lathe of Heaven' } }))
2124
+ * console.log(bookLatheOfHeaven.get()?.title) // 'The Lathe of Heaven' or undefined
807
2125
  *
808
- * It will return undefined if there is no matching record
2126
+ * // Find any book in stock
2127
+ * const anyInStockBook = store.query.record('book', () => ({ inStock: { eq: true } }))
2128
+ * ```
809
2129
  *
810
- * @param typeName - The name of the type?
811
- * @param queryCreator - A function that returns the query expression.
812
- * @param name - (optional) The name of the query.
2130
+ * @public
813
2131
  */
814
2132
  record<TypeName extends R['typeName']>(typeName: TypeName, queryCreator?: () => QueryExpression<Extract<R, {
815
2133
  typeName: TypeName;
@@ -817,11 +2135,28 @@ export declare class StoreQueries<R extends UnknownRecord> {
817
2135
  typeName: TypeName;
818
2136
  }> | undefined>;
819
2137
  /**
820
- * Create a derivation that will return an array of records matching the given query
2138
+ * Creates a reactive query that returns an array of all records matching the given query criteria.
2139
+ * The array automatically updates when records are added, updated, or removed.
2140
+ *
2141
+ * @param typeName - The type name of records to query
2142
+ * @param queryCreator - Function that returns the query expression object to match against
2143
+ * @param name - Optional name for the query computation (used for debugging)
2144
+ * @returns A computed value containing an array of all matching records
2145
+ *
2146
+ * @example
2147
+ * ```ts
2148
+ * // Get all books in stock
2149
+ * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))
2150
+ * console.log(inStockBooks.get()) // Book[]
2151
+ *
2152
+ * // Get all books by a specific author
2153
+ * const leguinBooks = store.query.records('book', () => ({ authorId: { eq: 'author:leguin' } }))
2154
+ *
2155
+ * // Get all books (no filter)
2156
+ * const allBooks = store.query.records('book')
2157
+ * ```
821
2158
  *
822
- * @param typeName - The name of the type?
823
- * @param queryCreator - A function that returns the query expression.
824
- * @param name - (optinal) The name of the query.
2159
+ * @public
825
2160
  */
826
2161
  records<TypeName extends R['typeName']>(typeName: TypeName, queryCreator?: () => QueryExpression<Extract<R, {
827
2162
  typeName: TypeName;
@@ -829,11 +2164,29 @@ export declare class StoreQueries<R extends UnknownRecord> {
829
2164
  typeName: TypeName;
830
2165
  }>>>;
831
2166
  /**
832
- * Create a derivation that will return the ids of all records of the given type.
2167
+ * Creates a reactive query that returns a set of record IDs matching the given query criteria.
2168
+ * This is more efficient than `records()` when you only need the IDs and not the full record objects.
2169
+ * The set automatically updates with collection diffs when records change.
2170
+ *
2171
+ * @param typeName - The type name of records to query
2172
+ * @param queryCreator - Function that returns the query expression object to match against
2173
+ * @param name - Optional name for the query computation (used for debugging)
2174
+ * @returns A computed value containing a set of matching record IDs with collection diffs
2175
+ *
2176
+ * @example
2177
+ * ```ts
2178
+ * // Get IDs of all books in stock
2179
+ * const inStockBookIds = store.query.ids('book', () => ({ inStock: { eq: true } }))
2180
+ * console.log(inStockBookIds.get()) // Set<RecordId<Book>>
2181
+ *
2182
+ * // Get all book IDs (no filter)
2183
+ * const allBookIds = store.query.ids('book')
2184
+ *
2185
+ * // Use with other queries for efficient lookups
2186
+ * const authorBookIds = store.query.ids('book', () => ({ authorId: { eq: 'author:leguin' } }))
2187
+ * ```
833
2188
  *
834
- * @param typeName - The name of the type.
835
- * @param queryCreator - A function that returns the query expression.
836
- * @param name - (optinal) The name of the query.
2189
+ * @public
837
2190
  */
838
2191
  ids<TypeName extends R['typeName']>(typeName: TypeName, queryCreator?: () => QueryExpression<Extract<R, {
839
2192
  typeName: TypeName;
@@ -842,6 +2195,27 @@ export declare class StoreQueries<R extends UnknownRecord> {
842
2195
  }>>>, CollectionDiff<IdOf<Extract<R, {
843
2196
  typeName: TypeName;
844
2197
  }>>>>;
2198
+ /**
2199
+ * Executes a one-time query against the current store state and returns matching records.
2200
+ * This is a non-reactive query that returns results immediately without creating a computed value.
2201
+ * Use this when you need a snapshot of data at a specific point in time.
2202
+ *
2203
+ * @param typeName - The type name of records to query
2204
+ * @param query - The query expression object to match against
2205
+ * @returns An array of records that match the query at the current moment
2206
+ *
2207
+ * @example
2208
+ * ```ts
2209
+ * // Get current in-stock books (non-reactive)
2210
+ * const currentInStockBooks = store.query.exec('book', { inStock: { eq: true } })
2211
+ * console.log(currentInStockBooks) // Book[]
2212
+ *
2213
+ * // Unlike records(), this won't update when the data changes
2214
+ * const staticBookList = store.query.exec('book', { authorId: { eq: 'author:leguin' } })
2215
+ * ```
2216
+ *
2217
+ * @public
2218
+ */
845
2219
  exec<TypeName extends R['typeName']>(typeName: TypeName, query: QueryExpression<Extract<R, {
846
2220
  typeName: TypeName;
847
2221
  }>>): Array<Extract<R, {
@@ -851,12 +2225,72 @@ export declare class StoreQueries<R extends UnknownRecord> {
851
2225
 
852
2226
  /* Excluded from this release type: StoreRecord */
853
2227
 
854
- /** @public */
2228
+ /**
2229
+ * Manages the schema definition, validation, and migration system for a Store.
2230
+ *
2231
+ * StoreSchema coordinates record types, handles data migrations between schema
2232
+ * versions, validates records, and provides the foundational structure for
2233
+ * reactive stores. It acts as the central authority for data consistency
2234
+ * and evolution within the store system.
2235
+ *
2236
+ * @example
2237
+ * ```ts
2238
+ * // Define record types
2239
+ * const Book = createRecordType<Book>('book', { scope: 'document' })
2240
+ * const Author = createRecordType<Author>('author', { scope: 'document' })
2241
+ *
2242
+ * // Create schema with migrations
2243
+ * const schema = StoreSchema.create(
2244
+ * { book: Book, author: Author },
2245
+ * {
2246
+ * migrations: [bookMigrations, authorMigrations],
2247
+ * onValidationFailure: (failure) => {
2248
+ * console.warn('Validation failed, using default:', failure.error)
2249
+ * return failure.record // or return a corrected version
2250
+ * }
2251
+ * }
2252
+ * )
2253
+ *
2254
+ * // Use with store
2255
+ * const store = new Store({ schema })
2256
+ * ```
2257
+ *
2258
+ * @public
2259
+ */
855
2260
  export declare class StoreSchema<R extends UnknownRecord, P = unknown> {
856
2261
  readonly types: {
857
2262
  [Record in R as Record['typeName']]: RecordType<R, any>;
858
2263
  };
859
2264
  private readonly options;
2265
+ /**
2266
+ * Creates a new StoreSchema with the given record types and options.
2267
+ *
2268
+ * This static factory method is the recommended way to create a StoreSchema.
2269
+ * It ensures type safety while providing a clean API for schema definition.
2270
+ *
2271
+ * @param types - Object mapping type names to their RecordType definitions
2272
+ * @param options - Optional configuration for migrations, validation, and integrity checking
2273
+ * @returns A new StoreSchema instance
2274
+ *
2275
+ * @example
2276
+ * ```ts
2277
+ * const Book = createRecordType<Book>('book', { scope: 'document' })
2278
+ * const Author = createRecordType<Author>('author', { scope: 'document' })
2279
+ *
2280
+ * const schema = StoreSchema.create(
2281
+ * {
2282
+ * book: Book,
2283
+ * author: Author
2284
+ * },
2285
+ * {
2286
+ * migrations: [bookMigrations],
2287
+ * onValidationFailure: (failure) => failure.record
2288
+ * }
2289
+ * )
2290
+ * ```
2291
+ *
2292
+ * @public
2293
+ */
860
2294
  static create<R extends UnknownRecord, P = unknown>(types: {
861
2295
  [TypeName in R['typeName']]: {
862
2296
  createId: any;
@@ -866,19 +2300,186 @@ export declare class StoreSchema<R extends UnknownRecord, P = unknown> {
866
2300
  readonly sortedMigrations: readonly Migration[];
867
2301
  private readonly migrationCache;
868
2302
  private constructor();
2303
+ /**
2304
+ * Validates a record using its corresponding RecordType validator.
2305
+ *
2306
+ * This method ensures that records conform to their type definitions before
2307
+ * being stored. If validation fails and an onValidationFailure handler is
2308
+ * provided, it will be called to potentially recover from the error.
2309
+ *
2310
+ * @param store - The store instance where validation is occurring
2311
+ * @param record - The record to validate
2312
+ * @param phase - The lifecycle phase where validation is happening
2313
+ * @param recordBefore - The previous version of the record (for updates)
2314
+ * @returns The validated record, potentially modified by validation failure handler
2315
+ *
2316
+ * @example
2317
+ * ```ts
2318
+ * try {
2319
+ * const validatedBook = schema.validateRecord(
2320
+ * store,
2321
+ * { id: 'book:1', typeName: 'book', title: '', author: 'Jane Doe' },
2322
+ * 'createRecord',
2323
+ * null
2324
+ * )
2325
+ * } catch (error) {
2326
+ * console.error('Record validation failed:', error)
2327
+ * }
2328
+ * ```
2329
+ *
2330
+ * @public
2331
+ */
869
2332
  validateRecord(store: Store<R>, record: R, phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord', recordBefore: null | R): R;
2333
+ /**
2334
+ * Gets all migrations that need to be applied to upgrade from a persisted schema
2335
+ * to the current schema version.
2336
+ *
2337
+ * This method compares the persisted schema with the current schema and determines
2338
+ * which migrations need to be applied to bring the data up to date. It handles
2339
+ * both regular migrations and retroactive migrations, and caches results for
2340
+ * performance.
2341
+ *
2342
+ * @param persistedSchema - The schema version that was previously persisted
2343
+ * @returns A Result containing the list of migrations to apply, or an error message
2344
+ *
2345
+ * @example
2346
+ * ```ts
2347
+ * const persistedSchema = {
2348
+ * schemaVersion: 2,
2349
+ * sequences: { 'com.tldraw.book': 1, 'com.tldraw.author': 0 }
2350
+ * }
2351
+ *
2352
+ * const migrationsResult = schema.getMigrationsSince(persistedSchema)
2353
+ * if (migrationsResult.ok) {
2354
+ * console.log('Migrations to apply:', migrationsResult.value.length)
2355
+ * // Apply each migration to bring data up to date
2356
+ * }
2357
+ * ```
2358
+ *
2359
+ * @public
2360
+ */
870
2361
  getMigrationsSince(persistedSchema: SerializedSchema): Result<Migration[], string>;
2362
+ /**
2363
+ * Migrates a single persisted record to match the current schema version.
2364
+ *
2365
+ * This method applies the necessary migrations to transform a record from an
2366
+ * older (or newer) schema version to the current version. It supports both
2367
+ * forward ('up') and backward ('down') migrations.
2368
+ *
2369
+ * @param record - The record to migrate
2370
+ * @param persistedSchema - The schema version the record was persisted with
2371
+ * @param direction - Direction to migrate ('up' for newer, 'down' for older)
2372
+ * @returns A MigrationResult containing the migrated record or an error
2373
+ *
2374
+ * @example
2375
+ * ```ts
2376
+ * const oldRecord = { id: 'book:1', typeName: 'book', title: 'Old Title', publishDate: '2020-01-01' }
2377
+ * const oldSchema = { schemaVersion: 2, sequences: { 'com.tldraw.book': 1 } }
2378
+ *
2379
+ * const result = schema.migratePersistedRecord(oldRecord, oldSchema, 'up')
2380
+ * if (result.type === 'success') {
2381
+ * console.log('Migrated record:', result.value)
2382
+ * // Record now has publishedYear instead of publishDate
2383
+ * } else {
2384
+ * console.error('Migration failed:', result.reason)
2385
+ * }
2386
+ * ```
2387
+ *
2388
+ * @public
2389
+ */
871
2390
  migratePersistedRecord(record: R, persistedSchema: SerializedSchema, direction?: 'down' | 'up'): MigrationResult<R>;
2391
+ /**
2392
+ * Migrates an entire store snapshot to match the current schema version.
2393
+ *
2394
+ * This method applies all necessary migrations to bring a persisted store
2395
+ * snapshot up to the current schema version. It handles both record-level
2396
+ * and store-level migrations, and can optionally mutate the input store
2397
+ * for performance.
2398
+ *
2399
+ * @param snapshot - The store snapshot containing data and schema information
2400
+ * @param opts - Options controlling migration behavior
2401
+ * - mutateInputStore - Whether to modify the input store directly (default: false)
2402
+ * @returns A MigrationResult containing the migrated store or an error
2403
+ *
2404
+ * @example
2405
+ * ```ts
2406
+ * const snapshot = {
2407
+ * schema: { schemaVersion: 2, sequences: { 'com.tldraw.book': 1 } },
2408
+ * store: {
2409
+ * 'book:1': { id: 'book:1', typeName: 'book', title: 'Old Book', publishDate: '2020-01-01' }
2410
+ * }
2411
+ * }
2412
+ *
2413
+ * const result = schema.migrateStoreSnapshot(snapshot)
2414
+ * if (result.type === 'success') {
2415
+ * console.log('Migrated store:', result.value)
2416
+ * // All records are now at current schema version
2417
+ * }
2418
+ * ```
2419
+ *
2420
+ * @public
2421
+ */
872
2422
  migrateStoreSnapshot(snapshot: StoreSnapshot<R>, opts?: {
873
2423
  mutateInputStore?: boolean;
874
2424
  }): MigrationResult<SerializedStore<R>>;
875
2425
  /* Excluded from this release type: createIntegrityChecker */
2426
+ /**
2427
+ * Serializes the current schema to a SerializedSchemaV2 format.
2428
+ *
2429
+ * This method creates a serialized representation of the current schema,
2430
+ * capturing the latest version number for each migration sequence.
2431
+ * The result can be persisted and later used to determine what migrations
2432
+ * need to be applied when loading data.
2433
+ *
2434
+ * @returns A SerializedSchemaV2 object representing the current schema state
2435
+ *
2436
+ * @example
2437
+ * ```ts
2438
+ * const serialized = schema.serialize()
2439
+ * console.log(serialized)
2440
+ * // {
2441
+ * // schemaVersion: 2,
2442
+ * // sequences: {
2443
+ * // 'com.tldraw.book': 3,
2444
+ * // 'com.tldraw.author': 2
2445
+ * // }
2446
+ * // }
2447
+ *
2448
+ * // Store this with your data for future migrations
2449
+ * localStorage.setItem('schema', JSON.stringify(serialized))
2450
+ * ```
2451
+ *
2452
+ * @public
2453
+ */
876
2454
  serialize(): SerializedSchemaV2;
877
2455
  /* Excluded from this release type: serializeEarliestVersion */
878
2456
  /* Excluded from this release type: getType */
879
2457
  }
880
2458
 
881
- /** @public */
2459
+ /**
2460
+ * Configuration options for creating a StoreSchema.
2461
+ *
2462
+ * These options control migration behavior, validation error handling,
2463
+ * and integrity checking for the store schema.
2464
+ *
2465
+ * @example
2466
+ * ```ts
2467
+ * const options: StoreSchemaOptions<MyRecord, MyProps> = {
2468
+ * migrations: [bookMigrations, authorMigrations],
2469
+ * onValidationFailure: (failure) => {
2470
+ * // Log the error and return a corrected record
2471
+ * console.error('Validation failed:', failure.error)
2472
+ * return sanitizeRecord(failure.record)
2473
+ * },
2474
+ * createIntegrityChecker: (store) => {
2475
+ * // Set up integrity checking logic
2476
+ * return setupIntegrityChecks(store)
2477
+ * }
2478
+ * }
2479
+ * ```
2480
+ *
2481
+ * @public
2482
+ */
882
2483
  export declare interface StoreSchemaOptions<R extends UnknownRecord, P> {
883
2484
  migrations?: MigrationSequence[];
884
2485
  /** @public */
@@ -888,14 +2489,36 @@ export declare interface StoreSchemaOptions<R extends UnknownRecord, P> {
888
2489
 
889
2490
  /**
890
2491
  * The side effect manager (aka a "correct state enforcer") is responsible
891
- * for making sure that the editor's state is always correct. This includes
2492
+ * for making sure that the store's state is always correct and consistent. This includes
892
2493
  * things like: deleting a shape if its parent is deleted; unbinding
893
- * arrows when their binding target is deleted; etc.
2494
+ * arrows when their binding target is deleted; maintaining referential integrity; etc.
2495
+ *
2496
+ * Side effects are organized into lifecycle hooks that run before and after
2497
+ * record operations (create, change, delete), allowing you to validate data,
2498
+ * transform records, and maintain business rules.
2499
+ *
2500
+ * @example
2501
+ * ```ts
2502
+ * const sideEffects = new StoreSideEffects(store)
2503
+ *
2504
+ * // Ensure arrows are deleted when their target shape is deleted
2505
+ * sideEffects.registerAfterDeleteHandler('shape', (shape) => {
2506
+ * const arrows = store.query.records('arrow', () => ({
2507
+ * toId: { eq: shape.id }
2508
+ * })).get()
2509
+ * store.remove(arrows.map(arrow => arrow.id))
2510
+ * })
2511
+ * ```
894
2512
  *
895
2513
  * @public
896
2514
  */
897
2515
  export declare class StoreSideEffects<R extends UnknownRecord> {
898
2516
  private readonly store;
2517
+ /**
2518
+ * Creates a new side effects manager for the given store.
2519
+ *
2520
+ * store - The store instance to manage side effects for
2521
+ */
899
2522
  constructor(store: Store<R>);
900
2523
  private _beforeCreateHandlers;
901
2524
  private _afterCreateHandlers;
@@ -1107,13 +2730,52 @@ export declare class StoreSideEffects<R extends UnknownRecord> {
1107
2730
  registerOperationCompleteHandler(handler: StoreOperationCompleteHandler): () => void;
1108
2731
  }
1109
2732
 
1110
- /** @public */
2733
+ /**
2734
+ * A snapshot of the store including both data and schema information.
2735
+ * This enables proper migration when loading data from different schema versions.
2736
+ *
2737
+ * @example
2738
+ * ```ts
2739
+ * const snapshot = store.getStoreSnapshot()
2740
+ * // Later...
2741
+ * store.loadStoreSnapshot(snapshot)
2742
+ * ```
2743
+ *
2744
+ * @public
2745
+ */
1111
2746
  export declare interface StoreSnapshot<R extends UnknownRecord> {
2747
+ /** The serialized store data */
1112
2748
  store: SerializedStore<R>;
2749
+ /** The serialized schema information */
1113
2750
  schema: SerializedSchema;
1114
2751
  }
1115
2752
 
1116
- /** @public */
2753
+ /**
2754
+ * Information about a record validation failure that occurred in the store.
2755
+ *
2756
+ * This interface provides context about validation errors, including the failed
2757
+ * record, the store state, and the operation phase where the failure occurred.
2758
+ * It's used by validation failure handlers to implement recovery strategies.
2759
+ *
2760
+ * @example
2761
+ * ```ts
2762
+ * const schema = StoreSchema.create(
2763
+ * { book: Book },
2764
+ * {
2765
+ * onValidationFailure: (failure: StoreValidationFailure<Book>) => {
2766
+ * console.error(`Validation failed during ${failure.phase}:`, failure.error)
2767
+ * console.log('Failed record:', failure.record)
2768
+ * console.log('Previous record:', failure.recordBefore)
2769
+ *
2770
+ * // Return a corrected version of the record
2771
+ * return { ...failure.record, title: failure.record.title || 'Untitled' }
2772
+ * }
2773
+ * }
2774
+ * )
2775
+ * ```
2776
+ *
2777
+ * @public
2778
+ */
1117
2779
  export declare interface StoreValidationFailure<R extends UnknownRecord> {
1118
2780
  error: unknown;
1119
2781
  store: Store<R>;
@@ -1122,20 +2784,85 @@ export declare interface StoreValidationFailure<R extends UnknownRecord> {
1122
2784
  recordBefore: null | R;
1123
2785
  }
1124
2786
 
1125
- /** @public */
2787
+ /**
2788
+ * A validator for store records that ensures data integrity.
2789
+ * Validators are called when records are created or updated.
2790
+ *
2791
+ * @example
2792
+ * ```ts
2793
+ * const bookValidator: StoreValidator<Book> = {
2794
+ * validate(record: unknown): Book {
2795
+ * // Validate and return the record
2796
+ * if (typeof record !== 'object' || !record.title) {
2797
+ * throw new Error('Invalid book')
2798
+ * }
2799
+ * return record as Book
2800
+ * }
2801
+ * }
2802
+ * ```
2803
+ *
2804
+ * @public
2805
+ */
1126
2806
  export declare interface StoreValidator<R extends UnknownRecord> {
2807
+ /**
2808
+ * Validate a record.
2809
+ *
2810
+ * @param record - The record to validate
2811
+ * @returns The validated record
2812
+ * @throws When validation fails
2813
+ */
1127
2814
  validate(record: unknown): R;
2815
+ /**
2816
+ * Validate a record using a known good version for reference.
2817
+ *
2818
+ * @param knownGoodVersion - A known valid version of the record
2819
+ * @param record - The record to validate
2820
+ * @returns The validated record
2821
+ */
1128
2822
  validateUsingKnownGoodVersion?(knownGoodVersion: R, record: unknown): R;
1129
2823
  }
1130
2824
 
1131
- /** @public */
2825
+ /**
2826
+ * A map of validators for each record type in the store.
2827
+ *
2828
+ * @example
2829
+ * ```ts
2830
+ * const validators: StoreValidators<Book | Author> = {
2831
+ * book: bookValidator,
2832
+ * author: authorValidator
2833
+ * }
2834
+ * ```
2835
+ *
2836
+ * @public
2837
+ */
1132
2838
  export declare type StoreValidators<R extends UnknownRecord> = {
1133
2839
  [K in R['typeName']]: StoreValidator<Extract<R, {
1134
2840
  typeName: K;
1135
2841
  }>>;
1136
2842
  };
1137
2843
 
1138
- /** @public */
2844
+ /**
2845
+ * A generic type representing any record that extends BaseRecord.
2846
+ * This is useful for type constraints when you need to work with records of unknown types,
2847
+ * but still want to ensure they follow the BaseRecord structure.
2848
+ *
2849
+ * @example
2850
+ * ```ts
2851
+ * // Function that works with any type of record
2852
+ * function logRecord(record: UnknownRecord): void {
2853
+ * console.log(`Record ${record.id} of type ${record.typeName}`)
2854
+ * }
2855
+ *
2856
+ * // Can be used with any record type
2857
+ * const book: Book = { id: 'book:123' as RecordId<Book>, typeName: 'book', title: '1984' }
2858
+ * const author: Author = { id: 'author:456' as RecordId<Author>, typeName: 'author', name: 'Orwell' }
2859
+ *
2860
+ * logRecord(book) // "Record book:123 of type book"
2861
+ * logRecord(author) // "Record author:456 of type author"
2862
+ * ```
2863
+ *
2864
+ * @public
2865
+ */
1139
2866
  export declare type UnknownRecord = BaseRecord<string, RecordId<UnknownRecord>>;
1140
2867
 
1141
2868
  export { }