@tldraw/store 5.6.0-next.0b1aaf8f5861 → 5.6.0-next.568da2160ccd

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.
@@ -1809,6 +1809,12 @@ export declare class Store<R extends UnknownRecord = UnknownRecord, Props = unkn
1809
1809
  * signal for the underlying record. Return a signal (usually a computed) for the cached value.
1810
1810
  * For simple derivations, use {@link Store.createComputedCache}. This function is useful if you
1811
1811
  * need more precise control over intermediate values.
1812
+ *
1813
+ * After the record is deleted the record signal holds `UNINITIALIZED` rather than a record, and
1814
+ * a signal that a reader still depends on can be re-derived once more in that state. Guard with
1815
+ * `isUninitialized` and return a value no live record can produce: returning the same value the
1816
+ * record last had (e.g. `undefined`) leaves readers stale if a record with that id comes back.
1817
+ * {@link Store.createComputedCache} handles this for you.
1812
1818
  */
1813
1819
  createCache<Result, Record extends R = R>(create: (id: IdOf<Record>, recordSignal: Signal<R>) => Signal<Result>): {
1814
1820
  get: (id: IdOf<Record>) => Result | undefined;
package/dist-cjs/index.js CHANGED
@@ -56,7 +56,7 @@ var import_StoreSchema = require("./lib/StoreSchema");
56
56
  var import_StoreSideEffects = require("./lib/StoreSideEffects");
57
57
  (0, import_utils.registerTldrawLibraryVersion)(
58
58
  "@tldraw/store",
59
- "5.6.0-next.0b1aaf8f5861",
59
+ "5.6.0-next.568da2160ccd",
60
60
  "cjs"
61
61
  );
62
62
  //# sourceMappingURL=index.js.map
@@ -29,6 +29,7 @@ var import_devFreeze = require("./devFreeze");
29
29
  var import_RecordsDiff = require("./RecordsDiff");
30
30
  var import_StoreQueries = require("./StoreQueries");
31
31
  var import_StoreSideEffects = require("./StoreSideEffects");
32
+ const DELETED_RECORD = /* @__PURE__ */ Symbol("deleted record");
32
33
  class Store {
33
34
  /**
34
35
  * The unique identifier of the store instance.
@@ -180,8 +181,9 @@ class Store {
180
181
  }
181
182
  isFlushingHistory = false;
182
183
  _flushHistory() {
183
- if (this.historyAccumulator.hasChanges()) {
184
- const entries = this.historyAccumulator.flush();
184
+ const version = this.history.__unsafe__getWithoutCapture();
185
+ const entries = this.historyAccumulator.flush(version);
186
+ if (entries.length > 0) {
185
187
  const errors = [];
186
188
  const wasFlushingHistory = this.isFlushingHistory;
187
189
  this.isFlushingHistory = true;
@@ -241,14 +243,18 @@ class Store {
241
243
  * @param changes - The changes to add to the history.
242
244
  */
243
245
  updateHistory(changes) {
244
- this.historyAccumulator.add({
245
- changes,
246
- source: this.isMergingRemoteChanges ? "remote" : "user"
247
- });
246
+ const version = this.history.__unsafe__getWithoutCapture() + 1;
247
+ this.history.set(version, changes);
248
+ this.historyAccumulator.add(
249
+ {
250
+ changes,
251
+ source: this.isMergingRemoteChanges ? "remote" : "user"
252
+ },
253
+ version
254
+ );
248
255
  if (this.listeners.size === 0) {
249
256
  this.historyAccumulator.clear();
250
257
  }
251
- this.history.set(this.history.__unsafe__getWithoutCapture() + 1, changes);
252
258
  }
253
259
  validate(phase) {
254
260
  this.allRecords().forEach((record) => this.schema.validateRecord(this, record, phase, null));
@@ -661,11 +667,20 @@ class Store {
661
667
  * Run `fn` and return a {@link RecordsDiff} of the changes that occurred as a result.
662
668
  */
663
669
  extractingChanges(fn) {
664
- const changes = [];
665
- const dispose = this.historyAccumulator.addInterceptor((entry) => changes.push(entry.changes));
670
+ const collected = [];
671
+ const dispose = this.historyAccumulator.addInterceptor((entry, version) => {
672
+ while (collected.length && collected[collected.length - 1].version >= version) {
673
+ collected.pop();
674
+ }
675
+ collected.push({ version, changes: entry.changes });
676
+ });
666
677
  try {
667
678
  (0, import_state.transact)(fn);
668
- return (0, import_RecordsDiff.squashRecordDiffs)(changes);
679
+ const current = this.history.__unsafe__getWithoutCapture();
680
+ while (collected.length && collected[collected.length - 1].version > current) {
681
+ collected.pop();
682
+ }
683
+ return (0, import_RecordsDiff.squashRecordDiffs)(collected.map((c) => c.changes));
669
684
  } finally {
670
685
  dispose();
671
686
  }
@@ -716,6 +731,12 @@ class Store {
716
731
  * signal for the underlying record. Return a signal (usually a computed) for the cached value.
717
732
  * For simple derivations, use {@link Store.createComputedCache}. This function is useful if you
718
733
  * need more precise control over intermediate values.
734
+ *
735
+ * After the record is deleted the record signal holds `UNINITIALIZED` rather than a record, and
736
+ * a signal that a reader still depends on can be re-derived once more in that state. Guard with
737
+ * `isUninitialized` and return a value no live record can produce: returning the same value the
738
+ * record last had (e.g. `undefined`) leaves readers stale if a record with that id comes back.
739
+ * {@link Store.createComputedCache} handles this for you.
719
740
  */
720
741
  createCache(create) {
721
742
  const cache = new import_utils.WeakCache();
@@ -736,15 +757,22 @@ class Store {
736
757
  * @public
737
758
  */
738
759
  createComputedCache(name, derive, opts) {
760
+ const areRecordsEqual = opts?.areRecordsEqual;
761
+ const areResultsEqual = opts?.areResultsEqual;
739
762
  return this.createCache((id, record) => {
740
- const recordSignal = opts?.areRecordsEqual ? (0, import_state.computed)(`${name}:${id}:isEqual`, () => record.get(), { isEqual: opts.areRecordsEqual }) : record;
763
+ const recordSignal = areRecordsEqual ? (0, import_state.computed)(`${name}:${id}:isEqual`, () => record.get(), {
764
+ // a deleted record's atom holds UNINITIALIZED, which user code must not see
765
+ isEqual: (a, b) => (0, import_state.isUninitialized)(a) || (0, import_state.isUninitialized)(b) ? a === b : areRecordsEqual(a, b)
766
+ }) : record;
741
767
  return (0, import_state.computed)(
742
768
  name + ":" + id,
743
769
  () => {
744
- return derive(recordSignal.get());
770
+ const value = recordSignal.get();
771
+ if ((0, import_state.isUninitialized)(value)) return DELETED_RECORD;
772
+ return derive(value);
745
773
  },
746
774
  {
747
- isEqual: opts?.areResultsEqual
775
+ isEqual: areResultsEqual ? (a, b) => a === DELETED_RECORD || b === DELETED_RECORD ? a === b : areResultsEqual(a, b) : void 0
748
776
  }
749
777
  );
750
778
  });
@@ -874,10 +902,14 @@ function squashHistoryEntries(entries) {
874
902
  }
875
903
  class HistoryAccumulator {
876
904
  _history = [];
905
+ // The `history` atom value each entry was recorded at. A transaction that aborts rolls that atom
906
+ // back but cannot unwind this array, so an entry stamped at or past the atom's current value
907
+ // belongs to a rolled-back change-set and must not reach listeners.
908
+ _versions = [];
877
909
  _interceptors = /* @__PURE__ */ new Set();
878
910
  /**
879
- * Add an interceptor that will be called for each history entry.
880
- * Returns a function to remove the interceptor.
911
+ * Add an interceptor that will be called for each history entry, with the version it was
912
+ * recorded at (see `add`). Returns a function to remove the interceptor.
881
913
  */
882
914
  addInterceptor(fn) {
883
915
  this._interceptors.add(fn);
@@ -886,22 +918,25 @@ class HistoryAccumulator {
886
918
  };
887
919
  }
888
920
  /**
889
- * Add a history entry to the accumulator.
921
+ * Add a history entry recorded at `version` (the `history` atom value it sets).
890
922
  * Calls all registered interceptors with the entry.
891
923
  */
892
- add(entry) {
924
+ add(entry, version) {
925
+ this.discardFrom(version);
893
926
  this._history.push(entry);
927
+ this._versions.push(version);
894
928
  for (const interceptor of this._interceptors) {
895
- interceptor(entry);
929
+ interceptor(entry, version);
896
930
  }
897
931
  }
898
932
  /**
899
933
  * Flush all accumulated history entries, squashing adjacent entries from the same source.
900
934
  * Clears the internal history buffer.
901
935
  */
902
- flush() {
936
+ flush(currentVersion) {
937
+ this.discardFrom(currentVersion + 1);
903
938
  const history = squashHistoryEntries(this._history);
904
- this._history = [];
939
+ this.clear();
905
940
  return history;
906
941
  }
907
942
  /**
@@ -909,12 +944,15 @@ class HistoryAccumulator {
909
944
  */
910
945
  clear() {
911
946
  this._history = [];
912
- }
913
- /**
914
- * Check if there are any accumulated history entries.
915
- */
916
- hasChanges() {
917
- return this._history.length > 0;
947
+ this._versions = [];
948
+ }
949
+ // Drops the entries recorded at or after `version`. Entries are in ascending version order: the
950
+ // atom increments by one per entry, and a rollback is followed by exactly this discard.
951
+ discardFrom(version) {
952
+ while (this._versions.length && this._versions[this._versions.length - 1] >= version) {
953
+ this._versions.pop();
954
+ this._history.pop();
955
+ }
918
956
  }
919
957
  }
920
958
  function createComputedCache(name, derive, opts) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/Store.ts"],
4
- "sourcesContent": ["import { Atom, Reactor, Signal, atom, computed, reactor, transact } from '@tldraw/state'\nimport {\n\tWeakCache,\n\tassert,\n\tfilterEntries,\n\tgetOwnProperty,\n\tisEqual,\n\tobjectMapEntries,\n\tobjectMapKeys,\n\tobjectMapValues,\n\tthrottleToNextFrame,\n\tuniqueId,\n} from '@tldraw/utils'\nimport { AtomMap } from './AtomMap'\nimport { IdOf, RecordId, UnknownRecord } from './BaseRecord'\nimport { devFreeze } from './devFreeze'\nimport { hasAnyKey, isRecordsDiffEmpty, RecordsDiff, squashRecordDiffs } from './RecordsDiff'\nimport { RecordScope } from './RecordType'\nimport { StoreQueries } from './StoreQueries'\nimport { SerializedSchema, StoreSchema } from './StoreSchema'\nimport { StoreSideEffects } from './StoreSideEffects'\n\n/**\n * Extracts the record type from a record ID type.\n *\n * @example\n * ```ts\n * type BookId = RecordId<Book>\n * type BookType = RecordFromId<BookId> // Book\n * ```\n *\n * @public\n */\nexport type RecordFromId<K extends RecordId<UnknownRecord>> =\n\tK extends RecordId<infer R> ? R : never\n\n/**\n * A diff describing the changes to a collection.\n *\n * @example\n * ```ts\n * const diff: CollectionDiff<string> = {\n * added: new Set(['newItem']),\n * removed: new Set(['oldItem'])\n * }\n * ```\n *\n * @public\n */\nexport interface CollectionDiff<T> {\n\t/** Items that were added to the collection */\n\tadded?: Set<T>\n\t/** Items that were removed from the collection */\n\tremoved?: Set<T>\n}\n\n/**\n * The source of a change to the store.\n * - `'user'` - Changes originating from local user actions\n * - `'remote'` - Changes originating from remote synchronization\n *\n * @public\n */\nexport type ChangeSource = 'user' | 'remote'\n\n/**\n * Filters for store listeners to control which changes trigger the listener.\n *\n * @example\n * ```ts\n * const filters: StoreListenerFilters = {\n * source: 'user', // Only listen to user changes\n * scope: 'document' // Only listen to document-scoped records\n * }\n * ```\n *\n * @public\n */\nexport interface StoreListenerFilters {\n\t/** Filter by the source of changes */\n\tsource: ChangeSource | 'all'\n\t/** Filter by the scope of records */\n\tscope: RecordScope | 'all'\n}\n\n/**\n * An entry containing changes that originated either by user actions or remote changes.\n * History entries are used to track and replay changes to the store.\n *\n * @example\n * ```ts\n * const entry: HistoryEntry<Book> = {\n * changes: {\n * added: { 'book:123': bookRecord },\n * updated: {},\n * removed: {}\n * },\n * source: 'user'\n * }\n * ```\n *\n * @public\n */\nexport interface HistoryEntry<R extends UnknownRecord = UnknownRecord> {\n\t/** The changes that occurred in this history entry */\n\tchanges: RecordsDiff<R>\n\t/** The source of these changes */\n\tsource: ChangeSource\n}\n\n/**\n * A function that will be called when the history changes.\n *\n * @example\n * ```ts\n * const listener: StoreListener<Book> = (entry) => {\n * console.log('Changes:', entry.changes)\n * console.log('Source:', entry.source)\n * }\n *\n * store.listen(listener)\n * ```\n *\n * @param entry - The history entry containing the changes\n *\n * @public\n */\nexport type StoreListener<R extends UnknownRecord> = (entry: HistoryEntry<R>) => void\n\n/**\n * A computed cache that stores derived data for records.\n * The cache automatically updates when underlying records change and cleans up when records are deleted.\n *\n * @example\n * ```ts\n * const expensiveCache = store.createComputedCache(\n * 'expensive',\n * (book: Book) => performExpensiveCalculation(book)\n * )\n *\n * const result = expensiveCache.get(bookId)\n * ```\n *\n * @public\n */\nexport interface ComputedCache<Data, R extends UnknownRecord> {\n\t/**\n\t * Get the cached data for a record by its ID.\n\t *\n\t * @param id - The ID of the record\n\t * @returns The cached data or undefined if the record doesn't exist\n\t */\n\tget(id: IdOf<R>): Data | undefined\n}\n\n/**\n * Options for creating a computed cache.\n *\n * @example\n * ```ts\n * const options: CreateComputedCacheOpts<string[], Book> = {\n * areRecordsEqual: (a, b) => a.title === b.title,\n * areResultsEqual: (a, b) => JSON.stringify(a) === JSON.stringify(b)\n * }\n * ```\n *\n * @public\n */\nexport interface CreateComputedCacheOpts<Data, R extends UnknownRecord> {\n\t/** Custom equality function for comparing records */\n\tareRecordsEqual?(a: R, b: R): boolean\n\t/** Custom equality function for comparing results */\n\tareResultsEqual?(a: Data, b: Data): boolean\n}\n\n/**\n * A serialized snapshot of the record store's values.\n * This is a plain JavaScript object that can be saved to storage or transmitted over the network.\n *\n * @example\n * ```ts\n * const serialized: SerializedStore<Book> = {\n * 'book:123': { id: 'book:123', typeName: 'book', title: 'The Lathe of Heaven' },\n * 'book:456': { id: 'book:456', typeName: 'book', title: 'The Left Hand of Darkness' }\n * }\n * ```\n *\n * @public\n */\nexport type SerializedStore<R extends UnknownRecord> = Record<IdOf<R>, R>\n\n/**\n * A snapshot of the store including both data and schema information.\n * This enables proper migration when loading data from different schema versions.\n *\n * @example\n * ```ts\n * const snapshot = store.getStoreSnapshot()\n * // Later...\n * store.loadStoreSnapshot(snapshot)\n * ```\n *\n * @public\n */\nexport interface StoreSnapshot<R extends UnknownRecord> {\n\t/** The serialized store data */\n\tstore: SerializedStore<R>\n\t/** The serialized schema information */\n\tschema: SerializedSchema\n}\n\n/**\n * A validator for store records that ensures data integrity.\n * Validators are called when records are created or updated.\n *\n * @example\n * ```ts\n * const bookValidator: StoreValidator<Book> = {\n * validate(record: unknown): Book {\n * // Validate and return the record\n * if (typeof record !== 'object' || !record.title) {\n * throw new Error('Invalid book')\n * }\n * return record as Book\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface StoreValidator<R extends UnknownRecord> {\n\t/**\n\t * Validate a record.\n\t *\n\t * @param record - The record to validate\n\t * @returns The validated record\n\t * @throws When validation fails\n\t */\n\tvalidate(record: unknown): R\n\t/**\n\t * Validate a record using a known good version for reference.\n\t *\n\t * @param knownGoodVersion - A known valid version of the record\n\t * @param record - The record to validate\n\t * @returns The validated record\n\t */\n\tvalidateUsingKnownGoodVersion?(knownGoodVersion: R, record: unknown): R\n}\n\n/**\n * A map of validators for each record type in the store.\n *\n * @example\n * ```ts\n * const validators: StoreValidators<Book | Author> = {\n * book: bookValidator,\n * author: authorValidator\n * }\n * ```\n *\n * @public\n */\nexport type StoreValidators<R extends UnknownRecord> = {\n\t[K in R['typeName']]: StoreValidator<Extract<R, { typeName: K }>>\n}\n\n/**\n * Information about an error that occurred in the store.\n *\n * @example\n * ```ts\n * const error: StoreError = {\n * error: new Error('Validation failed'),\n * phase: 'updateRecord',\n * recordBefore: oldRecord,\n * recordAfter: newRecord,\n * isExistingValidationIssue: false\n * }\n * ```\n *\n * @public\n */\nexport interface StoreError {\n\t/** The error that occurred */\n\terror: Error\n\t/** The phase during which the error occurred */\n\tphase: 'initialize' | 'createRecord' | 'updateRecord' | 'tests'\n\t/** The record state before the operation (if applicable) */\n\trecordBefore?: unknown\n\t/** The record state after the operation */\n\trecordAfter: unknown\n\t/** Whether this is an existing validation issue */\n\tisExistingValidationIssue: boolean\n}\n\n/**\n * Extract the record type from a Store type.\n * Used internally for type inference.\n *\n * @internal\n */\nexport type StoreRecord<S extends Store<any>> = S extends Store<infer R> ? R : never\n\n/**\n * A reactive store that manages collections of typed records.\n *\n * The Store is the central container for your application's data, providing:\n * - Reactive state management with automatic updates\n * - Type-safe record operations\n * - History tracking and change notifications\n * - Schema validation and migrations\n * - Side effects and business logic hooks\n * - Efficient querying and indexing\n *\n * @example\n * ```ts\n * // Create a store with schema\n * const schema = StoreSchema.create({\n * book: Book,\n * author: Author\n * })\n *\n * const store = new Store({\n * schema,\n * props: {}\n * })\n *\n * // Add records\n * const book = Book.create({ title: 'The Lathe of Heaven', author: 'Le Guin' })\n * store.put([book])\n *\n * // Listen to changes\n * store.listen((entry) => {\n * console.log('Changes:', entry.changes)\n * })\n * ```\n *\n * @public\n */\nexport class Store<R extends UnknownRecord = UnknownRecord, Props = unknown> {\n\t/**\n\t * The unique identifier of the store instance.\n\t *\n\t * @public\n\t */\n\tpublic readonly id: string\n\t/**\n\t * An AtomMap containing the stores records.\n\t *\n\t * @internal\n\t * @readonly\n\t */\n\tprivate readonly records: AtomMap<IdOf<R>, R>\n\n\t/**\n\t * An atom containing the store's history.\n\t *\n\t * @public\n\t * @readonly\n\t */\n\treadonly history: Atom<number, RecordsDiff<R>> = atom('history', 0, {\n\t\thistoryLength: 1000,\n\t})\n\n\t/**\n\t * Reactive queries and indexes for efficiently accessing store data.\n\t * Provides methods for filtering, indexing, and subscribing to subsets of records.\n\t *\n\t * @example\n\t * ```ts\n\t * // Create an index by a property\n\t * const booksByAuthor = store.query.index('book', 'author')\n\t *\n\t * // Get records matching criteria\n\t * const inStockBooks = store.query.records('book', () => ({\n\t * inStock: { eq: true }\n\t * }))\n\t * ```\n\t *\n\t * @public\n\t * @readonly\n\t */\n\treadonly query: StoreQueries<R>\n\n\t/**\n\t * A set containing listeners that have been added to this store.\n\t *\n\t * @internal\n\t */\n\tprivate listeners = new Set<{ onHistory: StoreListener<R>; filters: StoreListenerFilters }>()\n\n\t/**\n\t * An array of history entries that have not yet been flushed.\n\t *\n\t * @internal\n\t */\n\tprivate historyAccumulator = new HistoryAccumulator<R>()\n\n\t/**\n\t * A reactor that responds to changes to the history by squashing the accumulated history and\n\t * notifying listeners of the changes.\n\t *\n\t * @internal\n\t */\n\tprivate historyReactor: Reactor\n\n\t/**\n\t * Cancels the history flush scheduled for the next frame, if any.\n\t *\n\t * @internal\n\t */\n\tprivate cancelHistoryReactor: null | (() => void) = null\n\n\t/**\n\t * The schema that defines the structure and validation rules for records in this store.\n\t *\n\t * @public\n\t */\n\treadonly schema: StoreSchema<R, Props>\n\n\t/**\n\t * Custom properties associated with this store instance.\n\t *\n\t * @public\n\t */\n\treadonly props: Props\n\n\t/**\n\t * A mapping of record scopes to the set of record type names that belong to each scope.\n\t * Used to filter records by their persistence and synchronization behavior.\n\t *\n\t * @public\n\t */\n\tpublic readonly scopedTypes: { readonly [K in RecordScope]: ReadonlySet<R['typeName']> }\n\n\t/**\n\t * Side effects manager that handles lifecycle events for record operations.\n\t * Allows registration of callbacks for create, update, delete, and validation events.\n\t *\n\t * @example\n\t * ```ts\n\t * store.sideEffects.registerAfterCreateHandler('book', (book) => {\n\t * console.log('Book created:', book.title)\n\t * })\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic readonly sideEffects = new StoreSideEffects<R>(this)\n\n\t/**\n\t * Creates a new Store instance.\n\t *\n\t * @example\n\t * ```ts\n\t * const store = new Store({\n\t * schema: StoreSchema.create({ book: Book }),\n\t * props: { appName: 'MyLibrary' },\n\t * initialData: savedData\n\t * })\n\t * ```\n\t *\n\t * @param config - Configuration object for the store\n\t */\n\tconstructor(config: {\n\t\t/** Optional unique identifier for the store */\n\t\tid?: string\n\t\t/** The store's initial data to populate on creation */\n\t\tinitialData?: SerializedStore<R>\n\t\t/** The schema defining record types, validation, and migrations */\n\t\tschema: StoreSchema<R, Props>\n\t\t/** Custom properties for the store instance */\n\t\tprops: Props\n\t}) {\n\t\tconst { initialData, schema, id } = config\n\n\t\tthis.id = id ?? uniqueId()\n\t\tthis.schema = schema\n\t\tthis.props = config.props\n\n\t\tif (initialData) {\n\t\t\tthis.records = new AtomMap(\n\t\t\t\t'store',\n\t\t\t\tobjectMapEntries(initialData).map(([id, record]) => [\n\t\t\t\t\tid,\n\t\t\t\t\tdevFreeze(this.schema.validateRecord(this, record, 'initialize', null)),\n\t\t\t\t])\n\t\t\t)\n\t\t} else {\n\t\t\tthis.records = new AtomMap('store')\n\t\t}\n\n\t\tthis.query = new StoreQueries<R>(this.records, this.history)\n\n\t\tthis.historyReactor = reactor(\n\t\t\t'Store.historyReactor',\n\t\t\t() => {\n\t\t\t\t// deref to make sure we're subscribed regardless of whether we need to propagate\n\t\t\t\tthis.history.get()\n\t\t\t\t// If we have accumulated history, flush it and update listeners\n\t\t\t\tthis._flushHistory()\n\t\t\t},\n\t\t\t{ scheduleEffect: (cb) => (this.cancelHistoryReactor = throttleToNextFrame(cb)) }\n\t\t)\n\t\tconst scopedTypes: { [K in RecordScope]: Set<R['typeName']> } = {\n\t\t\tdocument: new Set(),\n\t\t\tsession: new Set(),\n\t\t\tpresence: new Set(),\n\t\t}\n\t\tfor (const type of objectMapValues(this.schema.types)) {\n\t\t\tscopedTypes[type.scope].add(type.typeName)\n\t\t}\n\t\tthis.scopedTypes = scopedTypes\n\t}\n\n\tprivate isFlushingHistory = false\n\n\tpublic _flushHistory() {\n\t\t// If we have accumulated history, flush it and update listeners\n\t\tif (this.historyAccumulator.hasChanges()) {\n\t\t\tconst entries = this.historyAccumulator.flush()\n\t\t\tconst errors: unknown[] = []\n\t\t\tconst wasFlushingHistory = this.isFlushingHistory\n\t\t\tthis.isFlushingHistory = true\n\t\t\tfor (const { changes, source } of entries) {\n\t\t\t\t// Filtered diffs are computed at most once per scope per entry, and shared by every\n\t\t\t\t// listener watching that scope.\n\t\t\t\tconst scopedChanges = new Map<RecordScope, RecordsDiff<R> | null>()\n\t\t\t\tfor (const { onHistory, filters } of this.listeners) {\n\t\t\t\t\tif (filters.source !== 'all' && filters.source !== source) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tlet listenerChanges = changes\n\t\t\t\t\tif (filters.scope !== 'all') {\n\t\t\t\t\t\tif (!scopedChanges.has(filters.scope)) {\n\t\t\t\t\t\t\tscopedChanges.set(filters.scope, this.filterChangesByScope(changes, filters.scope))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst filtered = scopedChanges.get(filters.scope)\n\t\t\t\t\t\tif (!filtered) continue\n\t\t\t\t\t\tlistenerChanges = filtered\n\t\t\t\t\t}\n\t\t\t\t\t// The entries are already dequeued, so a listener that throws must not stop the others\n\t\t\t\t\t// (e.g. a sync client) from receiving them: deliver to all, then rethrow the first error.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tonHistory({ changes: listenerChanges, source })\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors.push(error)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.isFlushingHistory = wasFlushingHistory\n\t\t\tif (errors.length > 0) throw errors[0]\n\t\t}\n\t}\n\n\tdispose() {\n\t\t// Deliver what is still pending first: a change-set made in the same frame as the dispose\n\t\t// would otherwise never reach the listeners (e.g. a sync client) still attached to the store.\n\t\ttry {\n\t\t\tthis._flushHistory()\n\t\t} finally {\n\t\t\tthis.cancelHistoryReactor?.()\n\t\t}\n\t}\n\n\t/**\n\t * Filters out non-document changes from a diff. Returns null if there are no changes left.\n\t * @param change - the records diff\n\t * @param scope - the records scope\n\t * @returns\n\t */\n\tfilterChangesByScope(change: RecordsDiff<R>, scope: RecordScope) {\n\t\tconst result = {\n\t\t\tadded: filterEntries(change.added, (_, r) => this.scopedTypes[scope].has(r.typeName)),\n\t\t\tupdated: filterEntries(change.updated, (_, r) => this.scopedTypes[scope].has(r[1].typeName)),\n\t\t\tremoved: filterEntries(change.removed, (_, r) => this.scopedTypes[scope].has(r.typeName)),\n\t\t}\n\t\tif (isRecordsDiffEmpty(result)) {\n\t\t\treturn null\n\t\t}\n\t\treturn result\n\t}\n\n\t/**\n\t * Update the history with a diff of changes.\n\t *\n\t * @param changes - The changes to add to the history.\n\t */\n\tprivate updateHistory(changes: RecordsDiff<R>): void {\n\t\tthis.historyAccumulator.add({\n\t\t\tchanges,\n\t\t\tsource: this.isMergingRemoteChanges ? 'remote' : 'user',\n\t\t})\n\t\tif (this.listeners.size === 0) {\n\t\t\tthis.historyAccumulator.clear()\n\t\t}\n\t\t// Don't capture: a reaction that writes would otherwise re-run on every store change (S11).\n\t\tthis.history.set(this.history.__unsafe__getWithoutCapture() + 1, changes)\n\t}\n\n\tvalidate(phase: 'initialize' | 'createRecord' | 'updateRecord' | 'tests') {\n\t\tthis.allRecords().forEach((record) => this.schema.validateRecord(this, record, phase, null))\n\t}\n\n\t/**\n\t * Add or update records in the store. If a record with the same ID already exists, it will be updated.\n\t * Otherwise, a new record will be created.\n\t *\n\t * @example\n\t * ```ts\n\t * // Add new records\n\t * const book = Book.create({ title: 'Lathe Of Heaven', author: 'Le Guin' })\n\t * store.put([book])\n\t *\n\t * // Update existing record\n\t * store.put([{ ...book, title: 'The Lathe of Heaven' }])\n\t * ```\n\t *\n\t * @param records - The records to add or update\n\t * @param phaseOverride - Override the validation phase (used internally)\n\t * @public\n\t */\n\tput(records: R[], phaseOverride?: 'initialize'): void {\n\t\tthis.atomic(() => {\n\t\t\tconst updates: Record<IdOf<UnknownRecord>, [from: R, to: R]> = {}\n\t\t\tconst additions: Record<IdOf<UnknownRecord>, R> = {}\n\n\t\t\t// Iterate through all records, creating, updating or removing as needed\n\t\t\tlet record: R\n\n\t\t\tconst source = this.isMergingRemoteChanges ? 'remote' : 'user'\n\n\t\t\tfor (let i = 0, n = records.length; i < n; i++) {\n\t\t\t\trecord = records[i]\n\n\t\t\t\tconst initialValue = this.records.__unsafe__getWithoutCapture(record.id)\n\t\t\t\t// If we already have an atom for this record, update its value.\n\t\t\t\tif (initialValue) {\n\t\t\t\t\t// If we have a beforeUpdate callback, run it against the initial and next records\n\t\t\t\t\trecord = this.sideEffects.handleBeforeChange(initialValue, record, source)\n\n\t\t\t\t\t// Validate the record\n\t\t\t\t\tconst validated = this.schema.validateRecord(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tphaseOverride ?? 'updateRecord',\n\t\t\t\t\t\tinitialValue\n\t\t\t\t\t)\n\n\t\t\t\t\tif (validated === initialValue) continue\n\n\t\t\t\t\trecord = devFreeze(validated)\n\t\t\t\t\tthis.records.set(record.id, record)\n\n\t\t\t\t\tif (additions[record.id]) {\n\t\t\t\t\t\t// the same record was created earlier in this call: fold the update into it\n\t\t\t\t\t\tadditions[record.id] = record\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// an earlier update to the same record in this call owns the `from`\n\t\t\t\t\t\tconst from = updates[record.id]?.[0] ?? initialValue\n\t\t\t\t\t\tif (from === record) {\n\t\t\t\t\t\t\t// back to where it started within this call: nothing to record\n\t\t\t\t\t\t\tdelete updates[record.id]\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tupdates[record.id] = [from, record]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis.addDiffForAfterEvent(initialValue, record)\n\t\t\t\t} else {\n\t\t\t\t\trecord = this.sideEffects.handleBeforeCreate(record, source)\n\n\t\t\t\t\t// If we don't have an atom, create one.\n\n\t\t\t\t\t// Validate the record\n\t\t\t\t\trecord = this.schema.validateRecord(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\trecord as R,\n\t\t\t\t\t\tphaseOverride ?? 'createRecord',\n\t\t\t\t\t\tnull\n\t\t\t\t\t)\n\n\t\t\t\t\t// freeze it\n\t\t\t\t\trecord = devFreeze(record)\n\n\t\t\t\t\t// Mark the change as a new addition.\n\t\t\t\t\tadditions[record.id] = record\n\t\t\t\t\tthis.addDiffForAfterEvent(null, record)\n\n\t\t\t\t\tthis.records.set(record.id, record)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Validation may have left every record identical to before, in which case there is no\n\t\t\t// change-set to record.\n\t\t\tif (!hasAnyKey(additions) && !hasAnyKey(updates)) return\n\t\t\tthis.updateHistory({\n\t\t\t\tadded: additions,\n\t\t\t\tupdated: updates,\n\t\t\t\tremoved: {} as Record<IdOf<R>, R>,\n\t\t\t})\n\t\t})\n\t}\n\n\t/**\n\t * Remove records from the store by their IDs.\n\t *\n\t * @example\n\t * ```ts\n\t * // Remove a single record\n\t * store.remove([book.id])\n\t *\n\t * // Remove multiple records\n\t * store.remove([book1.id, book2.id, book3.id])\n\t * ```\n\t *\n\t * @param ids - The IDs of the records to remove\n\t * @public\n\t */\n\tremove(ids: IdOf<R>[]): void {\n\t\tthis.atomic(() => {\n\t\t\tconst toDelete = new Set<IdOf<R>>(ids)\n\t\t\tconst source = this.isMergingRemoteChanges ? 'remote' : 'user'\n\n\t\t\tif (this.sideEffects.isEnabled()) {\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tconst record = this.records.__unsafe__getWithoutCapture(id)\n\t\t\t\t\tif (!record) continue\n\n\t\t\t\t\tif (this.sideEffects.handleBeforeDelete(record, source) === false) {\n\t\t\t\t\t\ttoDelete.delete(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst actuallyDeleted = this.records.deleteMany(toDelete)\n\t\t\tif (actuallyDeleted.length === 0) return\n\n\t\t\tconst removed = {} as RecordsDiff<R>['removed']\n\t\t\tfor (const [id, record] of actuallyDeleted) {\n\t\t\t\tremoved[id] = record\n\t\t\t\tthis.addDiffForAfterEvent(record, null)\n\t\t\t}\n\n\t\t\t// Update the history with the removed records.\n\t\t\tthis.updateHistory({ added: {}, updated: {}, removed } as RecordsDiff<R>)\n\t\t})\n\t}\n\n\t/**\n\t * Get a record by its ID. This creates a reactive subscription to the record.\n\t *\n\t * @example\n\t * ```ts\n\t * const book = store.get(bookId)\n\t * if (book) {\n\t * console.log(book.title)\n\t * }\n\t * ```\n\t *\n\t * @param id - The ID of the record to get\n\t * @returns The record if it exists, undefined otherwise\n\t * @public\n\t */\n\tget<K extends IdOf<R>>(id: K): RecordFromId<K> | undefined {\n\t\treturn this.records.get(id) as RecordFromId<K> | undefined\n\t}\n\n\t/**\n\t * Get a record by its ID without creating a reactive subscription.\n\t * Use this when you need to access a record but don't want reactive updates.\n\t *\n\t * @example\n\t * ```ts\n\t * // Won't trigger reactive updates when this record changes\n\t * const book = store.unsafeGetWithoutCapture(bookId)\n\t * ```\n\t *\n\t * @param id - The ID of the record to get\n\t * @returns The record if it exists, undefined otherwise\n\t * @public\n\t */\n\tunsafeGetWithoutCapture<K extends IdOf<R>>(id: K): RecordFromId<K> | undefined {\n\t\treturn this.records.__unsafe__getWithoutCapture(id) as RecordFromId<K> | undefined\n\t}\n\n\t/**\n\t * Serialize the store's records to a plain JavaScript object.\n\t * Only includes records matching the specified scope.\n\t *\n\t * @example\n\t * ```ts\n\t * // Serialize only document records (default)\n\t * const documentData = store.serialize('document')\n\t *\n\t * // Serialize all records\n\t * const allData = store.serialize('all')\n\t * ```\n\t *\n\t * @param scope - The scope of records to serialize. Defaults to 'document'\n\t * @returns The serialized store data\n\t * @public\n\t */\n\tserialize(scope: RecordScope | 'all' = 'document'): SerializedStore<R> {\n\t\tconst result = {} as SerializedStore<R>\n\t\tfor (const [id, record] of this.records) {\n\t\t\tif (scope === 'all' || this.scopedTypes[scope].has(record.typeName)) {\n\t\t\t\tresult[id as IdOf<R>] = record\n\t\t\t}\n\t\t}\n\t\treturn result\n\t}\n\n\t/**\n\t * Get a serialized snapshot of the store and its schema.\n\t * This includes both the data and schema information needed for proper migration.\n\t *\n\t * @example\n\t * ```ts\n\t * const snapshot = store.getStoreSnapshot()\n\t * localStorage.setItem('myApp', JSON.stringify(snapshot))\n\t *\n\t * // Later...\n\t * const saved = JSON.parse(localStorage.getItem('myApp'))\n\t * store.loadStoreSnapshot(saved)\n\t * ```\n\t *\n\t * @param scope - The scope of records to serialize. Defaults to 'document'\n\t * @returns A snapshot containing both store data and schema information\n\t * @public\n\t */\n\tgetStoreSnapshot(scope: RecordScope | 'all' = 'document'): StoreSnapshot<R> {\n\t\treturn {\n\t\t\tstore: this.serialize(scope),\n\t\t\tschema: this.schema.serialize(),\n\t\t}\n\t}\n\n\t/**\n\t * Migrate a serialized snapshot to the current schema version.\n\t * This applies any necessary migrations to bring old data up to date.\n\t *\n\t * @example\n\t * ```ts\n\t * const oldSnapshot = JSON.parse(localStorage.getItem('myApp'))\n\t * const migratedSnapshot = store.migrateSnapshot(oldSnapshot)\n\t * ```\n\t *\n\t * @param snapshot - The snapshot to migrate\n\t * @returns The migrated snapshot with current schema version\n\t * @throws Error if migration fails\n\t * @public\n\t */\n\tmigrateSnapshot(snapshot: StoreSnapshot<R>): StoreSnapshot<R> {\n\t\tconst migrationResult = this.schema.migrateStoreSnapshot(snapshot)\n\n\t\tif (migrationResult.type === 'error') {\n\t\t\tthrow new Error(`Failed to migrate snapshot: ${migrationResult.reason}`)\n\t\t}\n\n\t\treturn {\n\t\t\tstore: migrationResult.value,\n\t\t\tschema: this.schema.serialize(),\n\t\t}\n\t}\n\n\t/**\n\t * Load a serialized snapshot into the store, replacing all current data.\n\t * The snapshot will be automatically migrated to the current schema version if needed.\n\t *\n\t * @example\n\t * ```ts\n\t * const snapshot = JSON.parse(localStorage.getItem('myApp'))\n\t * store.loadStoreSnapshot(snapshot)\n\t * ```\n\t *\n\t * @param snapshot - The snapshot to load\n\t * @throws Error if migration fails or snapshot is invalid\n\t * @public\n\t */\n\tloadStoreSnapshot(snapshot: StoreSnapshot<R>): void {\n\t\tconst migrationResult = this.schema.migrateStoreSnapshot(snapshot)\n\n\t\tif (migrationResult.type === 'error') {\n\t\t\tthrow new Error(`Failed to migrate snapshot: ${migrationResult.reason}`)\n\t\t}\n\n\t\tconst prevSideEffectsEnabled = this.sideEffects.isEnabled()\n\t\ttry {\n\t\t\tthis.sideEffects.setIsEnabled(false)\n\t\t\tthis.atomic(() => {\n\t\t\t\tthis.clear()\n\t\t\t\tthis.put(Object.values(migrationResult.value))\n\t\t\t\tthis.ensureStoreIsUsable()\n\t\t\t}, false)\n\t\t} finally {\n\t\t\tthis.sideEffects.setIsEnabled(prevSideEffectsEnabled)\n\t\t}\n\t}\n\n\t/**\n\t * Get an array of all records in the store.\n\t *\n\t * @example\n\t * ```ts\n\t * const allRecords = store.allRecords()\n\t * const books = allRecords.filter(r => r.typeName === 'book')\n\t * ```\n\t *\n\t * @returns An array containing all records in the store\n\t * @public\n\t */\n\tallRecords(): R[] {\n\t\treturn Array.from(this.records.values())\n\t}\n\n\t/**\n\t * Remove all records from the store.\n\t *\n\t * @example\n\t * ```ts\n\t * store.clear()\n\t * console.log(store.allRecords().length) // 0\n\t * ```\n\t *\n\t * @public\n\t */\n\tclear(): void {\n\t\tthis.remove(Array.from(this.records.keys()))\n\t}\n\n\t/**\n\t * Update a single record using an updater function. To update multiple records at once,\n\t * use the `update` method of the `TypedStore` class.\n\t *\n\t * @example\n\t * ```ts\n\t * store.update(book.id, (book) => ({\n\t * ...book,\n\t * title: 'Updated Title'\n\t * }))\n\t * ```\n\t *\n\t * @param id - The ID of the record to update\n\t * @param updater - A function that receives the current record and returns the updated record\n\t * @public\n\t */\n\tupdate<K extends IdOf<R>>(id: K, updater: (record: RecordFromId<K>) => RecordFromId<K>) {\n\t\tconst existing = this.unsafeGetWithoutCapture(id)\n\t\tif (!existing) {\n\t\t\tconsole.error(`Record ${id} not found. This is probably an error`)\n\t\t\treturn\n\t\t}\n\n\t\tthis.put([updater(existing) as any])\n\t}\n\n\t/**\n\t * Check whether a record with the given ID exists in the store.\n\t *\n\t * @example\n\t * ```ts\n\t * if (store.has(bookId)) {\n\t * console.log('Book exists!')\n\t * }\n\t * ```\n\t *\n\t * @param id - The ID of the record to check\n\t * @returns True if the record exists, false otherwise\n\t * @public\n\t */\n\thas<K extends IdOf<R>>(id: K): boolean {\n\t\treturn this.records.has(id)\n\t}\n\n\t/**\n\t * Add a listener that will be called when the store changes.\n\t * Returns a function to remove the listener.\n\t *\n\t * @example\n\t * ```ts\n\t * const removeListener = store.listen((entry) => {\n\t * console.log('Changes:', entry.changes)\n\t * console.log('Source:', entry.source)\n\t * })\n\t *\n\t * // Listen only to user changes to document records\n\t * const removeDocumentListener = store.listen(\n\t * (entry) => console.log('Document changed:', entry),\n\t * { source: 'user', scope: 'document' }\n\t * )\n\t *\n\t * // Later, remove the listener\n\t * removeListener()\n\t * ```\n\t *\n\t * @param onHistory - The listener function to call when changes occur\n\t * @param filters - Optional filters to control when the listener is called\n\t * @returns A function that removes the listener when called\n\t * @public\n\t */\n\tlisten(onHistory: StoreListener<R>, filters?: Partial<StoreListenerFilters>) {\n\t\t// flush history so that this listener's history starts from exactly now\n\t\tthis._flushHistory()\n\n\t\tconst listener = {\n\t\t\tonHistory,\n\t\t\tfilters: {\n\t\t\t\tsource: filters?.source ?? 'all',\n\t\t\t\tscope: filters?.scope ?? 'all',\n\t\t\t},\n\t\t}\n\n\t\tif (!this.historyReactor.scheduler.isActivelyListening) {\n\t\t\tthis.historyReactor.start()\n\t\t\tthis.historyReactor.scheduler.execute()\n\t\t}\n\n\t\tthis.listeners.add(listener)\n\n\t\treturn () => {\n\t\t\t// Flush so this listener's history ends at exactly now, but not from inside a flush:\n\t\t\t// the other listeners would then receive changes made during that flush before the\n\t\t\t// entry they are still being handed.\n\t\t\tif (!this.isFlushingHistory) {\n\t\t\t\ttry {\n\t\t\t\t\tthis._flushHistory()\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Removers run in teardown loops (Editor.dispose, TLSyncClient.close); throwing here\n\t\t\t\t\t// would skip the cleanups that follow.\n\t\t\t\t\tconsole.error(error)\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.listeners.delete(listener)\n\n\t\t\tif (this.listeners.size === 0) {\n\t\t\t\tthis.historyReactor.stop()\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate isMergingRemoteChanges = false\n\n\t/**\n\t * Merge changes from a remote source. Changes made within the provided function\n\t * will be marked with source 'remote' instead of 'user'.\n\t *\n\t * @example\n\t * ```ts\n\t * // Changes from sync/collaboration\n\t * store.mergeRemoteChanges(() => {\n\t * store.put(remoteRecords)\n\t * store.remove(deletedIds)\n\t * })\n\t * ```\n\t *\n\t * @param fn - A function that applies the remote changes\n\t * @public\n\t */\n\tmergeRemoteChanges(fn: () => void) {\n\t\tif (this.isMergingRemoteChanges) {\n\t\t\treturn fn()\n\t\t}\n\n\t\tif (this._isInAtomicOp) {\n\t\t\tthrow new Error('Cannot merge remote changes while in atomic operation')\n\t\t}\n\n\t\ttry {\n\t\t\tthis.atomic(fn, true, true)\n\t\t} finally {\n\t\t\tthis.ensureStoreIsUsable()\n\t\t}\n\t}\n\n\t/**\n\t * Run `fn` and return a {@link RecordsDiff} of the changes that occurred as a result.\n\t */\n\textractingChanges(fn: () => void): RecordsDiff<R> {\n\t\tconst changes: Array<RecordsDiff<R>> = []\n\t\tconst dispose = this.historyAccumulator.addInterceptor((entry) => changes.push(entry.changes))\n\t\ttry {\n\t\t\ttransact(fn)\n\t\t\treturn squashRecordDiffs(changes)\n\t\t} finally {\n\t\t\tdispose()\n\t\t}\n\t}\n\n\tapplyDiff(\n\t\tdiff: RecordsDiff<R>,\n\t\t{\n\t\t\trunCallbacks = true,\n\t\t\tignoreEphemeralKeys = false,\n\t\t}: { runCallbacks?: boolean; ignoreEphemeralKeys?: boolean } = {}\n\t) {\n\t\tthis.atomic(() => {\n\t\t\tconst toPut = objectMapValues(diff.added)\n\n\t\t\tfor (const [_from, to] of objectMapValues(diff.updated)) {\n\t\t\t\tconst type = this.schema.getType(to.typeName)\n\t\t\t\tif (ignoreEphemeralKeys && type.ephemeralKeySet.size) {\n\t\t\t\t\tconst existing = this.unsafeGetWithoutCapture(to.id)\n\t\t\t\t\tif (!existing) {\n\t\t\t\t\t\ttoPut.push(to)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tlet changed: R | null = null\n\t\t\t\t\tfor (const [key, value] of Object.entries(to)) {\n\t\t\t\t\t\tif (type.ephemeralKeySet.has(key) || Object.is(value, getOwnProperty(existing, key))) {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!changed) changed = { ...existing } as R\n\t\t\t\t\t\t;(changed as any)[key] = value\n\t\t\t\t\t}\n\t\t\t\t\t// a key the update removed (present before, absent in `to`) is a change too\n\t\t\t\t\tfor (const key of Object.keys(existing)) {\n\t\t\t\t\t\tif (type.ephemeralKeySet.has(key) || Object.hasOwn(to, key)) continue\n\t\t\t\t\t\tif (!changed) changed = { ...existing } as R\n\t\t\t\t\t\tdelete (changed as any)[key]\n\t\t\t\t\t}\n\t\t\t\t\tif (changed) toPut.push(changed)\n\t\t\t\t} else {\n\t\t\t\t\ttoPut.push(to)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst toRemove = objectMapKeys(diff.removed)\n\t\t\tif (toPut.length) {\n\t\t\t\tthis.put(toPut)\n\t\t\t}\n\t\t\tif (toRemove.length) {\n\t\t\t\tthis.remove(toRemove)\n\t\t\t}\n\t\t}, runCallbacks)\n\t}\n\n\t/**\n\t * Create a cache based on values in the store. Pass in a function that takes and ID and a\n\t * signal for the underlying record. Return a signal (usually a computed) for the cached value.\n\t * For simple derivations, use {@link Store.createComputedCache}. This function is useful if you\n\t * need more precise control over intermediate values.\n\t */\n\tcreateCache<Result, Record extends R = R>(\n\t\tcreate: (id: IdOf<Record>, recordSignal: Signal<R>) => Signal<Result>\n\t) {\n\t\tconst cache = new WeakCache<Atom<any>, Signal<Result>>()\n\t\treturn {\n\t\t\tget: (id: IdOf<Record>) => {\n\t\t\t\tconst atom = this.records.getAtom(id)\n\t\t\t\tif (!atom) return undefined\n\t\t\t\treturn cache.get(atom, () => create(id, atom as Signal<R>)).get()\n\t\t\t},\n\t\t}\n\t}\n\n\t/**\n\t * Create a computed cache.\n\t *\n\t * @param name - The name of the derivation cache.\n\t * @param derive - A function used to derive the value of the cache.\n\t * @param opts - Options for the computed cache.\n\t * @public\n\t */\n\tcreateComputedCache<Result, Record extends R = R>(\n\t\tname: string,\n\t\tderive: (record: Record) => Result | undefined,\n\t\topts?: CreateComputedCacheOpts<Result, Record>\n\t): ComputedCache<Result, Record> {\n\t\treturn this.createCache((id, record) => {\n\t\t\tconst recordSignal = opts?.areRecordsEqual\n\t\t\t\t? computed(`${name}:${id}:isEqual`, () => record.get(), { isEqual: opts.areRecordsEqual })\n\t\t\t\t: record\n\n\t\t\treturn computed<Result | undefined>(\n\t\t\t\tname + ':' + id,\n\t\t\t\t() => {\n\t\t\t\t\treturn derive(recordSignal.get() as Record)\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tisEqual: opts?.areResultsEqual,\n\t\t\t\t}\n\t\t\t)\n\t\t})\n\t}\n\n\tprivate _integrityChecker?: () => void | undefined\n\n\t/** @internal */\n\tensureStoreIsUsable() {\n\t\tthis.atomic(() => {\n\t\t\tthis._integrityChecker ??= this.schema.createIntegrityChecker(this)\n\t\t\tthis._integrityChecker?.()\n\t\t})\n\t}\n\n\tprivate _isPossiblyCorrupted = false\n\t/** @internal */\n\tmarkAsPossiblyCorrupted() {\n\t\tthis._isPossiblyCorrupted = true\n\t}\n\t/** @internal */\n\tisPossiblyCorrupted() {\n\t\treturn this._isPossiblyCorrupted\n\t}\n\n\tprivate pendingAfterEvents: Map<IdOf<R>, { before: R | null; after: R | null }> | null = null\n\tprivate addDiffForAfterEvent(before: R | null, after: R | null) {\n\t\tassert(this.pendingAfterEvents, 'must be in event operation')\n\t\tif (before === after) return\n\t\tif (before && after) assert(before.id === after.id)\n\t\tif (!before && !after) return\n\t\tconst id = (before || after)!.id\n\t\tconst existing = this.pendingAfterEvents.get(id)\n\t\tif (existing) {\n\t\t\texisting.after = after\n\t\t} else {\n\t\t\tthis.pendingAfterEvents.set(id, { before, after })\n\t\t}\n\t}\n\tprivate flushAtomicCallbacks(isMergingRemoteChanges: boolean) {\n\t\tlet updateDepth = 0\n\t\tlet source: ChangeSource = isMergingRemoteChanges ? 'remote' : 'user'\n\t\twhile (this.pendingAfterEvents) {\n\t\t\tconst events = this.pendingAfterEvents\n\t\t\tthis.pendingAfterEvents = null\n\n\t\t\tif (!this.sideEffects.isEnabled()) continue\n\n\t\t\tupdateDepth++\n\t\t\tif (updateDepth > 100) {\n\t\t\t\tthrow new Error('Maximum store update depth exceeded, bailing out')\n\t\t\t}\n\n\t\t\tfor (const { before, after } of events.values()) {\n\t\t\t\tif (before && after && before !== after && !isEqual(before, after)) {\n\t\t\t\t\tthis.sideEffects.handleAfterChange(before, after, source)\n\t\t\t\t} else if (before && !after) {\n\t\t\t\t\tthis.sideEffects.handleAfterDelete(before, source)\n\t\t\t\t} else if (!before && after) {\n\t\t\t\t\tthis.sideEffects.handleAfterCreate(after, source)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!this.pendingAfterEvents) {\n\t\t\t\tthis.sideEffects.handleOperationComplete(source)\n\t\t\t}\n\t\t\t// Whatever the after-handlers or the operation-complete handlers changed in response to\n\t\t\t// a remote operation is not itself remote: later rounds are attributed to 'user'.\n\t\t\tsource = 'user'\n\t\t}\n\t}\n\tprivate _isInAtomicOp = false\n\t/** @internal */\n\tatomic<T>(fn: () => T, runCallbacks = true, isMergingRemoteChanges = false): T {\n\t\treturn transact(() => {\n\t\t\tif (this._isInAtomicOp) {\n\t\t\t\tif (!this.pendingAfterEvents) this.pendingAfterEvents = new Map()\n\t\t\t\tconst prevSideEffectsEnabled = this.sideEffects.isEnabled()\n\t\t\t\tassert(!isMergingRemoteChanges, 'cannot call mergeRemoteChanges while in atomic operation')\n\t\t\t\ttry {\n\t\t\t\t\t// if we are in an atomic context with side effects ON allow switching before* callbacks OFF.\n\t\t\t\t\t// but don't allow switching them ON if they had been marked OFF before.\n\t\t\t\t\tif (prevSideEffectsEnabled && !runCallbacks) {\n\t\t\t\t\t\tthis.sideEffects.setIsEnabled(false)\n\t\t\t\t\t}\n\t\t\t\t\treturn fn()\n\t\t\t\t} finally {\n\t\t\t\t\tthis.sideEffects.setIsEnabled(prevSideEffectsEnabled)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.pendingAfterEvents = new Map()\n\t\t\tconst prevSideEffectsEnabled = this.sideEffects.isEnabled()\n\t\t\t// an operation may switch side effects off, but never on while they are disabled\n\t\t\tthis.sideEffects.setIsEnabled(runCallbacks && prevSideEffectsEnabled)\n\t\t\tthis._isInAtomicOp = true\n\n\t\t\tif (isMergingRemoteChanges) {\n\t\t\t\tthis.isMergingRemoteChanges = true\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst result = fn()\n\t\t\t\tthis.isMergingRemoteChanges = false\n\n\t\t\t\tthis.flushAtomicCallbacks(isMergingRemoteChanges)\n\n\t\t\t\treturn result\n\t\t\t} finally {\n\t\t\t\tthis.pendingAfterEvents = null\n\t\t\t\tthis.sideEffects.setIsEnabled(prevSideEffectsEnabled)\n\t\t\t\tthis._isInAtomicOp = false\n\t\t\t\tthis.isMergingRemoteChanges = false\n\t\t\t}\n\t\t})\n\t}\n\n\t/** @internal */\n\taddHistoryInterceptor(fn: (entry: HistoryEntry<R>, source: ChangeSource) => void) {\n\t\treturn this.historyAccumulator.addInterceptor((entry) =>\n\t\t\tfn(entry, this.isMergingRemoteChanges ? 'remote' : 'user')\n\t\t)\n\t}\n}\n\n/**\n * Collect and squash history entries by their adjacent sources.\n * Adjacent entries from the same source are combined into a single entry.\n *\n * For example: [user, user, remote, remote, user] becomes [user, remote, user]\n *\n * @example\n * ```ts\n * const entries = [\n * { source: 'user', changes: userChanges1 },\n * { source: 'user', changes: userChanges2 },\n * { source: 'remote', changes: remoteChanges }\n * ]\n *\n * const squashed = squashHistoryEntries(entries)\n * // Results in 2 entries: combined user changes + remote changes\n * ```\n *\n * @param entries - The array of history entries to squash\n * @returns An array of squashed history entries\n * @public\n */\nfunction squashHistoryEntries<T extends UnknownRecord>(\n\tentries: HistoryEntry<T>[]\n): HistoryEntry<T>[] {\n\tif (entries.length === 0) return []\n\n\tconst chunked: HistoryEntry<T>[][] = []\n\tlet chunk: HistoryEntry<T>[] = [entries[0]]\n\tlet entry: HistoryEntry<T>\n\n\tfor (let i = 1, n = entries.length; i < n; i++) {\n\t\tentry = entries[i]\n\t\tif (chunk[0].source !== entry.source) {\n\t\t\tchunked.push(chunk)\n\t\t\tchunk = []\n\t\t}\n\t\tchunk.push(entry)\n\t}\n\t// Push the last chunk\n\tchunked.push(chunk)\n\n\treturn devFreeze(\n\t\tchunked.map((chunk) => ({\n\t\t\tsource: chunk[0].source,\n\t\t\t// a single-entry chunk needs no squashing \u2014 skip the O(N) copy of its diff\n\t\t\tchanges:\n\t\t\t\tchunk.length === 1 ? chunk[0].changes : squashRecordDiffs(chunk.map((e) => e.changes)),\n\t\t}))\n\t)\n}\n\n/**\n * Internal class that accumulates history entries before they are flushed to listeners.\n * Handles batching and squashing of adjacent entries from the same source.\n *\n * @internal\n */\nclass HistoryAccumulator<T extends UnknownRecord> {\n\tprivate _history: HistoryEntry<T>[] = []\n\n\tprivate _interceptors: Set<(entry: HistoryEntry<T>) => void> = new Set()\n\n\t/**\n\t * Add an interceptor that will be called for each history entry.\n\t * Returns a function to remove the interceptor.\n\t */\n\taddInterceptor(fn: (entry: HistoryEntry<T>) => void) {\n\t\tthis._interceptors.add(fn)\n\t\treturn () => {\n\t\t\tthis._interceptors.delete(fn)\n\t\t}\n\t}\n\n\t/**\n\t * Add a history entry to the accumulator.\n\t * Calls all registered interceptors with the entry.\n\t */\n\tadd(entry: HistoryEntry<T>) {\n\t\tthis._history.push(entry)\n\t\tfor (const interceptor of this._interceptors) {\n\t\t\tinterceptor(entry)\n\t\t}\n\t}\n\n\t/**\n\t * Flush all accumulated history entries, squashing adjacent entries from the same source.\n\t * Clears the internal history buffer.\n\t */\n\tflush() {\n\t\tconst history = squashHistoryEntries(this._history)\n\t\tthis._history = []\n\t\treturn history\n\t}\n\n\t/**\n\t * Clear all accumulated history entries without flushing.\n\t */\n\tclear() {\n\t\tthis._history = []\n\t}\n\n\t/**\n\t * Check if there are any accumulated history entries.\n\t */\n\thasChanges() {\n\t\treturn this._history.length > 0\n\t}\n}\n\n/**\n * A store or an object containing a store.\n * This type is used for APIs that can accept either a store directly or an object with a store property.\n *\n * @example\n * ```ts\n * function useStore(storeOrObject: StoreObject<MyRecord>) {\n * const store = storeOrObject instanceof Store ? storeOrObject : storeOrObject.store\n * return store\n * }\n * ```\n *\n * @public\n */\nexport type StoreObject<R extends UnknownRecord> = Store<R> | { store: Store<R> }\n/**\n * Extract the record type from a StoreObject.\n *\n * @example\n * ```ts\n * type MyStoreObject = { store: Store<Book | Author> }\n * type Records = StoreObjectRecordType<MyStoreObject> // Book | Author\n * ```\n *\n * @public\n */\nexport type StoreObjectRecordType<Context extends StoreObject<any>> =\n\tContext extends Store<infer R> ? R : Context extends { store: Store<infer R> } ? R : never\n\n/**\n * Create a computed cache that works with any StoreObject (store or object containing a store).\n * This is a standalone version of Store.createComputedCache that can work with multiple store instances.\n *\n * @example\n * ```ts\n * const expensiveCache = createComputedCache(\n * 'expensiveData',\n * (context: { store: Store<Book> }, book: Book) => {\n * return performExpensiveCalculation(book)\n * }\n * )\n *\n * // Use with different store instances\n * const result1 = expensiveCache.get(storeObject1, bookId)\n * const result2 = expensiveCache.get(storeObject2, bookId)\n * ```\n *\n * @param name - A unique name for the cache (used for debugging)\n * @param derive - Function that derives a value from the context and record\n * @param opts - Optional configuration for equality checks\n * @returns A cache that can be used with multiple store instances\n * @public\n */\nexport function createComputedCache<\n\tContext extends StoreObject<any>,\n\tResult,\n\tRecord extends StoreObjectRecordType<Context> = StoreObjectRecordType<Context>,\n>(\n\tname: string,\n\tderive: (context: Context, record: Record) => Result | undefined,\n\topts?: CreateComputedCacheOpts<Result, Record>\n) {\n\tconst cache = new WeakCache<Context, ComputedCache<Result, Record>>()\n\treturn {\n\t\tget(context: Context, id: IdOf<Record>) {\n\t\t\tconst computedCache = cache.get(context, () => {\n\t\t\t\tconst store = (context instanceof Store ? context : context.store) as Store<Record>\n\t\t\t\treturn store.createComputedCache(name, (record) => derive(context, record), opts)\n\t\t\t})\n\t\t\treturn computedCache.get(id)\n\t\t},\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tAtom,\n\tReactor,\n\tSignal,\n\tatom,\n\tcomputed,\n\tisUninitialized,\n\treactor,\n\ttransact,\n} from '@tldraw/state'\nimport {\n\tWeakCache,\n\tassert,\n\tfilterEntries,\n\tgetOwnProperty,\n\tisEqual,\n\tobjectMapEntries,\n\tobjectMapKeys,\n\tobjectMapValues,\n\tthrottleToNextFrame,\n\tuniqueId,\n} from '@tldraw/utils'\nimport { AtomMap } from './AtomMap'\nimport { IdOf, RecordId, UnknownRecord } from './BaseRecord'\nimport { devFreeze } from './devFreeze'\nimport { hasAnyKey, isRecordsDiffEmpty, RecordsDiff, squashRecordDiffs } from './RecordsDiff'\nimport { RecordScope } from './RecordType'\nimport { StoreQueries } from './StoreQueries'\nimport { SerializedSchema, StoreSchema } from './StoreSchema'\nimport { StoreSideEffects } from './StoreSideEffects'\n\n// The value of a cached computed whose record has been deleted (see `createComputedCache`). It never\n// reaches callers: `get` resolves the record's atom first, and there is none for a deleted id.\nconst DELETED_RECORD = Symbol('deleted record')\n\n/**\n * Extracts the record type from a record ID type.\n *\n * @example\n * ```ts\n * type BookId = RecordId<Book>\n * type BookType = RecordFromId<BookId> // Book\n * ```\n *\n * @public\n */\nexport type RecordFromId<K extends RecordId<UnknownRecord>> =\n\tK extends RecordId<infer R> ? R : never\n\n/**\n * A diff describing the changes to a collection.\n *\n * @example\n * ```ts\n * const diff: CollectionDiff<string> = {\n * added: new Set(['newItem']),\n * removed: new Set(['oldItem'])\n * }\n * ```\n *\n * @public\n */\nexport interface CollectionDiff<T> {\n\t/** Items that were added to the collection */\n\tadded?: Set<T>\n\t/** Items that were removed from the collection */\n\tremoved?: Set<T>\n}\n\n/**\n * The source of a change to the store.\n * - `'user'` - Changes originating from local user actions\n * - `'remote'` - Changes originating from remote synchronization\n *\n * @public\n */\nexport type ChangeSource = 'user' | 'remote'\n\n/**\n * Filters for store listeners to control which changes trigger the listener.\n *\n * @example\n * ```ts\n * const filters: StoreListenerFilters = {\n * source: 'user', // Only listen to user changes\n * scope: 'document' // Only listen to document-scoped records\n * }\n * ```\n *\n * @public\n */\nexport interface StoreListenerFilters {\n\t/** Filter by the source of changes */\n\tsource: ChangeSource | 'all'\n\t/** Filter by the scope of records */\n\tscope: RecordScope | 'all'\n}\n\n/**\n * An entry containing changes that originated either by user actions or remote changes.\n * History entries are used to track and replay changes to the store.\n *\n * @example\n * ```ts\n * const entry: HistoryEntry<Book> = {\n * changes: {\n * added: { 'book:123': bookRecord },\n * updated: {},\n * removed: {}\n * },\n * source: 'user'\n * }\n * ```\n *\n * @public\n */\nexport interface HistoryEntry<R extends UnknownRecord = UnknownRecord> {\n\t/** The changes that occurred in this history entry */\n\tchanges: RecordsDiff<R>\n\t/** The source of these changes */\n\tsource: ChangeSource\n}\n\n/**\n * A function that will be called when the history changes.\n *\n * @example\n * ```ts\n * const listener: StoreListener<Book> = (entry) => {\n * console.log('Changes:', entry.changes)\n * console.log('Source:', entry.source)\n * }\n *\n * store.listen(listener)\n * ```\n *\n * @param entry - The history entry containing the changes\n *\n * @public\n */\nexport type StoreListener<R extends UnknownRecord> = (entry: HistoryEntry<R>) => void\n\n/**\n * A computed cache that stores derived data for records.\n * The cache automatically updates when underlying records change and cleans up when records are deleted.\n *\n * @example\n * ```ts\n * const expensiveCache = store.createComputedCache(\n * 'expensive',\n * (book: Book) => performExpensiveCalculation(book)\n * )\n *\n * const result = expensiveCache.get(bookId)\n * ```\n *\n * @public\n */\nexport interface ComputedCache<Data, R extends UnknownRecord> {\n\t/**\n\t * Get the cached data for a record by its ID.\n\t *\n\t * @param id - The ID of the record\n\t * @returns The cached data or undefined if the record doesn't exist\n\t */\n\tget(id: IdOf<R>): Data | undefined\n}\n\n/**\n * Options for creating a computed cache.\n *\n * @example\n * ```ts\n * const options: CreateComputedCacheOpts<string[], Book> = {\n * areRecordsEqual: (a, b) => a.title === b.title,\n * areResultsEqual: (a, b) => JSON.stringify(a) === JSON.stringify(b)\n * }\n * ```\n *\n * @public\n */\nexport interface CreateComputedCacheOpts<Data, R extends UnknownRecord> {\n\t/** Custom equality function for comparing records */\n\tareRecordsEqual?(a: R, b: R): boolean\n\t/** Custom equality function for comparing results */\n\tareResultsEqual?(a: Data, b: Data): boolean\n}\n\n/**\n * A serialized snapshot of the record store's values.\n * This is a plain JavaScript object that can be saved to storage or transmitted over the network.\n *\n * @example\n * ```ts\n * const serialized: SerializedStore<Book> = {\n * 'book:123': { id: 'book:123', typeName: 'book', title: 'The Lathe of Heaven' },\n * 'book:456': { id: 'book:456', typeName: 'book', title: 'The Left Hand of Darkness' }\n * }\n * ```\n *\n * @public\n */\nexport type SerializedStore<R extends UnknownRecord> = Record<IdOf<R>, R>\n\n/**\n * A snapshot of the store including both data and schema information.\n * This enables proper migration when loading data from different schema versions.\n *\n * @example\n * ```ts\n * const snapshot = store.getStoreSnapshot()\n * // Later...\n * store.loadStoreSnapshot(snapshot)\n * ```\n *\n * @public\n */\nexport interface StoreSnapshot<R extends UnknownRecord> {\n\t/** The serialized store data */\n\tstore: SerializedStore<R>\n\t/** The serialized schema information */\n\tschema: SerializedSchema\n}\n\n/**\n * A validator for store records that ensures data integrity.\n * Validators are called when records are created or updated.\n *\n * @example\n * ```ts\n * const bookValidator: StoreValidator<Book> = {\n * validate(record: unknown): Book {\n * // Validate and return the record\n * if (typeof record !== 'object' || !record.title) {\n * throw new Error('Invalid book')\n * }\n * return record as Book\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface StoreValidator<R extends UnknownRecord> {\n\t/**\n\t * Validate a record.\n\t *\n\t * @param record - The record to validate\n\t * @returns The validated record\n\t * @throws When validation fails\n\t */\n\tvalidate(record: unknown): R\n\t/**\n\t * Validate a record using a known good version for reference.\n\t *\n\t * @param knownGoodVersion - A known valid version of the record\n\t * @param record - The record to validate\n\t * @returns The validated record\n\t */\n\tvalidateUsingKnownGoodVersion?(knownGoodVersion: R, record: unknown): R\n}\n\n/**\n * A map of validators for each record type in the store.\n *\n * @example\n * ```ts\n * const validators: StoreValidators<Book | Author> = {\n * book: bookValidator,\n * author: authorValidator\n * }\n * ```\n *\n * @public\n */\nexport type StoreValidators<R extends UnknownRecord> = {\n\t[K in R['typeName']]: StoreValidator<Extract<R, { typeName: K }>>\n}\n\n/**\n * Information about an error that occurred in the store.\n *\n * @example\n * ```ts\n * const error: StoreError = {\n * error: new Error('Validation failed'),\n * phase: 'updateRecord',\n * recordBefore: oldRecord,\n * recordAfter: newRecord,\n * isExistingValidationIssue: false\n * }\n * ```\n *\n * @public\n */\nexport interface StoreError {\n\t/** The error that occurred */\n\terror: Error\n\t/** The phase during which the error occurred */\n\tphase: 'initialize' | 'createRecord' | 'updateRecord' | 'tests'\n\t/** The record state before the operation (if applicable) */\n\trecordBefore?: unknown\n\t/** The record state after the operation */\n\trecordAfter: unknown\n\t/** Whether this is an existing validation issue */\n\tisExistingValidationIssue: boolean\n}\n\n/**\n * Extract the record type from a Store type.\n * Used internally for type inference.\n *\n * @internal\n */\nexport type StoreRecord<S extends Store<any>> = S extends Store<infer R> ? R : never\n\n/**\n * A reactive store that manages collections of typed records.\n *\n * The Store is the central container for your application's data, providing:\n * - Reactive state management with automatic updates\n * - Type-safe record operations\n * - History tracking and change notifications\n * - Schema validation and migrations\n * - Side effects and business logic hooks\n * - Efficient querying and indexing\n *\n * @example\n * ```ts\n * // Create a store with schema\n * const schema = StoreSchema.create({\n * book: Book,\n * author: Author\n * })\n *\n * const store = new Store({\n * schema,\n * props: {}\n * })\n *\n * // Add records\n * const book = Book.create({ title: 'The Lathe of Heaven', author: 'Le Guin' })\n * store.put([book])\n *\n * // Listen to changes\n * store.listen((entry) => {\n * console.log('Changes:', entry.changes)\n * })\n * ```\n *\n * @public\n */\nexport class Store<R extends UnknownRecord = UnknownRecord, Props = unknown> {\n\t/**\n\t * The unique identifier of the store instance.\n\t *\n\t * @public\n\t */\n\tpublic readonly id: string\n\t/**\n\t * An AtomMap containing the stores records.\n\t *\n\t * @internal\n\t * @readonly\n\t */\n\tprivate readonly records: AtomMap<IdOf<R>, R>\n\n\t/**\n\t * An atom containing the store's history.\n\t *\n\t * @public\n\t * @readonly\n\t */\n\treadonly history: Atom<number, RecordsDiff<R>> = atom('history', 0, {\n\t\thistoryLength: 1000,\n\t})\n\n\t/**\n\t * Reactive queries and indexes for efficiently accessing store data.\n\t * Provides methods for filtering, indexing, and subscribing to subsets of records.\n\t *\n\t * @example\n\t * ```ts\n\t * // Create an index by a property\n\t * const booksByAuthor = store.query.index('book', 'author')\n\t *\n\t * // Get records matching criteria\n\t * const inStockBooks = store.query.records('book', () => ({\n\t * inStock: { eq: true }\n\t * }))\n\t * ```\n\t *\n\t * @public\n\t * @readonly\n\t */\n\treadonly query: StoreQueries<R>\n\n\t/**\n\t * A set containing listeners that have been added to this store.\n\t *\n\t * @internal\n\t */\n\tprivate listeners = new Set<{ onHistory: StoreListener<R>; filters: StoreListenerFilters }>()\n\n\t/**\n\t * An array of history entries that have not yet been flushed.\n\t *\n\t * @internal\n\t */\n\tprivate historyAccumulator = new HistoryAccumulator<R>()\n\n\t/**\n\t * A reactor that responds to changes to the history by squashing the accumulated history and\n\t * notifying listeners of the changes.\n\t *\n\t * @internal\n\t */\n\tprivate historyReactor: Reactor\n\n\t/**\n\t * Cancels the history flush scheduled for the next frame, if any.\n\t *\n\t * @internal\n\t */\n\tprivate cancelHistoryReactor: null | (() => void) = null\n\n\t/**\n\t * The schema that defines the structure and validation rules for records in this store.\n\t *\n\t * @public\n\t */\n\treadonly schema: StoreSchema<R, Props>\n\n\t/**\n\t * Custom properties associated with this store instance.\n\t *\n\t * @public\n\t */\n\treadonly props: Props\n\n\t/**\n\t * A mapping of record scopes to the set of record type names that belong to each scope.\n\t * Used to filter records by their persistence and synchronization behavior.\n\t *\n\t * @public\n\t */\n\tpublic readonly scopedTypes: { readonly [K in RecordScope]: ReadonlySet<R['typeName']> }\n\n\t/**\n\t * Side effects manager that handles lifecycle events for record operations.\n\t * Allows registration of callbacks for create, update, delete, and validation events.\n\t *\n\t * @example\n\t * ```ts\n\t * store.sideEffects.registerAfterCreateHandler('book', (book) => {\n\t * console.log('Book created:', book.title)\n\t * })\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic readonly sideEffects = new StoreSideEffects<R>(this)\n\n\t/**\n\t * Creates a new Store instance.\n\t *\n\t * @example\n\t * ```ts\n\t * const store = new Store({\n\t * schema: StoreSchema.create({ book: Book }),\n\t * props: { appName: 'MyLibrary' },\n\t * initialData: savedData\n\t * })\n\t * ```\n\t *\n\t * @param config - Configuration object for the store\n\t */\n\tconstructor(config: {\n\t\t/** Optional unique identifier for the store */\n\t\tid?: string\n\t\t/** The store's initial data to populate on creation */\n\t\tinitialData?: SerializedStore<R>\n\t\t/** The schema defining record types, validation, and migrations */\n\t\tschema: StoreSchema<R, Props>\n\t\t/** Custom properties for the store instance */\n\t\tprops: Props\n\t}) {\n\t\tconst { initialData, schema, id } = config\n\n\t\tthis.id = id ?? uniqueId()\n\t\tthis.schema = schema\n\t\tthis.props = config.props\n\n\t\tif (initialData) {\n\t\t\tthis.records = new AtomMap(\n\t\t\t\t'store',\n\t\t\t\tobjectMapEntries(initialData).map(([id, record]) => [\n\t\t\t\t\tid,\n\t\t\t\t\tdevFreeze(this.schema.validateRecord(this, record, 'initialize', null)),\n\t\t\t\t])\n\t\t\t)\n\t\t} else {\n\t\t\tthis.records = new AtomMap('store')\n\t\t}\n\n\t\tthis.query = new StoreQueries<R>(this.records, this.history)\n\n\t\tthis.historyReactor = reactor(\n\t\t\t'Store.historyReactor',\n\t\t\t() => {\n\t\t\t\t// deref to make sure we're subscribed regardless of whether we need to propagate\n\t\t\t\tthis.history.get()\n\t\t\t\t// If we have accumulated history, flush it and update listeners\n\t\t\t\tthis._flushHistory()\n\t\t\t},\n\t\t\t{ scheduleEffect: (cb) => (this.cancelHistoryReactor = throttleToNextFrame(cb)) }\n\t\t)\n\t\tconst scopedTypes: { [K in RecordScope]: Set<R['typeName']> } = {\n\t\t\tdocument: new Set(),\n\t\t\tsession: new Set(),\n\t\t\tpresence: new Set(),\n\t\t}\n\t\tfor (const type of objectMapValues(this.schema.types)) {\n\t\t\tscopedTypes[type.scope].add(type.typeName)\n\t\t}\n\t\tthis.scopedTypes = scopedTypes\n\t}\n\n\tprivate isFlushingHistory = false\n\n\tpublic _flushHistory() {\n\t\t// If we have accumulated history, flush it and update listeners\n\t\tconst version = this.history.__unsafe__getWithoutCapture()\n\t\tconst entries = this.historyAccumulator.flush(version)\n\t\tif (entries.length > 0) {\n\t\t\tconst errors: unknown[] = []\n\t\t\tconst wasFlushingHistory = this.isFlushingHistory\n\t\t\tthis.isFlushingHistory = true\n\t\t\tfor (const { changes, source } of entries) {\n\t\t\t\t// Filtered diffs are computed at most once per scope per entry, and shared by every\n\t\t\t\t// listener watching that scope.\n\t\t\t\tconst scopedChanges = new Map<RecordScope, RecordsDiff<R> | null>()\n\t\t\t\tfor (const { onHistory, filters } of this.listeners) {\n\t\t\t\t\tif (filters.source !== 'all' && filters.source !== source) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tlet listenerChanges = changes\n\t\t\t\t\tif (filters.scope !== 'all') {\n\t\t\t\t\t\tif (!scopedChanges.has(filters.scope)) {\n\t\t\t\t\t\t\tscopedChanges.set(filters.scope, this.filterChangesByScope(changes, filters.scope))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst filtered = scopedChanges.get(filters.scope)\n\t\t\t\t\t\tif (!filtered) continue\n\t\t\t\t\t\tlistenerChanges = filtered\n\t\t\t\t\t}\n\t\t\t\t\t// The entries are already dequeued, so a listener that throws must not stop the others\n\t\t\t\t\t// (e.g. a sync client) from receiving them: deliver to all, then rethrow the first error.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tonHistory({ changes: listenerChanges, source })\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors.push(error)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.isFlushingHistory = wasFlushingHistory\n\t\t\tif (errors.length > 0) throw errors[0]\n\t\t}\n\t}\n\n\tdispose() {\n\t\t// Deliver what is still pending first: a change-set made in the same frame as the dispose\n\t\t// would otherwise never reach the listeners (e.g. a sync client) still attached to the store.\n\t\ttry {\n\t\t\tthis._flushHistory()\n\t\t} finally {\n\t\t\tthis.cancelHistoryReactor?.()\n\t\t}\n\t}\n\n\t/**\n\t * Filters out non-document changes from a diff. Returns null if there are no changes left.\n\t * @param change - the records diff\n\t * @param scope - the records scope\n\t * @returns\n\t */\n\tfilterChangesByScope(change: RecordsDiff<R>, scope: RecordScope) {\n\t\tconst result = {\n\t\t\tadded: filterEntries(change.added, (_, r) => this.scopedTypes[scope].has(r.typeName)),\n\t\t\tupdated: filterEntries(change.updated, (_, r) => this.scopedTypes[scope].has(r[1].typeName)),\n\t\t\tremoved: filterEntries(change.removed, (_, r) => this.scopedTypes[scope].has(r.typeName)),\n\t\t}\n\t\tif (isRecordsDiffEmpty(result)) {\n\t\t\treturn null\n\t\t}\n\t\treturn result\n\t}\n\n\t/**\n\t * Update the history with a diff of changes.\n\t *\n\t * @param changes - The changes to add to the history.\n\t */\n\tprivate updateHistory(changes: RecordsDiff<R>): void {\n\t\t// Don't capture: a reaction that writes to the store must not become a dependent of the\n\t\t// history atom, or every other store change would re-run it (and it could loop forever).\n\t\tconst version = this.history.__unsafe__getWithoutCapture() + 1\n\t\t// Set the atom before recording the entry: interceptors run inside `add`, and a flush they\n\t\t// trigger (e.g. via `listen`) would otherwise discard the entry as not yet committed.\n\t\tthis.history.set(version, changes)\n\t\tthis.historyAccumulator.add(\n\t\t\t{\n\t\t\t\tchanges,\n\t\t\t\tsource: this.isMergingRemoteChanges ? 'remote' : 'user',\n\t\t\t},\n\t\t\tversion\n\t\t)\n\t\tif (this.listeners.size === 0) {\n\t\t\tthis.historyAccumulator.clear()\n\t\t}\n\t}\n\n\tvalidate(phase: 'initialize' | 'createRecord' | 'updateRecord' | 'tests') {\n\t\tthis.allRecords().forEach((record) => this.schema.validateRecord(this, record, phase, null))\n\t}\n\n\t/**\n\t * Add or update records in the store. If a record with the same ID already exists, it will be updated.\n\t * Otherwise, a new record will be created.\n\t *\n\t * @example\n\t * ```ts\n\t * // Add new records\n\t * const book = Book.create({ title: 'Lathe Of Heaven', author: 'Le Guin' })\n\t * store.put([book])\n\t *\n\t * // Update existing record\n\t * store.put([{ ...book, title: 'The Lathe of Heaven' }])\n\t * ```\n\t *\n\t * @param records - The records to add or update\n\t * @param phaseOverride - Override the validation phase (used internally)\n\t * @public\n\t */\n\tput(records: R[], phaseOverride?: 'initialize'): void {\n\t\tthis.atomic(() => {\n\t\t\tconst updates: Record<IdOf<UnknownRecord>, [from: R, to: R]> = {}\n\t\t\tconst additions: Record<IdOf<UnknownRecord>, R> = {}\n\n\t\t\t// Iterate through all records, creating, updating or removing as needed\n\t\t\tlet record: R\n\n\t\t\tconst source = this.isMergingRemoteChanges ? 'remote' : 'user'\n\n\t\t\tfor (let i = 0, n = records.length; i < n; i++) {\n\t\t\t\trecord = records[i]\n\n\t\t\t\tconst initialValue = this.records.__unsafe__getWithoutCapture(record.id)\n\t\t\t\t// If we already have an atom for this record, update its value.\n\t\t\t\tif (initialValue) {\n\t\t\t\t\t// If we have a beforeUpdate callback, run it against the initial and next records\n\t\t\t\t\trecord = this.sideEffects.handleBeforeChange(initialValue, record, source)\n\n\t\t\t\t\t// Validate the record\n\t\t\t\t\tconst validated = this.schema.validateRecord(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tphaseOverride ?? 'updateRecord',\n\t\t\t\t\t\tinitialValue\n\t\t\t\t\t)\n\n\t\t\t\t\tif (validated === initialValue) continue\n\n\t\t\t\t\trecord = devFreeze(validated)\n\t\t\t\t\tthis.records.set(record.id, record)\n\n\t\t\t\t\tif (additions[record.id]) {\n\t\t\t\t\t\t// the same record was created earlier in this call: fold the update into it\n\t\t\t\t\t\tadditions[record.id] = record\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// an earlier update to the same record in this call owns the `from`\n\t\t\t\t\t\tconst from = updates[record.id]?.[0] ?? initialValue\n\t\t\t\t\t\tif (from === record) {\n\t\t\t\t\t\t\t// back to where it started within this call: nothing to record\n\t\t\t\t\t\t\tdelete updates[record.id]\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tupdates[record.id] = [from, record]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis.addDiffForAfterEvent(initialValue, record)\n\t\t\t\t} else {\n\t\t\t\t\trecord = this.sideEffects.handleBeforeCreate(record, source)\n\n\t\t\t\t\t// If we don't have an atom, create one.\n\n\t\t\t\t\t// Validate the record\n\t\t\t\t\trecord = this.schema.validateRecord(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\trecord as R,\n\t\t\t\t\t\tphaseOverride ?? 'createRecord',\n\t\t\t\t\t\tnull\n\t\t\t\t\t)\n\n\t\t\t\t\t// freeze it\n\t\t\t\t\trecord = devFreeze(record)\n\n\t\t\t\t\t// Mark the change as a new addition.\n\t\t\t\t\tadditions[record.id] = record\n\t\t\t\t\tthis.addDiffForAfterEvent(null, record)\n\n\t\t\t\t\tthis.records.set(record.id, record)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Validation may have left every record identical to before, in which case there is no\n\t\t\t// change-set to record.\n\t\t\tif (!hasAnyKey(additions) && !hasAnyKey(updates)) return\n\t\t\tthis.updateHistory({\n\t\t\t\tadded: additions,\n\t\t\t\tupdated: updates,\n\t\t\t\tremoved: {} as Record<IdOf<R>, R>,\n\t\t\t})\n\t\t})\n\t}\n\n\t/**\n\t * Remove records from the store by their IDs.\n\t *\n\t * @example\n\t * ```ts\n\t * // Remove a single record\n\t * store.remove([book.id])\n\t *\n\t * // Remove multiple records\n\t * store.remove([book1.id, book2.id, book3.id])\n\t * ```\n\t *\n\t * @param ids - The IDs of the records to remove\n\t * @public\n\t */\n\tremove(ids: IdOf<R>[]): void {\n\t\tthis.atomic(() => {\n\t\t\tconst toDelete = new Set<IdOf<R>>(ids)\n\t\t\tconst source = this.isMergingRemoteChanges ? 'remote' : 'user'\n\n\t\t\tif (this.sideEffects.isEnabled()) {\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tconst record = this.records.__unsafe__getWithoutCapture(id)\n\t\t\t\t\tif (!record) continue\n\n\t\t\t\t\tif (this.sideEffects.handleBeforeDelete(record, source) === false) {\n\t\t\t\t\t\ttoDelete.delete(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst actuallyDeleted = this.records.deleteMany(toDelete)\n\t\t\tif (actuallyDeleted.length === 0) return\n\n\t\t\tconst removed = {} as RecordsDiff<R>['removed']\n\t\t\tfor (const [id, record] of actuallyDeleted) {\n\t\t\t\tremoved[id] = record\n\t\t\t\tthis.addDiffForAfterEvent(record, null)\n\t\t\t}\n\n\t\t\t// Update the history with the removed records.\n\t\t\tthis.updateHistory({ added: {}, updated: {}, removed } as RecordsDiff<R>)\n\t\t})\n\t}\n\n\t/**\n\t * Get a record by its ID. This creates a reactive subscription to the record.\n\t *\n\t * @example\n\t * ```ts\n\t * const book = store.get(bookId)\n\t * if (book) {\n\t * console.log(book.title)\n\t * }\n\t * ```\n\t *\n\t * @param id - The ID of the record to get\n\t * @returns The record if it exists, undefined otherwise\n\t * @public\n\t */\n\tget<K extends IdOf<R>>(id: K): RecordFromId<K> | undefined {\n\t\treturn this.records.get(id) as RecordFromId<K> | undefined\n\t}\n\n\t/**\n\t * Get a record by its ID without creating a reactive subscription.\n\t * Use this when you need to access a record but don't want reactive updates.\n\t *\n\t * @example\n\t * ```ts\n\t * // Won't trigger reactive updates when this record changes\n\t * const book = store.unsafeGetWithoutCapture(bookId)\n\t * ```\n\t *\n\t * @param id - The ID of the record to get\n\t * @returns The record if it exists, undefined otherwise\n\t * @public\n\t */\n\tunsafeGetWithoutCapture<K extends IdOf<R>>(id: K): RecordFromId<K> | undefined {\n\t\treturn this.records.__unsafe__getWithoutCapture(id) as RecordFromId<K> | undefined\n\t}\n\n\t/**\n\t * Serialize the store's records to a plain JavaScript object.\n\t * Only includes records matching the specified scope.\n\t *\n\t * @example\n\t * ```ts\n\t * // Serialize only document records (default)\n\t * const documentData = store.serialize('document')\n\t *\n\t * // Serialize all records\n\t * const allData = store.serialize('all')\n\t * ```\n\t *\n\t * @param scope - The scope of records to serialize. Defaults to 'document'\n\t * @returns The serialized store data\n\t * @public\n\t */\n\tserialize(scope: RecordScope | 'all' = 'document'): SerializedStore<R> {\n\t\tconst result = {} as SerializedStore<R>\n\t\tfor (const [id, record] of this.records) {\n\t\t\tif (scope === 'all' || this.scopedTypes[scope].has(record.typeName)) {\n\t\t\t\tresult[id as IdOf<R>] = record\n\t\t\t}\n\t\t}\n\t\treturn result\n\t}\n\n\t/**\n\t * Get a serialized snapshot of the store and its schema.\n\t * This includes both the data and schema information needed for proper migration.\n\t *\n\t * @example\n\t * ```ts\n\t * const snapshot = store.getStoreSnapshot()\n\t * localStorage.setItem('myApp', JSON.stringify(snapshot))\n\t *\n\t * // Later...\n\t * const saved = JSON.parse(localStorage.getItem('myApp'))\n\t * store.loadStoreSnapshot(saved)\n\t * ```\n\t *\n\t * @param scope - The scope of records to serialize. Defaults to 'document'\n\t * @returns A snapshot containing both store data and schema information\n\t * @public\n\t */\n\tgetStoreSnapshot(scope: RecordScope | 'all' = 'document'): StoreSnapshot<R> {\n\t\treturn {\n\t\t\tstore: this.serialize(scope),\n\t\t\tschema: this.schema.serialize(),\n\t\t}\n\t}\n\n\t/**\n\t * Migrate a serialized snapshot to the current schema version.\n\t * This applies any necessary migrations to bring old data up to date.\n\t *\n\t * @example\n\t * ```ts\n\t * const oldSnapshot = JSON.parse(localStorage.getItem('myApp'))\n\t * const migratedSnapshot = store.migrateSnapshot(oldSnapshot)\n\t * ```\n\t *\n\t * @param snapshot - The snapshot to migrate\n\t * @returns The migrated snapshot with current schema version\n\t * @throws Error if migration fails\n\t * @public\n\t */\n\tmigrateSnapshot(snapshot: StoreSnapshot<R>): StoreSnapshot<R> {\n\t\tconst migrationResult = this.schema.migrateStoreSnapshot(snapshot)\n\n\t\tif (migrationResult.type === 'error') {\n\t\t\tthrow new Error(`Failed to migrate snapshot: ${migrationResult.reason}`)\n\t\t}\n\n\t\treturn {\n\t\t\tstore: migrationResult.value,\n\t\t\tschema: this.schema.serialize(),\n\t\t}\n\t}\n\n\t/**\n\t * Load a serialized snapshot into the store, replacing all current data.\n\t * The snapshot will be automatically migrated to the current schema version if needed.\n\t *\n\t * @example\n\t * ```ts\n\t * const snapshot = JSON.parse(localStorage.getItem('myApp'))\n\t * store.loadStoreSnapshot(snapshot)\n\t * ```\n\t *\n\t * @param snapshot - The snapshot to load\n\t * @throws Error if migration fails or snapshot is invalid\n\t * @public\n\t */\n\tloadStoreSnapshot(snapshot: StoreSnapshot<R>): void {\n\t\tconst migrationResult = this.schema.migrateStoreSnapshot(snapshot)\n\n\t\tif (migrationResult.type === 'error') {\n\t\t\tthrow new Error(`Failed to migrate snapshot: ${migrationResult.reason}`)\n\t\t}\n\n\t\tconst prevSideEffectsEnabled = this.sideEffects.isEnabled()\n\t\ttry {\n\t\t\tthis.sideEffects.setIsEnabled(false)\n\t\t\tthis.atomic(() => {\n\t\t\t\tthis.clear()\n\t\t\t\tthis.put(Object.values(migrationResult.value))\n\t\t\t\tthis.ensureStoreIsUsable()\n\t\t\t}, false)\n\t\t} finally {\n\t\t\tthis.sideEffects.setIsEnabled(prevSideEffectsEnabled)\n\t\t}\n\t}\n\n\t/**\n\t * Get an array of all records in the store.\n\t *\n\t * @example\n\t * ```ts\n\t * const allRecords = store.allRecords()\n\t * const books = allRecords.filter(r => r.typeName === 'book')\n\t * ```\n\t *\n\t * @returns An array containing all records in the store\n\t * @public\n\t */\n\tallRecords(): R[] {\n\t\treturn Array.from(this.records.values())\n\t}\n\n\t/**\n\t * Remove all records from the store.\n\t *\n\t * @example\n\t * ```ts\n\t * store.clear()\n\t * console.log(store.allRecords().length) // 0\n\t * ```\n\t *\n\t * @public\n\t */\n\tclear(): void {\n\t\tthis.remove(Array.from(this.records.keys()))\n\t}\n\n\t/**\n\t * Update a single record using an updater function. To update multiple records at once,\n\t * use the `update` method of the `TypedStore` class.\n\t *\n\t * @example\n\t * ```ts\n\t * store.update(book.id, (book) => ({\n\t * ...book,\n\t * title: 'Updated Title'\n\t * }))\n\t * ```\n\t *\n\t * @param id - The ID of the record to update\n\t * @param updater - A function that receives the current record and returns the updated record\n\t * @public\n\t */\n\tupdate<K extends IdOf<R>>(id: K, updater: (record: RecordFromId<K>) => RecordFromId<K>) {\n\t\tconst existing = this.unsafeGetWithoutCapture(id)\n\t\tif (!existing) {\n\t\t\tconsole.error(`Record ${id} not found. This is probably an error`)\n\t\t\treturn\n\t\t}\n\n\t\tthis.put([updater(existing) as any])\n\t}\n\n\t/**\n\t * Check whether a record with the given ID exists in the store.\n\t *\n\t * @example\n\t * ```ts\n\t * if (store.has(bookId)) {\n\t * console.log('Book exists!')\n\t * }\n\t * ```\n\t *\n\t * @param id - The ID of the record to check\n\t * @returns True if the record exists, false otherwise\n\t * @public\n\t */\n\thas<K extends IdOf<R>>(id: K): boolean {\n\t\treturn this.records.has(id)\n\t}\n\n\t/**\n\t * Add a listener that will be called when the store changes.\n\t * Returns a function to remove the listener.\n\t *\n\t * @example\n\t * ```ts\n\t * const removeListener = store.listen((entry) => {\n\t * console.log('Changes:', entry.changes)\n\t * console.log('Source:', entry.source)\n\t * })\n\t *\n\t * // Listen only to user changes to document records\n\t * const removeDocumentListener = store.listen(\n\t * (entry) => console.log('Document changed:', entry),\n\t * { source: 'user', scope: 'document' }\n\t * )\n\t *\n\t * // Later, remove the listener\n\t * removeListener()\n\t * ```\n\t *\n\t * @param onHistory - The listener function to call when changes occur\n\t * @param filters - Optional filters to control when the listener is called\n\t * @returns A function that removes the listener when called\n\t * @public\n\t */\n\tlisten(onHistory: StoreListener<R>, filters?: Partial<StoreListenerFilters>) {\n\t\t// flush history so that this listener's history starts from exactly now\n\t\tthis._flushHistory()\n\n\t\tconst listener = {\n\t\t\tonHistory,\n\t\t\tfilters: {\n\t\t\t\tsource: filters?.source ?? 'all',\n\t\t\t\tscope: filters?.scope ?? 'all',\n\t\t\t},\n\t\t}\n\n\t\tif (!this.historyReactor.scheduler.isActivelyListening) {\n\t\t\tthis.historyReactor.start()\n\t\t\tthis.historyReactor.scheduler.execute()\n\t\t}\n\n\t\tthis.listeners.add(listener)\n\n\t\treturn () => {\n\t\t\t// Flush so this listener's history ends at exactly now, but not from inside a flush:\n\t\t\t// the other listeners would then receive changes made during that flush before the\n\t\t\t// entry they are still being handed.\n\t\t\tif (!this.isFlushingHistory) {\n\t\t\t\ttry {\n\t\t\t\t\tthis._flushHistory()\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Removers run in teardown loops (Editor.dispose, TLSyncClient.close); throwing here\n\t\t\t\t\t// would skip the cleanups that follow.\n\t\t\t\t\tconsole.error(error)\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.listeners.delete(listener)\n\n\t\t\tif (this.listeners.size === 0) {\n\t\t\t\tthis.historyReactor.stop()\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate isMergingRemoteChanges = false\n\n\t/**\n\t * Merge changes from a remote source. Changes made within the provided function\n\t * will be marked with source 'remote' instead of 'user'.\n\t *\n\t * @example\n\t * ```ts\n\t * // Changes from sync/collaboration\n\t * store.mergeRemoteChanges(() => {\n\t * store.put(remoteRecords)\n\t * store.remove(deletedIds)\n\t * })\n\t * ```\n\t *\n\t * @param fn - A function that applies the remote changes\n\t * @public\n\t */\n\tmergeRemoteChanges(fn: () => void) {\n\t\tif (this.isMergingRemoteChanges) {\n\t\t\treturn fn()\n\t\t}\n\n\t\tif (this._isInAtomicOp) {\n\t\t\tthrow new Error('Cannot merge remote changes while in atomic operation')\n\t\t}\n\n\t\ttry {\n\t\t\tthis.atomic(fn, true, true)\n\t\t} finally {\n\t\t\tthis.ensureStoreIsUsable()\n\t\t}\n\t}\n\n\t/**\n\t * Run `fn` and return a {@link RecordsDiff} of the changes that occurred as a result.\n\t */\n\textractingChanges(fn: () => void): RecordsDiff<R> {\n\t\tconst collected: Array<{ version: number; changes: RecordsDiff<R> }> = []\n\t\tconst dispose = this.historyAccumulator.addInterceptor((entry, version) => {\n\t\t\t// a nested transaction that rolled back inside `fn` leaves entries stamped at or past the\n\t\t\t// next committed version \u2014 see HistoryAccumulator\n\t\t\twhile (collected.length && collected[collected.length - 1].version >= version) {\n\t\t\t\tcollected.pop()\n\t\t\t}\n\t\t\tcollected.push({ version, changes: entry.changes })\n\t\t})\n\t\ttry {\n\t\t\ttransact(fn)\n\t\t\tconst current = this.history.__unsafe__getWithoutCapture()\n\t\t\twhile (collected.length && collected[collected.length - 1].version > current) {\n\t\t\t\tcollected.pop()\n\t\t\t}\n\t\t\treturn squashRecordDiffs(collected.map((c) => c.changes))\n\t\t} finally {\n\t\t\tdispose()\n\t\t}\n\t}\n\n\tapplyDiff(\n\t\tdiff: RecordsDiff<R>,\n\t\t{\n\t\t\trunCallbacks = true,\n\t\t\tignoreEphemeralKeys = false,\n\t\t}: { runCallbacks?: boolean; ignoreEphemeralKeys?: boolean } = {}\n\t) {\n\t\tthis.atomic(() => {\n\t\t\tconst toPut = objectMapValues(diff.added)\n\n\t\t\tfor (const [_from, to] of objectMapValues(diff.updated)) {\n\t\t\t\tconst type = this.schema.getType(to.typeName)\n\t\t\t\tif (ignoreEphemeralKeys && type.ephemeralKeySet.size) {\n\t\t\t\t\tconst existing = this.unsafeGetWithoutCapture(to.id)\n\t\t\t\t\tif (!existing) {\n\t\t\t\t\t\ttoPut.push(to)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tlet changed: R | null = null\n\t\t\t\t\tfor (const [key, value] of Object.entries(to)) {\n\t\t\t\t\t\tif (type.ephemeralKeySet.has(key) || Object.is(value, getOwnProperty(existing, key))) {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!changed) changed = { ...existing } as R\n\t\t\t\t\t\t;(changed as any)[key] = value\n\t\t\t\t\t}\n\t\t\t\t\t// a key the update removed (present before, absent in `to`) is a change too\n\t\t\t\t\tfor (const key of Object.keys(existing)) {\n\t\t\t\t\t\tif (type.ephemeralKeySet.has(key) || Object.hasOwn(to, key)) continue\n\t\t\t\t\t\tif (!changed) changed = { ...existing } as R\n\t\t\t\t\t\tdelete (changed as any)[key]\n\t\t\t\t\t}\n\t\t\t\t\tif (changed) toPut.push(changed)\n\t\t\t\t} else {\n\t\t\t\t\ttoPut.push(to)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst toRemove = objectMapKeys(diff.removed)\n\t\t\tif (toPut.length) {\n\t\t\t\tthis.put(toPut)\n\t\t\t}\n\t\t\tif (toRemove.length) {\n\t\t\t\tthis.remove(toRemove)\n\t\t\t}\n\t\t}, runCallbacks)\n\t}\n\n\t/**\n\t * Create a cache based on values in the store. Pass in a function that takes and ID and a\n\t * signal for the underlying record. Return a signal (usually a computed) for the cached value.\n\t * For simple derivations, use {@link Store.createComputedCache}. This function is useful if you\n\t * need more precise control over intermediate values.\n\t *\n\t * After the record is deleted the record signal holds `UNINITIALIZED` rather than a record, and\n\t * a signal that a reader still depends on can be re-derived once more in that state. Guard with\n\t * `isUninitialized` and return a value no live record can produce: returning the same value the\n\t * record last had (e.g. `undefined`) leaves readers stale if a record with that id comes back.\n\t * {@link Store.createComputedCache} handles this for you.\n\t */\n\tcreateCache<Result, Record extends R = R>(\n\t\tcreate: (id: IdOf<Record>, recordSignal: Signal<R>) => Signal<Result>\n\t) {\n\t\tconst cache = new WeakCache<Atom<any>, Signal<Result>>()\n\t\treturn {\n\t\t\tget: (id: IdOf<Record>) => {\n\t\t\t\tconst atom = this.records.getAtom(id)\n\t\t\t\tif (!atom) return undefined\n\t\t\t\treturn cache.get(atom, () => create(id, atom as Signal<R>)).get()\n\t\t\t},\n\t\t}\n\t}\n\n\t/**\n\t * Create a computed cache.\n\t *\n\t * @param name - The name of the derivation cache.\n\t * @param derive - A function used to derive the value of the cache.\n\t * @param opts - Options for the computed cache.\n\t * @public\n\t */\n\tcreateComputedCache<Result, Record extends R = R>(\n\t\tname: string,\n\t\tderive: (record: Record) => Result | undefined,\n\t\topts?: CreateComputedCacheOpts<Result, Record>\n\t): ComputedCache<Result, Record> {\n\t\tconst areRecordsEqual = opts?.areRecordsEqual\n\t\tconst areResultsEqual = opts?.areResultsEqual\n\t\treturn this.createCache((id, record) => {\n\t\t\tconst recordSignal = areRecordsEqual\n\t\t\t\t? computed(`${name}:${id}:isEqual`, () => record.get(), {\n\t\t\t\t\t\t// a deleted record's atom holds UNINITIALIZED, which user code must not see\n\t\t\t\t\t\tisEqual: (a, b) =>\n\t\t\t\t\t\t\tisUninitialized(a) || isUninitialized(b) ? a === b : areRecordsEqual(a, b),\n\t\t\t\t\t})\n\t\t\t\t: record\n\n\t\t\treturn computed<Result | undefined>(\n\t\t\t\tname + ':' + id,\n\t\t\t\t() => {\n\t\t\t\t\t// The computed can be re-derived after its record is deleted (e.g. by a reaction\n\t\t\t\t\t// checking whether its parents changed), when the atom holds UNINITIALIZED. Return\n\t\t\t\t\t// DELETED_RECORD rather than undefined: it never equals a real result, so a reader\n\t\t\t\t\t// re-runs, looks the id up again, and so sees the record if it comes back.\n\t\t\t\t\tconst value = recordSignal.get()\n\t\t\t\t\tif (isUninitialized(value)) return DELETED_RECORD as any\n\t\t\t\t\treturn derive(value as Record)\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tisEqual: areResultsEqual\n\t\t\t\t\t\t? (a, b) =>\n\t\t\t\t\t\t\t\ta === DELETED_RECORD || b === DELETED_RECORD ? a === b : areResultsEqual(a, b)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t}\n\t\t\t)\n\t\t})\n\t}\n\n\tprivate _integrityChecker?: () => void | undefined\n\n\t/** @internal */\n\tensureStoreIsUsable() {\n\t\tthis.atomic(() => {\n\t\t\tthis._integrityChecker ??= this.schema.createIntegrityChecker(this)\n\t\t\tthis._integrityChecker?.()\n\t\t})\n\t}\n\n\tprivate _isPossiblyCorrupted = false\n\t/** @internal */\n\tmarkAsPossiblyCorrupted() {\n\t\tthis._isPossiblyCorrupted = true\n\t}\n\t/** @internal */\n\tisPossiblyCorrupted() {\n\t\treturn this._isPossiblyCorrupted\n\t}\n\n\tprivate pendingAfterEvents: Map<IdOf<R>, { before: R | null; after: R | null }> | null = null\n\tprivate addDiffForAfterEvent(before: R | null, after: R | null) {\n\t\tassert(this.pendingAfterEvents, 'must be in event operation')\n\t\tif (before === after) return\n\t\tif (before && after) assert(before.id === after.id)\n\t\tif (!before && !after) return\n\t\tconst id = (before || after)!.id\n\t\tconst existing = this.pendingAfterEvents.get(id)\n\t\tif (existing) {\n\t\t\texisting.after = after\n\t\t} else {\n\t\t\tthis.pendingAfterEvents.set(id, { before, after })\n\t\t}\n\t}\n\tprivate flushAtomicCallbacks(isMergingRemoteChanges: boolean) {\n\t\tlet updateDepth = 0\n\t\tlet source: ChangeSource = isMergingRemoteChanges ? 'remote' : 'user'\n\t\twhile (this.pendingAfterEvents) {\n\t\t\tconst events = this.pendingAfterEvents\n\t\t\tthis.pendingAfterEvents = null\n\n\t\t\tif (!this.sideEffects.isEnabled()) continue\n\n\t\t\tupdateDepth++\n\t\t\tif (updateDepth > 100) {\n\t\t\t\tthrow new Error('Maximum store update depth exceeded, bailing out')\n\t\t\t}\n\n\t\t\tfor (const { before, after } of events.values()) {\n\t\t\t\tif (before && after && before !== after && !isEqual(before, after)) {\n\t\t\t\t\tthis.sideEffects.handleAfterChange(before, after, source)\n\t\t\t\t} else if (before && !after) {\n\t\t\t\t\tthis.sideEffects.handleAfterDelete(before, source)\n\t\t\t\t} else if (!before && after) {\n\t\t\t\t\tthis.sideEffects.handleAfterCreate(after, source)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!this.pendingAfterEvents) {\n\t\t\t\tthis.sideEffects.handleOperationComplete(source)\n\t\t\t}\n\t\t\t// Whatever the after-handlers or the operation-complete handlers changed in response to\n\t\t\t// a remote operation is not itself remote: later rounds are attributed to 'user'.\n\t\t\tsource = 'user'\n\t\t}\n\t}\n\tprivate _isInAtomicOp = false\n\t/** @internal */\n\tatomic<T>(fn: () => T, runCallbacks = true, isMergingRemoteChanges = false): T {\n\t\treturn transact(() => {\n\t\t\tif (this._isInAtomicOp) {\n\t\t\t\tif (!this.pendingAfterEvents) this.pendingAfterEvents = new Map()\n\t\t\t\tconst prevSideEffectsEnabled = this.sideEffects.isEnabled()\n\t\t\t\tassert(!isMergingRemoteChanges, 'cannot call mergeRemoteChanges while in atomic operation')\n\t\t\t\ttry {\n\t\t\t\t\t// if we are in an atomic context with side effects ON allow switching before* callbacks OFF.\n\t\t\t\t\t// but don't allow switching them ON if they had been marked OFF before.\n\t\t\t\t\tif (prevSideEffectsEnabled && !runCallbacks) {\n\t\t\t\t\t\tthis.sideEffects.setIsEnabled(false)\n\t\t\t\t\t}\n\t\t\t\t\treturn fn()\n\t\t\t\t} finally {\n\t\t\t\t\tthis.sideEffects.setIsEnabled(prevSideEffectsEnabled)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.pendingAfterEvents = new Map()\n\t\t\tconst prevSideEffectsEnabled = this.sideEffects.isEnabled()\n\t\t\t// an operation may switch side effects off, but never on while they are disabled\n\t\t\tthis.sideEffects.setIsEnabled(runCallbacks && prevSideEffectsEnabled)\n\t\t\tthis._isInAtomicOp = true\n\n\t\t\tif (isMergingRemoteChanges) {\n\t\t\t\tthis.isMergingRemoteChanges = true\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst result = fn()\n\t\t\t\tthis.isMergingRemoteChanges = false\n\n\t\t\t\tthis.flushAtomicCallbacks(isMergingRemoteChanges)\n\n\t\t\t\treturn result\n\t\t\t} finally {\n\t\t\t\tthis.pendingAfterEvents = null\n\t\t\t\tthis.sideEffects.setIsEnabled(prevSideEffectsEnabled)\n\t\t\t\tthis._isInAtomicOp = false\n\t\t\t\tthis.isMergingRemoteChanges = false\n\t\t\t}\n\t\t})\n\t}\n\n\t/** @internal */\n\taddHistoryInterceptor(fn: (entry: HistoryEntry<R>, source: ChangeSource) => void) {\n\t\treturn this.historyAccumulator.addInterceptor((entry) =>\n\t\t\tfn(entry, this.isMergingRemoteChanges ? 'remote' : 'user')\n\t\t)\n\t}\n}\n\n/**\n * Collect and squash history entries by their adjacent sources.\n * Adjacent entries from the same source are combined into a single entry.\n *\n * For example: [user, user, remote, remote, user] becomes [user, remote, user]\n *\n * @example\n * ```ts\n * const entries = [\n * { source: 'user', changes: userChanges1 },\n * { source: 'user', changes: userChanges2 },\n * { source: 'remote', changes: remoteChanges }\n * ]\n *\n * const squashed = squashHistoryEntries(entries)\n * // Results in 2 entries: combined user changes + remote changes\n * ```\n *\n * @param entries - The array of history entries to squash\n * @returns An array of squashed history entries\n * @public\n */\nfunction squashHistoryEntries<T extends UnknownRecord>(\n\tentries: HistoryEntry<T>[]\n): HistoryEntry<T>[] {\n\tif (entries.length === 0) return []\n\n\tconst chunked: HistoryEntry<T>[][] = []\n\tlet chunk: HistoryEntry<T>[] = [entries[0]]\n\tlet entry: HistoryEntry<T>\n\n\tfor (let i = 1, n = entries.length; i < n; i++) {\n\t\tentry = entries[i]\n\t\tif (chunk[0].source !== entry.source) {\n\t\t\tchunked.push(chunk)\n\t\t\tchunk = []\n\t\t}\n\t\tchunk.push(entry)\n\t}\n\t// Push the last chunk\n\tchunked.push(chunk)\n\n\treturn devFreeze(\n\t\tchunked.map((chunk) => ({\n\t\t\tsource: chunk[0].source,\n\t\t\t// a single-entry chunk needs no squashing \u2014 skip the O(N) copy of its diff\n\t\t\tchanges:\n\t\t\t\tchunk.length === 1 ? chunk[0].changes : squashRecordDiffs(chunk.map((e) => e.changes)),\n\t\t}))\n\t)\n}\n\n/**\n * Internal class that accumulates history entries before they are flushed to listeners.\n * Handles batching and squashing of adjacent entries from the same source.\n *\n * @internal\n */\nclass HistoryAccumulator<T extends UnknownRecord> {\n\tprivate _history: HistoryEntry<T>[] = []\n\n\t// The `history` atom value each entry was recorded at. A transaction that aborts rolls that atom\n\t// back but cannot unwind this array, so an entry stamped at or past the atom's current value\n\t// belongs to a rolled-back change-set and must not reach listeners.\n\tprivate _versions: number[] = []\n\n\tprivate _interceptors: Set<(entry: HistoryEntry<T>, version: number) => void> = new Set()\n\n\t/**\n\t * Add an interceptor that will be called for each history entry, with the version it was\n\t * recorded at (see `add`). Returns a function to remove the interceptor.\n\t */\n\taddInterceptor(fn: (entry: HistoryEntry<T>, version: number) => void) {\n\t\tthis._interceptors.add(fn)\n\t\treturn () => {\n\t\t\tthis._interceptors.delete(fn)\n\t\t}\n\t}\n\n\t/**\n\t * Add a history entry recorded at `version` (the `history` atom value it sets).\n\t * Calls all registered interceptors with the entry.\n\t */\n\tadd(entry: HistoryEntry<T>, version: number) {\n\t\tthis.discardFrom(version)\n\t\tthis._history.push(entry)\n\t\tthis._versions.push(version)\n\t\tfor (const interceptor of this._interceptors) {\n\t\t\tinterceptor(entry, version)\n\t\t}\n\t}\n\n\t/**\n\t * Flush all accumulated history entries, squashing adjacent entries from the same source.\n\t * Clears the internal history buffer.\n\t */\n\tflush(currentVersion: number) {\n\t\tthis.discardFrom(currentVersion + 1)\n\t\tconst history = squashHistoryEntries(this._history)\n\t\tthis.clear()\n\t\treturn history\n\t}\n\n\t/**\n\t * Clear all accumulated history entries without flushing.\n\t */\n\tclear() {\n\t\tthis._history = []\n\t\tthis._versions = []\n\t}\n\n\t// Drops the entries recorded at or after `version`. Entries are in ascending version order: the\n\t// atom increments by one per entry, and a rollback is followed by exactly this discard.\n\tprivate discardFrom(version: number) {\n\t\twhile (this._versions.length && this._versions[this._versions.length - 1] >= version) {\n\t\t\tthis._versions.pop()\n\t\t\tthis._history.pop()\n\t\t}\n\t}\n}\n\n/**\n * A store or an object containing a store.\n * This type is used for APIs that can accept either a store directly or an object with a store property.\n *\n * @example\n * ```ts\n * function useStore(storeOrObject: StoreObject<MyRecord>) {\n * const store = storeOrObject instanceof Store ? storeOrObject : storeOrObject.store\n * return store\n * }\n * ```\n *\n * @public\n */\nexport type StoreObject<R extends UnknownRecord> = Store<R> | { store: Store<R> }\n/**\n * Extract the record type from a StoreObject.\n *\n * @example\n * ```ts\n * type MyStoreObject = { store: Store<Book | Author> }\n * type Records = StoreObjectRecordType<MyStoreObject> // Book | Author\n * ```\n *\n * @public\n */\nexport type StoreObjectRecordType<Context extends StoreObject<any>> =\n\tContext extends Store<infer R> ? R : Context extends { store: Store<infer R> } ? R : never\n\n/**\n * Create a computed cache that works with any StoreObject (store or object containing a store).\n * This is a standalone version of Store.createComputedCache that can work with multiple store instances.\n *\n * @example\n * ```ts\n * const expensiveCache = createComputedCache(\n * 'expensiveData',\n * (context: { store: Store<Book> }, book: Book) => {\n * return performExpensiveCalculation(book)\n * }\n * )\n *\n * // Use with different store instances\n * const result1 = expensiveCache.get(storeObject1, bookId)\n * const result2 = expensiveCache.get(storeObject2, bookId)\n * ```\n *\n * @param name - A unique name for the cache (used for debugging)\n * @param derive - Function that derives a value from the context and record\n * @param opts - Optional configuration for equality checks\n * @returns A cache that can be used with multiple store instances\n * @public\n */\nexport function createComputedCache<\n\tContext extends StoreObject<any>,\n\tResult,\n\tRecord extends StoreObjectRecordType<Context> = StoreObjectRecordType<Context>,\n>(\n\tname: string,\n\tderive: (context: Context, record: Record) => Result | undefined,\n\topts?: CreateComputedCacheOpts<Result, Record>\n) {\n\tconst cache = new WeakCache<Context, ComputedCache<Result, Record>>()\n\treturn {\n\t\tget(context: Context, id: IdOf<Record>) {\n\t\t\tconst computedCache = cache.get(context, () => {\n\t\t\t\tconst store = (context instanceof Store ? context : context.store) as Store<Record>\n\t\t\t\treturn store.createComputedCache(name, (record) => derive(context, record), opts)\n\t\t\t})\n\t\t\treturn computedCache.get(id)\n\t\t},\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["id", "atom", "prevSideEffectsEnabled", "chunk"]
7
7
  }
@@ -1809,6 +1809,12 @@ export declare class Store<R extends UnknownRecord = UnknownRecord, Props = unkn
1809
1809
  * signal for the underlying record. Return a signal (usually a computed) for the cached value.
1810
1810
  * For simple derivations, use {@link Store.createComputedCache}. This function is useful if you
1811
1811
  * need more precise control over intermediate values.
1812
+ *
1813
+ * After the record is deleted the record signal holds `UNINITIALIZED` rather than a record, and
1814
+ * a signal that a reader still depends on can be re-derived once more in that state. Guard with
1815
+ * `isUninitialized` and return a value no live record can produce: returning the same value the
1816
+ * record last had (e.g. `undefined`) leaves readers stale if a record with that id comes back.
1817
+ * {@link Store.createComputedCache} handles this for you.
1812
1818
  */
1813
1819
  createCache<Result, Record extends R = R>(create: (id: IdOf<Record>, recordSignal: Signal<R>) => Signal<Result>): {
1814
1820
  get: (id: IdOf<Record>) => Result | undefined;
@@ -29,7 +29,7 @@ import {
29
29
  } from "./lib/StoreSideEffects.mjs";
30
30
  registerTldrawLibraryVersion(
31
31
  "@tldraw/store",
32
- "5.6.0-next.0b1aaf8f5861",
32
+ "5.6.0-next.568da2160ccd",
33
33
  "esm"
34
34
  );
35
35
  export {