@tldraw/store 5.3.0 → 5.4.0-next.ef99ab47e5ce

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist-cjs/index.d.ts +1 -0
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/RecordType.js +1 -1
  4. package/dist-cjs/lib/RecordType.js.map +2 -2
  5. package/dist-cjs/lib/RecordsDiff.js +45 -9
  6. package/dist-cjs/lib/RecordsDiff.js.map +3 -3
  7. package/dist-cjs/lib/Store.js +18 -29
  8. package/dist-cjs/lib/Store.js.map +2 -2
  9. package/dist-cjs/lib/StoreQueries.js +7 -54
  10. package/dist-cjs/lib/StoreQueries.js.map +2 -2
  11. package/dist-cjs/lib/StoreSchema.js +8 -12
  12. package/dist-cjs/lib/StoreSchema.js.map +3 -3
  13. package/dist-cjs/lib/executeQuery.js +5 -2
  14. package/dist-cjs/lib/executeQuery.js.map +2 -2
  15. package/dist-cjs/lib/migrate.js +5 -7
  16. package/dist-cjs/lib/migrate.js.map +2 -2
  17. package/dist-esm/index.d.mts +1 -0
  18. package/dist-esm/index.mjs +1 -1
  19. package/dist-esm/lib/RecordType.mjs +1 -1
  20. package/dist-esm/lib/RecordType.mjs.map +2 -2
  21. package/dist-esm/lib/RecordsDiff.mjs +45 -9
  22. package/dist-esm/lib/RecordsDiff.mjs.map +3 -3
  23. package/dist-esm/lib/Store.mjs +19 -30
  24. package/dist-esm/lib/Store.mjs.map +2 -2
  25. package/dist-esm/lib/StoreQueries.mjs +7 -54
  26. package/dist-esm/lib/StoreQueries.mjs.map +2 -2
  27. package/dist-esm/lib/StoreSchema.mjs +8 -12
  28. package/dist-esm/lib/StoreSchema.mjs.map +3 -3
  29. package/dist-esm/lib/executeQuery.mjs +5 -2
  30. package/dist-esm/lib/executeQuery.mjs.map +2 -2
  31. package/dist-esm/lib/migrate.mjs +5 -7
  32. package/dist-esm/lib/migrate.mjs.map +2 -2
  33. package/package.json +3 -3
  34. package/src/lib/RecordType.ts +1 -1
  35. package/src/lib/RecordsDiff.ts +66 -10
  36. package/src/lib/Store.ts +21 -36
  37. package/src/lib/StoreQueries.test.ts +24 -0
  38. package/src/lib/StoreQueries.ts +7 -60
  39. package/src/lib/StoreSchema.ts +11 -17
  40. package/src/lib/executeQuery.ts +5 -2
  41. package/src/lib/migrate.ts +8 -12
@@ -8,6 +8,7 @@ import {
8
8
  import { areArraysShallowEqual, isEqual, objectMapValues } from "@tldraw/utils";
9
9
  import { executeQuery, objectMatchesQuery } from "./executeQuery.mjs";
10
10
  import { IncrementalSetConstructor } from "./IncrementalSetConstructor.mjs";
11
+ import { hasAnyKey, squashRecordDiffsMutableByType } from "./RecordsDiff.mjs";
11
12
  import { diffSets } from "./setUtils.mjs";
12
13
  class StoreQueries {
13
14
  /**
@@ -105,56 +106,8 @@ class StoreQueries {
105
106
  const diff = this.history.getDiffSince(lastComputedEpoch);
106
107
  if (diff === RESET_VALUE) return this.history.get();
107
108
  const res = { added: {}, removed: {}, updated: {} };
108
- let numAdded = 0;
109
- let numRemoved = 0;
110
- let numUpdated = 0;
111
- for (const changes of diff) {
112
- for (const added of objectMapValues(changes.added)) {
113
- if (added.typeName === typeName) {
114
- if (res.removed[added.id]) {
115
- const original = res.removed[added.id];
116
- delete res.removed[added.id];
117
- numRemoved--;
118
- if (original !== added) {
119
- res.updated[added.id] = [original, added];
120
- numUpdated++;
121
- }
122
- } else {
123
- res.added[added.id] = added;
124
- numAdded++;
125
- }
126
- }
127
- }
128
- for (const [from, to] of objectMapValues(changes.updated)) {
129
- if (to.typeName === typeName) {
130
- if (res.added[to.id]) {
131
- res.added[to.id] = to;
132
- } else if (res.updated[to.id]) {
133
- res.updated[to.id] = [res.updated[to.id][0], to];
134
- } else {
135
- res.updated[to.id] = [from, to];
136
- numUpdated++;
137
- }
138
- }
139
- }
140
- for (const removed of objectMapValues(changes.removed)) {
141
- if (removed.typeName === typeName) {
142
- if (res.added[removed.id]) {
143
- delete res.added[removed.id];
144
- numAdded--;
145
- } else if (res.updated[removed.id]) {
146
- res.removed[removed.id] = res.updated[removed.id][0];
147
- delete res.updated[removed.id];
148
- numUpdated--;
149
- numRemoved++;
150
- } else {
151
- res.removed[removed.id] = removed;
152
- numRemoved++;
153
- }
154
- }
155
- }
156
- }
157
- if (numAdded || numRemoved || numUpdated) {
109
+ const size = squashRecordDiffsMutableByType(res, diff, typeName);
110
+ if (size) {
158
111
  return withDiff(this.history.get(), res);
159
112
  } else {
160
113
  return lastValue;
@@ -333,7 +286,7 @@ class StoreQueries {
333
286
  *
334
287
  * @public
335
288
  */
336
- record(typeName, queryCreator = () => ({}), name = "record:" + typeName + (queryCreator ? ":" + queryCreator.toString() : "")) {
289
+ record(typeName, queryCreator = () => ({}), name = "record:" + typeName + ":" + queryCreator.toString()) {
337
290
  const ids = this.ids(typeName, queryCreator, name);
338
291
  return computed(name, () => {
339
292
  for (const id of ids.get()) {
@@ -366,7 +319,7 @@ class StoreQueries {
366
319
  *
367
320
  * @public
368
321
  */
369
- records(typeName, queryCreator = () => ({}), name = "records:" + typeName + (queryCreator ? ":" + queryCreator.toString() : "")) {
322
+ records(typeName, queryCreator = () => ({}), name = "records:" + typeName + ":" + queryCreator.toString()) {
370
323
  const ids = this.ids(typeName, queryCreator, "ids:" + name);
371
324
  return computed(
372
325
  name,
@@ -403,12 +356,12 @@ class StoreQueries {
403
356
  *
404
357
  * @public
405
358
  */
406
- ids(typeName, queryCreator = () => ({}), name = "ids:" + typeName + (queryCreator ? ":" + queryCreator.toString() : "")) {
359
+ ids(typeName, queryCreator = () => ({}), name = "ids:" + typeName + ":" + queryCreator.toString()) {
407
360
  const typeHistory = this.filterHistory(typeName);
408
361
  const fromScratch = () => {
409
362
  typeHistory.get();
410
363
  const query = queryCreator();
411
- if (Object.keys(query).length === 0) {
364
+ if (!hasAnyKey(query)) {
412
365
  return this.getAllIdsForType(typeName);
413
366
  }
414
367
  return executeQuery(this, typeName, query);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/StoreQueries.ts"],
4
- "sourcesContent": ["import {\n\tAtom,\n\tcomputed,\n\tComputed,\n\tEMPTY_ARRAY,\n\tisUninitialized,\n\tRESET_VALUE,\n\twithDiff,\n} from '@tldraw/state'\nimport { areArraysShallowEqual, isEqual, objectMapValues } from '@tldraw/utils'\nimport { AtomMap } from './AtomMap'\nimport { IdOf, UnknownRecord } from './BaseRecord'\nimport { executeQuery, objectMatchesQuery, QueryExpression } from './executeQuery'\nimport { IncrementalSetConstructor } from './IncrementalSetConstructor'\nimport { RecordsDiff } from './RecordsDiff'\nimport { diffSets } from './setUtils'\nimport { CollectionDiff } from './Store'\n\n/**\n * A type representing the diff of changes to a reactive store index.\n * Maps property values to the collection differences for record IDs that have that property value.\n *\n * @example\n * ```ts\n * // For an index on book titles, the diff might look like:\n * const titleIndexDiff: RSIndexDiff<Book, 'title'> = new Map([\n * ['The Lathe of Heaven', { added: new Set(['book:1']), removed: new Set() }],\n * ['Animal Farm', { added: new Set(), removed: new Set(['book:2']) }]\n * ])\n * ```\n *\n * @public\n */\nexport type RSIndexDiff<R extends UnknownRecord> = Map<any, CollectionDiff<IdOf<R>>>\n\n/**\n * A type representing a reactive store index as a map from property values to sets of record IDs.\n * This is used to efficiently look up records by a specific property value.\n *\n * @example\n * ```ts\n * // Index mapping book titles to the IDs of books with that title\n * const titleIndex: RSIndexMap<Book, 'title'> = new Map([\n * ['The Lathe of Heaven', new Set(['book:1'])],\n * ['Animal Farm', new Set(['book:2', 'book:3'])]\n * ])\n * ```\n *\n * @public\n */\nexport type RSIndexMap<R extends UnknownRecord> = Map<any, Set<IdOf<R>>>\n\n/**\n * A reactive computed index that provides efficient lookups of records by property values.\n * Returns a computed value containing an RSIndexMap with diffs for change tracking.\n *\n * @example\n * ```ts\n * // Create an index on book authors\n * const authorIndex: RSIndex<Book, 'authorId'> = store.query.index('book', 'authorId')\n *\n * // Get all books by a specific author\n * const leguinBooks = authorIndex.get().get('author:leguin')\n * ```\n *\n * @public\n */\nexport type RSIndex<R extends UnknownRecord> = Computed<RSIndexMap<R>, RSIndexDiff<R>>\n\n/**\n * A class that provides reactive querying capabilities for a record store.\n * Offers methods to create indexes, filter records, and perform efficient lookups with automatic cache management.\n * All queries are reactive and will automatically update when the underlying store data changes.\n *\n * @example\n * ```ts\n * // Create a store with books\n * const store = new Store({ schema: StoreSchema.create({ book: Book, author: Author }) })\n *\n * // Get reactive queries for books\n * const booksByAuthor = store.query.index('book', 'authorId')\n * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))\n * ```\n *\n * @public\n */\nexport class StoreQueries<R extends UnknownRecord> {\n\t/**\n\t * Creates a new StoreQueries instance.\n\t *\n\t * recordMap - The atom map containing all records in the store\n\t * history - The atom tracking the store's change history with diffs\n\t *\n\t * @internal\n\t */\n\tconstructor(\n\t\tprivate readonly recordMap: AtomMap<IdOf<R>, R>,\n\t\tprivate readonly history: Atom<number, RecordsDiff<R>>\n\t) {}\n\n\t/**\n\t * A cache of derivations (indexes).\n\t *\n\t * @internal\n\t */\n\tprivate indexCache = new Map<string, RSIndex<R>>()\n\n\t/**\n\t * A cache of derivations (filtered histories).\n\t *\n\t * @internal\n\t */\n\tprivate historyCache = new Map<string, Computed<number, RecordsDiff<R>>>()\n\n\t/**\n\t * @internal\n\t */\n\tpublic getAllIdsForType<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName\n\t): Set<IdOf<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = new Set<IdOf<S>>()\n\t\tfor (const record of this.recordMap.values()) {\n\t\t\tif (record.typeName === typeName) {\n\t\t\t\tids.add(record.id as IdOf<S>)\n\t\t\t}\n\t\t}\n\t\treturn ids\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tpublic getRecordById<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tid: IdOf<Extract<R, { typeName: TypeName }>>\n\t): Extract<R, { typeName: TypeName }> | undefined {\n\t\tconst record = this.recordMap.get(id as IdOf<R>)\n\t\tif (record && record.typeName === typeName) {\n\t\t\treturn record as Extract<R, { typeName: TypeName }>\n\t\t}\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Helper to extract nested property value using pre-split path parts.\n\t * @internal\n\t */\n\tprivate getNestedValue(obj: any, pathParts: string[]): any {\n\t\tlet current = obj\n\t\tfor (const part of pathParts) {\n\t\t\tif (current == null || typeof current !== 'object') return undefined\n\t\t\tcurrent = current[part]\n\t\t}\n\t\treturn current\n\t}\n\n\t/**\n\t * Creates a reactive computed that tracks the change history for records of a specific type.\n\t * The returned computed provides incremental diffs showing what records of the given type\n\t * have been added, updated, or removed.\n\t *\n\t * @param typeName - The type name to filter the history by\n\t * @returns A computed value containing the current epoch and diffs of changes for the specified type\n\t *\n\t * @example\n\t * ```ts\n\t * // Track changes to book records only\n\t * const bookHistory = store.query.filterHistory('book')\n\t *\n\t * // React to book changes\n\t * react('book-changes', () => {\n\t * const currentEpoch = bookHistory.get()\n\t * console.log('Books updated at epoch:', currentEpoch)\n\t * })\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic filterHistory<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName\n\t): Computed<number, RecordsDiff<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tif (this.historyCache.has(typeName)) {\n\t\t\treturn this.historyCache.get(typeName) as any\n\t\t}\n\n\t\tconst filtered = computed<number, RecordsDiff<S>>(\n\t\t\t'filterHistory:' + typeName,\n\t\t\t(lastValue, lastComputedEpoch) => {\n\t\t\t\tif (isUninitialized(lastValue)) {\n\t\t\t\t\treturn this.history.get()\n\t\t\t\t}\n\n\t\t\t\tconst diff = this.history.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (diff === RESET_VALUE) return this.history.get()\n\n\t\t\t\tconst res = { added: {}, removed: {}, updated: {} } as RecordsDiff<S>\n\t\t\t\tlet numAdded = 0\n\t\t\t\tlet numRemoved = 0\n\t\t\t\tlet numUpdated = 0\n\n\t\t\t\tfor (const changes of diff) {\n\t\t\t\t\tfor (const added of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (added.typeName === typeName) {\n\t\t\t\t\t\t\tif (res.removed[added.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\tconst original = res.removed[added.id as IdOf<S>]\n\t\t\t\t\t\t\t\tdelete res.removed[added.id as IdOf<S>]\n\t\t\t\t\t\t\t\tnumRemoved--\n\t\t\t\t\t\t\t\tif (original !== added) {\n\t\t\t\t\t\t\t\t\tres.updated[added.id as IdOf<S>] = [original, added as S]\n\t\t\t\t\t\t\t\t\tnumUpdated++\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tres.added[added.id as IdOf<S>] = added as S\n\t\t\t\t\t\t\t\tnumAdded++\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const [from, to] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (to.typeName === typeName) {\n\t\t\t\t\t\t\tif (res.added[to.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\tres.added[to.id as IdOf<S>] = to as S\n\t\t\t\t\t\t\t} else if (res.updated[to.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\tres.updated[to.id as IdOf<S>] = [res.updated[to.id as IdOf<S>][0], to as S]\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tres.updated[to.id as IdOf<S>] = [from as S, to as S]\n\t\t\t\t\t\t\t\tnumUpdated++\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const removed of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (removed.typeName === typeName) {\n\t\t\t\t\t\t\tif (res.added[removed.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\t// was added during this diff sequence, so just undo the add\n\t\t\t\t\t\t\t\tdelete res.added[removed.id as IdOf<S>]\n\t\t\t\t\t\t\t\tnumAdded--\n\t\t\t\t\t\t\t} else if (res.updated[removed.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\t// remove oldest version\n\t\t\t\t\t\t\t\tres.removed[removed.id as IdOf<S>] = res.updated[removed.id as IdOf<S>][0]\n\t\t\t\t\t\t\t\tdelete res.updated[removed.id as IdOf<S>]\n\t\t\t\t\t\t\t\tnumUpdated--\n\t\t\t\t\t\t\t\tnumRemoved++\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tres.removed[removed.id as IdOf<S>] = removed as S\n\t\t\t\t\t\t\t\tnumRemoved++\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (numAdded || numRemoved || numUpdated) {\n\t\t\t\t\treturn withDiff(this.history.get(), res)\n\t\t\t\t} else {\n\t\t\t\t\treturn lastValue\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ historyLength: 100 }\n\t\t)\n\n\t\tthis.historyCache.set(typeName, filtered)\n\n\t\treturn filtered\n\t}\n\n\t/**\n\t * Creates a reactive index that maps property values to sets of record IDs for efficient lookups.\n\t * The index automatically updates when records are added, updated, or removed, and results are cached\n\t * for performance.\n\t *\n\t * Supports nested property paths using backslash separator (e.g., 'metadata\\\\sessionId').\n\t *\n\t * @param typeName - The type name of records to index\n\t * @param path - The property name or backslash-delimited path to index by\n\t * @returns A reactive computed containing the index map with change diffs\n\t *\n\t * @example\n\t * ```ts\n\t * // Create an index of books by author ID\n\t * const booksByAuthor = store.query.index('book', 'authorId')\n\t *\n\t * // Get all books by a specific author\n\t * const authorBooks = booksByAuthor.get().get('author:leguin')\n\t * console.log(authorBooks) // Set<RecordId<Book>>\n\t *\n\t * // Index by nested property using backslash separator\n\t * const booksBySession = store.query.index('book', 'metadata\\\\sessionId')\n\t * const sessionBooks = booksBySession.get().get('session:alpha')\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic index<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tpath: string\n\t): RSIndex<Extract<R, { typeName: TypeName }>> {\n\t\tconst cacheKey = typeName + ':' + path\n\n\t\tif (this.indexCache.has(cacheKey)) {\n\t\t\treturn this.indexCache.get(cacheKey) as any\n\t\t}\n\n\t\tconst index = this.__uncached_createIndex(typeName, path)\n\n\t\tthis.indexCache.set(cacheKey, index as any)\n\n\t\treturn index\n\t}\n\n\t/**\n\t * Creates a new index without checking the cache. This method performs the actual work\n\t * of building the reactive index computation that tracks property values to record ID sets.\n\t *\n\t * Supports nested property paths using backslash separator.\n\t *\n\t * @param typeName - The type name of records to index\n\t * @param path - The property name or backslash-delimited path to index by\n\t * @returns A reactive computed containing the index map with change diffs\n\t *\n\t * @internal\n\t */\n\t__uncached_createIndex<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tpath: string\n\t): RSIndex<Extract<R, { typeName: TypeName }>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tconst typeHistory = this.filterHistory(typeName)\n\n\t\t// Create closure for efficient property value extraction\n\t\tconst pathParts = path.split('\\\\')\n\t\tconst getPropertyValue =\n\t\t\tpathParts.length > 1\n\t\t\t\t? (obj: S) => this.getNestedValue(obj, pathParts)\n\t\t\t\t: (obj: S) => obj[path as keyof S]\n\n\t\tconst fromScratch = () => {\n\t\t\t// deref typeHistory early so that the first time the incremental version runs\n\t\t\t// it gets a diff to work with instead of having to bail to this from-scratch version\n\t\t\ttypeHistory.get()\n\t\t\tconst res = new Map<any, Set<IdOf<S>>>()\n\t\t\tfor (const record of this.recordMap.values()) {\n\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\tconst value = getPropertyValue(record as S)\n\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\tif (!res.has(value)) {\n\t\t\t\t\t\t\tres.set(value, new Set())\n\t\t\t\t\t\t}\n\t\t\t\t\t\tres.get(value)!.add(record.id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn res\n\t\t}\n\n\t\treturn computed<RSIndexMap<S>, RSIndexDiff<S>>(\n\t\t\t'index:' + typeName + ':' + path,\n\t\t\t(prevValue, lastComputedEpoch) => {\n\t\t\t\tif (isUninitialized(prevValue)) return fromScratch()\n\n\t\t\t\tconst history = typeHistory.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (history === RESET_VALUE) {\n\t\t\t\t\treturn fromScratch()\n\t\t\t\t}\n\n\t\t\t\tconst setConstructors = new Map<any, IncrementalSetConstructor<IdOf<S>>>()\n\n\t\t\t\tconst add = (value: any, id: IdOf<S>) => {\n\t\t\t\t\tlet setConstructor = setConstructors.get(value)\n\t\t\t\t\tif (!setConstructor)\n\t\t\t\t\t\tsetConstructor = new IncrementalSetConstructor<IdOf<S>>(\n\t\t\t\t\t\t\tprevValue.get(value) ?? new Set()\n\t\t\t\t\t\t)\n\t\t\t\t\tsetConstructor.add(id)\n\t\t\t\t\tsetConstructors.set(value, setConstructor)\n\t\t\t\t}\n\n\t\t\t\tconst remove = (value: any, id: IdOf<S>) => {\n\t\t\t\t\tlet set = setConstructors.get(value)\n\t\t\t\t\tif (!set) set = new IncrementalSetConstructor<IdOf<S>>(prevValue.get(value) ?? new Set())\n\t\t\t\t\tset.remove(id)\n\t\t\t\t\tsetConstructors.set(value, set)\n\t\t\t\t}\n\n\t\t\t\tfor (const changes of history) {\n\t\t\t\t\tfor (const record of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\t\t\tconst value = getPropertyValue(record as S)\n\t\t\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\t\t\tadd(value, record.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [from, to] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (to.typeName === typeName) {\n\t\t\t\t\t\t\tconst prev = getPropertyValue(from as S)\n\t\t\t\t\t\t\tconst next = getPropertyValue(to as S)\n\t\t\t\t\t\t\tif (prev !== next) {\n\t\t\t\t\t\t\t\tif (prev !== undefined) {\n\t\t\t\t\t\t\t\t\tremove(prev, to.id)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (next !== undefined) {\n\t\t\t\t\t\t\t\t\tadd(next, to.id)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const record of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\t\t\tconst value = getPropertyValue(record as S)\n\t\t\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\t\t\tremove(value, record.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet nextValue: undefined | RSIndexMap<S> = undefined\n\t\t\t\tlet nextDiff: undefined | RSIndexDiff<S> = undefined\n\n\t\t\t\tfor (const [value, setConstructor] of setConstructors) {\n\t\t\t\t\tconst result = setConstructor.get()\n\t\t\t\t\tif (!result) continue\n\t\t\t\t\tif (!nextValue) nextValue = new Map(prevValue)\n\t\t\t\t\tif (!nextDiff) nextDiff = new Map()\n\t\t\t\t\tif (result.value.size === 0) {\n\t\t\t\t\t\tnextValue.delete(value)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextValue.set(value, result.value)\n\t\t\t\t\t}\n\t\t\t\t\tnextDiff.set(value, result.diff)\n\t\t\t\t}\n\n\t\t\t\tif (nextValue && nextDiff) {\n\t\t\t\t\treturn withDiff(nextValue, nextDiff)\n\t\t\t\t}\n\n\t\t\t\treturn prevValue\n\t\t\t},\n\t\t\t{ historyLength: 100 }\n\t\t)\n\t}\n\n\t/**\n\t * Creates a reactive query that returns the first record matching the given query criteria.\n\t * Returns undefined if no matching record is found. The query automatically updates\n\t * when records change.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing the first matching record or undefined\n\t *\n\t * @example\n\t * ```ts\n\t * // Find the first book with a specific title\n\t * const bookLatheOfHeaven = store.query.record('book', () => ({ title: { eq: 'The Lathe of Heaven' } }))\n\t * console.log(bookLatheOfHeaven.get()?.title) // 'The Lathe of Heaven' or undefined\n\t *\n\t * // Find any book in stock\n\t * const anyInStockBook = store.query.record('book', () => ({ inStock: { eq: true } }))\n\t * ```\n\t *\n\t * @public\n\t */\n\trecord<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'record:' + typeName + (queryCreator ? ':' + queryCreator.toString() : '')\n\t): Computed<Extract<R, { typeName: TypeName }> | undefined> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = this.ids(typeName, queryCreator, name)\n\n\t\treturn computed<S | undefined>(name, () => {\n\t\t\tfor (const id of ids.get()) {\n\t\t\t\treturn this.recordMap.get(id) as S | undefined\n\t\t\t}\n\t\t\treturn undefined\n\t\t})\n\t}\n\n\t/**\n\t * Creates a reactive query that returns an array of all records matching the given query criteria.\n\t * The array automatically updates when records are added, updated, or removed.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing an array of all matching records\n\t *\n\t * @example\n\t * ```ts\n\t * // Get all books in stock\n\t * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))\n\t * console.log(inStockBooks.get()) // Book[]\n\t *\n\t * // Get all books by a specific author\n\t * const leguinBooks = store.query.records('book', () => ({ authorId: { eq: 'author:leguin' } }))\n\t *\n\t * // Get all books (no filter)\n\t * const allBooks = store.query.records('book')\n\t * ```\n\t *\n\t * @public\n\t */\n\trecords<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'records:' + typeName + (queryCreator ? ':' + queryCreator.toString() : '')\n\t): Computed<Array<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = this.ids(typeName, queryCreator, 'ids:' + name)\n\n\t\treturn computed<S[]>(\n\t\t\tname,\n\t\t\t() => {\n\t\t\t\treturn Array.from(ids.get(), (id) => this.recordMap.get(id) as S)\n\t\t\t},\n\t\t\t{\n\t\t\t\tisEqual: areArraysShallowEqual,\n\t\t\t}\n\t\t)\n\t}\n\n\t/**\n\t * Creates a reactive query that returns a set of record IDs matching the given query criteria.\n\t * This is more efficient than `records()` when you only need the IDs and not the full record objects.\n\t * The set automatically updates with collection diffs when records change.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing a set of matching record IDs with collection diffs\n\t *\n\t * @example\n\t * ```ts\n\t * // Get IDs of all books in stock\n\t * const inStockBookIds = store.query.ids('book', () => ({ inStock: { eq: true } }))\n\t * console.log(inStockBookIds.get()) // Set<RecordId<Book>>\n\t *\n\t * // Get all book IDs (no filter)\n\t * const allBookIds = store.query.ids('book')\n\t *\n\t * // Use with other queries for efficient lookups\n\t * const authorBookIds = store.query.ids('book', () => ({ authorId: { eq: 'author:leguin' } }))\n\t * ```\n\t *\n\t * @public\n\t */\n\tids<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'ids:' + typeName + (queryCreator ? ':' + queryCreator.toString() : '')\n\t): Computed<\n\t\tSet<IdOf<Extract<R, { typeName: TypeName }>>>,\n\t\tCollectionDiff<IdOf<Extract<R, { typeName: TypeName }>>>\n\t> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tconst typeHistory = this.filterHistory(typeName)\n\n\t\tconst fromScratch = () => {\n\t\t\t// deref type history early to allow first incremental update to use diffs\n\t\t\ttypeHistory.get()\n\t\t\tconst query: QueryExpression<S> = queryCreator()\n\t\t\tif (Object.keys(query).length === 0) {\n\t\t\t\treturn this.getAllIdsForType(typeName)\n\t\t\t}\n\n\t\t\treturn executeQuery(this, typeName, query)\n\t\t}\n\n\t\tconst fromScratchWithDiff = (prevValue: Set<IdOf<S>>) => {\n\t\t\tconst nextValue = fromScratch()\n\t\t\tconst diff = diffSets(prevValue, nextValue)\n\t\t\tif (diff) {\n\t\t\t\treturn withDiff(nextValue, diff)\n\t\t\t} else {\n\t\t\t\treturn prevValue\n\t\t\t}\n\t\t}\n\t\tconst cachedQuery = computed('ids_query:' + name, queryCreator, {\n\t\t\tisEqual,\n\t\t})\n\n\t\treturn computed(\n\t\t\t'query:' + name,\n\t\t\t(prevValue, lastComputedEpoch) => {\n\t\t\t\tconst query = cachedQuery.get()\n\t\t\t\tif (isUninitialized(prevValue)) {\n\t\t\t\t\treturn fromScratch()\n\t\t\t\t}\n\n\t\t\t\t// if the query changed since last time this ran then we need to start again\n\t\t\t\tif (lastComputedEpoch < cachedQuery.lastChangedEpoch) {\n\t\t\t\t\treturn fromScratchWithDiff(prevValue)\n\t\t\t\t}\n\n\t\t\t\t// otherwise iterate over the changes from the store and apply them to the previous value if needed\n\t\t\t\tconst history = typeHistory.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (history === RESET_VALUE) {\n\t\t\t\t\treturn fromScratchWithDiff(prevValue)\n\t\t\t\t}\n\n\t\t\t\tconst setConstructor = new IncrementalSetConstructor<IdOf<S>>(\n\t\t\t\t\tprevValue\n\t\t\t\t) as IncrementalSetConstructor<IdOf<S>>\n\n\t\t\t\tfor (const changes of history) {\n\t\t\t\t\tfor (const added of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (added.typeName === typeName && objectMatchesQuery(query, added)) {\n\t\t\t\t\t\t\tsetConstructor.add(added.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [_, updated] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (updated.typeName === typeName) {\n\t\t\t\t\t\t\tif (objectMatchesQuery(query, updated)) {\n\t\t\t\t\t\t\t\tsetConstructor.add(updated.id)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsetConstructor.remove(updated.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const removed of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (removed.typeName === typeName) {\n\t\t\t\t\t\t\tsetConstructor.remove(removed.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst result = setConstructor.get()\n\t\t\t\tif (!result) {\n\t\t\t\t\treturn prevValue\n\t\t\t\t}\n\n\t\t\t\treturn withDiff(result.value, result.diff)\n\t\t\t},\n\t\t\t{ historyLength: 50 }\n\t\t)\n\t}\n\n\t/**\n\t * Executes a one-time query against the current store state and returns matching records.\n\t * This is a non-reactive query that returns results immediately without creating a computed value.\n\t * Use this when you need a snapshot of data at a specific point in time.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param query - The query expression object to match against\n\t * @returns An array of records that match the query at the current moment\n\t *\n\t * @example\n\t * ```ts\n\t * // Get current in-stock books (non-reactive)\n\t * const currentInStockBooks = store.query.exec('book', { inStock: { eq: true } })\n\t * console.log(currentInStockBooks) // Book[]\n\t *\n\t * // Unlike records(), this won't update when the data changes\n\t * const staticBookList = store.query.exec('book', { authorId: { eq: 'author:leguin' } })\n\t * ```\n\t *\n\t * @public\n\t */\n\texec<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tquery: QueryExpression<Extract<R, { typeName: TypeName }>>\n\t): Array<Extract<R, { typeName: TypeName }>> {\n\t\tconst ids = executeQuery(this, typeName, query)\n\t\tif (ids.size === 0) {\n\t\t\treturn EMPTY_ARRAY\n\t\t}\n\t\treturn Array.from(ids, (id) => this.recordMap.get(id) as Extract<R, { typeName: TypeName }>)\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tAtom,\n\tcomputed,\n\tComputed,\n\tEMPTY_ARRAY,\n\tisUninitialized,\n\tRESET_VALUE,\n\twithDiff,\n} from '@tldraw/state'\nimport { areArraysShallowEqual, isEqual, objectMapValues } from '@tldraw/utils'\nimport { AtomMap } from './AtomMap'\nimport { IdOf, UnknownRecord } from './BaseRecord'\nimport { executeQuery, objectMatchesQuery, QueryExpression } from './executeQuery'\nimport { IncrementalSetConstructor } from './IncrementalSetConstructor'\nimport { hasAnyKey, RecordsDiff, squashRecordDiffsMutableByType } from './RecordsDiff'\nimport { diffSets } from './setUtils'\nimport { CollectionDiff } from './Store'\n\n/**\n * A type representing the diff of changes to a reactive store index.\n * Maps property values to the collection differences for record IDs that have that property value.\n *\n * @example\n * ```ts\n * // For an index on book titles, the diff might look like:\n * const titleIndexDiff: RSIndexDiff<Book, 'title'> = new Map([\n * ['The Lathe of Heaven', { added: new Set(['book:1']), removed: new Set() }],\n * ['Animal Farm', { added: new Set(), removed: new Set(['book:2']) }]\n * ])\n * ```\n *\n * @public\n */\nexport type RSIndexDiff<R extends UnknownRecord> = Map<any, CollectionDiff<IdOf<R>>>\n\n/**\n * A type representing a reactive store index as a map from property values to sets of record IDs.\n * This is used to efficiently look up records by a specific property value.\n *\n * @example\n * ```ts\n * // Index mapping book titles to the IDs of books with that title\n * const titleIndex: RSIndexMap<Book, 'title'> = new Map([\n * ['The Lathe of Heaven', new Set(['book:1'])],\n * ['Animal Farm', new Set(['book:2', 'book:3'])]\n * ])\n * ```\n *\n * @public\n */\nexport type RSIndexMap<R extends UnknownRecord> = Map<any, Set<IdOf<R>>>\n\n/**\n * A reactive computed index that provides efficient lookups of records by property values.\n * Returns a computed value containing an RSIndexMap with diffs for change tracking.\n *\n * @example\n * ```ts\n * // Create an index on book authors\n * const authorIndex: RSIndex<Book, 'authorId'> = store.query.index('book', 'authorId')\n *\n * // Get all books by a specific author\n * const leguinBooks = authorIndex.get().get('author:leguin')\n * ```\n *\n * @public\n */\nexport type RSIndex<R extends UnknownRecord> = Computed<RSIndexMap<R>, RSIndexDiff<R>>\n\n/**\n * A class that provides reactive querying capabilities for a record store.\n * Offers methods to create indexes, filter records, and perform efficient lookups with automatic cache management.\n * All queries are reactive and will automatically update when the underlying store data changes.\n *\n * @example\n * ```ts\n * // Create a store with books\n * const store = new Store({ schema: StoreSchema.create({ book: Book, author: Author }) })\n *\n * // Get reactive queries for books\n * const booksByAuthor = store.query.index('book', 'authorId')\n * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))\n * ```\n *\n * @public\n */\nexport class StoreQueries<R extends UnknownRecord> {\n\t/**\n\t * Creates a new StoreQueries instance.\n\t *\n\t * recordMap - The atom map containing all records in the store\n\t * history - The atom tracking the store's change history with diffs\n\t *\n\t * @internal\n\t */\n\tconstructor(\n\t\tprivate readonly recordMap: AtomMap<IdOf<R>, R>,\n\t\tprivate readonly history: Atom<number, RecordsDiff<R>>\n\t) {}\n\n\t/**\n\t * A cache of derivations (indexes).\n\t *\n\t * @internal\n\t */\n\tprivate indexCache = new Map<string, RSIndex<R>>()\n\n\t/**\n\t * A cache of derivations (filtered histories).\n\t *\n\t * @internal\n\t */\n\tprivate historyCache = new Map<string, Computed<number, RecordsDiff<R>>>()\n\n\t/**\n\t * @internal\n\t */\n\tpublic getAllIdsForType<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName\n\t): Set<IdOf<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = new Set<IdOf<S>>()\n\t\tfor (const record of this.recordMap.values()) {\n\t\t\tif (record.typeName === typeName) {\n\t\t\t\tids.add(record.id as IdOf<S>)\n\t\t\t}\n\t\t}\n\t\treturn ids\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tpublic getRecordById<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tid: IdOf<Extract<R, { typeName: TypeName }>>\n\t): Extract<R, { typeName: TypeName }> | undefined {\n\t\tconst record = this.recordMap.get(id as IdOf<R>)\n\t\tif (record && record.typeName === typeName) {\n\t\t\treturn record as Extract<R, { typeName: TypeName }>\n\t\t}\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Helper to extract nested property value using pre-split path parts.\n\t * @internal\n\t */\n\tprivate getNestedValue(obj: any, pathParts: string[]): any {\n\t\tlet current = obj\n\t\tfor (const part of pathParts) {\n\t\t\tif (current == null || typeof current !== 'object') return undefined\n\t\t\tcurrent = current[part]\n\t\t}\n\t\treturn current\n\t}\n\n\t/**\n\t * Creates a reactive computed that tracks the change history for records of a specific type.\n\t * The returned computed provides incremental diffs showing what records of the given type\n\t * have been added, updated, or removed.\n\t *\n\t * @param typeName - The type name to filter the history by\n\t * @returns A computed value containing the current epoch and diffs of changes for the specified type\n\t *\n\t * @example\n\t * ```ts\n\t * // Track changes to book records only\n\t * const bookHistory = store.query.filterHistory('book')\n\t *\n\t * // React to book changes\n\t * react('book-changes', () => {\n\t * const currentEpoch = bookHistory.get()\n\t * console.log('Books updated at epoch:', currentEpoch)\n\t * })\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic filterHistory<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName\n\t): Computed<number, RecordsDiff<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tif (this.historyCache.has(typeName)) {\n\t\t\treturn this.historyCache.get(typeName) as any\n\t\t}\n\n\t\tconst filtered = computed<number, RecordsDiff<S>>(\n\t\t\t'filterHistory:' + typeName,\n\t\t\t(lastValue, lastComputedEpoch) => {\n\t\t\t\tif (isUninitialized(lastValue)) {\n\t\t\t\t\treturn this.history.get()\n\t\t\t\t}\n\n\t\t\t\tconst diff = this.history.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (diff === RESET_VALUE) return this.history.get()\n\n\t\t\t\tconst res = { added: {}, removed: {}, updated: {} } as RecordsDiff<S>\n\t\t\t\tconst size = squashRecordDiffsMutableByType(res, diff, typeName)\n\n\t\t\t\tif (size) {\n\t\t\t\t\treturn withDiff(this.history.get(), res)\n\t\t\t\t} else {\n\t\t\t\t\treturn lastValue\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ historyLength: 100 }\n\t\t)\n\n\t\tthis.historyCache.set(typeName, filtered)\n\n\t\treturn filtered\n\t}\n\n\t/**\n\t * Creates a reactive index that maps property values to sets of record IDs for efficient lookups.\n\t * The index automatically updates when records are added, updated, or removed, and results are cached\n\t * for performance.\n\t *\n\t * Supports nested property paths using backslash separator (e.g., 'metadata\\\\sessionId').\n\t *\n\t * @param typeName - The type name of records to index\n\t * @param path - The property name or backslash-delimited path to index by\n\t * @returns A reactive computed containing the index map with change diffs\n\t *\n\t * @example\n\t * ```ts\n\t * // Create an index of books by author ID\n\t * const booksByAuthor = store.query.index('book', 'authorId')\n\t *\n\t * // Get all books by a specific author\n\t * const authorBooks = booksByAuthor.get().get('author:leguin')\n\t * console.log(authorBooks) // Set<RecordId<Book>>\n\t *\n\t * // Index by nested property using backslash separator\n\t * const booksBySession = store.query.index('book', 'metadata\\\\sessionId')\n\t * const sessionBooks = booksBySession.get().get('session:alpha')\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic index<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tpath: string\n\t): RSIndex<Extract<R, { typeName: TypeName }>> {\n\t\tconst cacheKey = typeName + ':' + path\n\n\t\tif (this.indexCache.has(cacheKey)) {\n\t\t\treturn this.indexCache.get(cacheKey) as any\n\t\t}\n\n\t\tconst index = this.__uncached_createIndex(typeName, path)\n\n\t\tthis.indexCache.set(cacheKey, index as any)\n\n\t\treturn index\n\t}\n\n\t/**\n\t * Creates a new index without checking the cache. This method performs the actual work\n\t * of building the reactive index computation that tracks property values to record ID sets.\n\t *\n\t * Supports nested property paths using backslash separator.\n\t *\n\t * @param typeName - The type name of records to index\n\t * @param path - The property name or backslash-delimited path to index by\n\t * @returns A reactive computed containing the index map with change diffs\n\t *\n\t * @internal\n\t */\n\t__uncached_createIndex<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tpath: string\n\t): RSIndex<Extract<R, { typeName: TypeName }>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tconst typeHistory = this.filterHistory(typeName)\n\n\t\t// Create closure for efficient property value extraction\n\t\tconst pathParts = path.split('\\\\')\n\t\tconst getPropertyValue =\n\t\t\tpathParts.length > 1\n\t\t\t\t? (obj: S) => this.getNestedValue(obj, pathParts)\n\t\t\t\t: (obj: S) => obj[path as keyof S]\n\n\t\tconst fromScratch = () => {\n\t\t\t// deref typeHistory early so that the first time the incremental version runs\n\t\t\t// it gets a diff to work with instead of having to bail to this from-scratch version\n\t\t\ttypeHistory.get()\n\t\t\tconst res = new Map<any, Set<IdOf<S>>>()\n\t\t\tfor (const record of this.recordMap.values()) {\n\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\tconst value = getPropertyValue(record as S)\n\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\tif (!res.has(value)) {\n\t\t\t\t\t\t\tres.set(value, new Set())\n\t\t\t\t\t\t}\n\t\t\t\t\t\tres.get(value)!.add(record.id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn res\n\t\t}\n\n\t\treturn computed<RSIndexMap<S>, RSIndexDiff<S>>(\n\t\t\t'index:' + typeName + ':' + path,\n\t\t\t(prevValue, lastComputedEpoch) => {\n\t\t\t\tif (isUninitialized(prevValue)) return fromScratch()\n\n\t\t\t\tconst history = typeHistory.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (history === RESET_VALUE) {\n\t\t\t\t\treturn fromScratch()\n\t\t\t\t}\n\n\t\t\t\tconst setConstructors = new Map<any, IncrementalSetConstructor<IdOf<S>>>()\n\n\t\t\t\tconst add = (value: any, id: IdOf<S>) => {\n\t\t\t\t\tlet setConstructor = setConstructors.get(value)\n\t\t\t\t\tif (!setConstructor)\n\t\t\t\t\t\tsetConstructor = new IncrementalSetConstructor<IdOf<S>>(\n\t\t\t\t\t\t\tprevValue.get(value) ?? new Set()\n\t\t\t\t\t\t)\n\t\t\t\t\tsetConstructor.add(id)\n\t\t\t\t\tsetConstructors.set(value, setConstructor)\n\t\t\t\t}\n\n\t\t\t\tconst remove = (value: any, id: IdOf<S>) => {\n\t\t\t\t\tlet set = setConstructors.get(value)\n\t\t\t\t\tif (!set) set = new IncrementalSetConstructor<IdOf<S>>(prevValue.get(value) ?? new Set())\n\t\t\t\t\tset.remove(id)\n\t\t\t\t\tsetConstructors.set(value, set)\n\t\t\t\t}\n\n\t\t\t\tfor (const changes of history) {\n\t\t\t\t\tfor (const record of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\t\t\tconst value = getPropertyValue(record as S)\n\t\t\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\t\t\tadd(value, record.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [from, to] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (to.typeName === typeName) {\n\t\t\t\t\t\t\tconst prev = getPropertyValue(from as S)\n\t\t\t\t\t\t\tconst next = getPropertyValue(to as S)\n\t\t\t\t\t\t\tif (prev !== next) {\n\t\t\t\t\t\t\t\tif (prev !== undefined) {\n\t\t\t\t\t\t\t\t\tremove(prev, to.id)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (next !== undefined) {\n\t\t\t\t\t\t\t\t\tadd(next, to.id)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const record of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\t\t\tconst value = getPropertyValue(record as S)\n\t\t\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\t\t\tremove(value, record.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet nextValue: undefined | RSIndexMap<S> = undefined\n\t\t\t\tlet nextDiff: undefined | RSIndexDiff<S> = undefined\n\n\t\t\t\tfor (const [value, setConstructor] of setConstructors) {\n\t\t\t\t\tconst result = setConstructor.get()\n\t\t\t\t\tif (!result) continue\n\t\t\t\t\tif (!nextValue) nextValue = new Map(prevValue)\n\t\t\t\t\tif (!nextDiff) nextDiff = new Map()\n\t\t\t\t\tif (result.value.size === 0) {\n\t\t\t\t\t\tnextValue.delete(value)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextValue.set(value, result.value)\n\t\t\t\t\t}\n\t\t\t\t\tnextDiff.set(value, result.diff)\n\t\t\t\t}\n\n\t\t\t\tif (nextValue && nextDiff) {\n\t\t\t\t\treturn withDiff(nextValue, nextDiff)\n\t\t\t\t}\n\n\t\t\t\treturn prevValue\n\t\t\t},\n\t\t\t{ historyLength: 100 }\n\t\t)\n\t}\n\n\t/**\n\t * Creates a reactive query that returns the first record matching the given query criteria.\n\t * Returns undefined if no matching record is found. The query automatically updates\n\t * when records change.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing the first matching record or undefined\n\t *\n\t * @example\n\t * ```ts\n\t * // Find the first book with a specific title\n\t * const bookLatheOfHeaven = store.query.record('book', () => ({ title: { eq: 'The Lathe of Heaven' } }))\n\t * console.log(bookLatheOfHeaven.get()?.title) // 'The Lathe of Heaven' or undefined\n\t *\n\t * // Find any book in stock\n\t * const anyInStockBook = store.query.record('book', () => ({ inStock: { eq: true } }))\n\t * ```\n\t *\n\t * @public\n\t */\n\trecord<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'record:' + typeName + ':' + queryCreator.toString()\n\t): Computed<Extract<R, { typeName: TypeName }> | undefined> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = this.ids(typeName, queryCreator, name)\n\n\t\treturn computed<S | undefined>(name, () => {\n\t\t\tfor (const id of ids.get()) {\n\t\t\t\treturn this.recordMap.get(id) as S | undefined\n\t\t\t}\n\t\t\treturn undefined\n\t\t})\n\t}\n\n\t/**\n\t * Creates a reactive query that returns an array of all records matching the given query criteria.\n\t * The array automatically updates when records are added, updated, or removed.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing an array of all matching records\n\t *\n\t * @example\n\t * ```ts\n\t * // Get all books in stock\n\t * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))\n\t * console.log(inStockBooks.get()) // Book[]\n\t *\n\t * // Get all books by a specific author\n\t * const leguinBooks = store.query.records('book', () => ({ authorId: { eq: 'author:leguin' } }))\n\t *\n\t * // Get all books (no filter)\n\t * const allBooks = store.query.records('book')\n\t * ```\n\t *\n\t * @public\n\t */\n\trecords<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'records:' + typeName + ':' + queryCreator.toString()\n\t): Computed<Array<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = this.ids(typeName, queryCreator, 'ids:' + name)\n\n\t\treturn computed<S[]>(\n\t\t\tname,\n\t\t\t() => {\n\t\t\t\treturn Array.from(ids.get(), (id) => this.recordMap.get(id) as S)\n\t\t\t},\n\t\t\t{\n\t\t\t\tisEqual: areArraysShallowEqual,\n\t\t\t}\n\t\t)\n\t}\n\n\t/**\n\t * Creates a reactive query that returns a set of record IDs matching the given query criteria.\n\t * This is more efficient than `records()` when you only need the IDs and not the full record objects.\n\t * The set automatically updates with collection diffs when records change.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing a set of matching record IDs with collection diffs\n\t *\n\t * @example\n\t * ```ts\n\t * // Get IDs of all books in stock\n\t * const inStockBookIds = store.query.ids('book', () => ({ inStock: { eq: true } }))\n\t * console.log(inStockBookIds.get()) // Set<RecordId<Book>>\n\t *\n\t * // Get all book IDs (no filter)\n\t * const allBookIds = store.query.ids('book')\n\t *\n\t * // Use with other queries for efficient lookups\n\t * const authorBookIds = store.query.ids('book', () => ({ authorId: { eq: 'author:leguin' } }))\n\t * ```\n\t *\n\t * @public\n\t */\n\tids<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'ids:' + typeName + ':' + queryCreator.toString()\n\t): Computed<\n\t\tSet<IdOf<Extract<R, { typeName: TypeName }>>>,\n\t\tCollectionDiff<IdOf<Extract<R, { typeName: TypeName }>>>\n\t> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tconst typeHistory = this.filterHistory(typeName)\n\n\t\tconst fromScratch = () => {\n\t\t\t// deref type history early to allow first incremental update to use diffs\n\t\t\ttypeHistory.get()\n\t\t\tconst query: QueryExpression<S> = queryCreator()\n\t\t\tif (!hasAnyKey(query)) {\n\t\t\t\treturn this.getAllIdsForType(typeName)\n\t\t\t}\n\n\t\t\treturn executeQuery(this, typeName, query)\n\t\t}\n\n\t\tconst fromScratchWithDiff = (prevValue: Set<IdOf<S>>) => {\n\t\t\tconst nextValue = fromScratch()\n\t\t\tconst diff = diffSets(prevValue, nextValue)\n\t\t\tif (diff) {\n\t\t\t\treturn withDiff(nextValue, diff)\n\t\t\t} else {\n\t\t\t\treturn prevValue\n\t\t\t}\n\t\t}\n\t\tconst cachedQuery = computed('ids_query:' + name, queryCreator, {\n\t\t\tisEqual,\n\t\t})\n\n\t\treturn computed(\n\t\t\t'query:' + name,\n\t\t\t(prevValue, lastComputedEpoch) => {\n\t\t\t\tconst query = cachedQuery.get()\n\t\t\t\tif (isUninitialized(prevValue)) {\n\t\t\t\t\treturn fromScratch()\n\t\t\t\t}\n\n\t\t\t\t// if the query changed since last time this ran then we need to start again\n\t\t\t\tif (lastComputedEpoch < cachedQuery.lastChangedEpoch) {\n\t\t\t\t\treturn fromScratchWithDiff(prevValue)\n\t\t\t\t}\n\n\t\t\t\t// otherwise iterate over the changes from the store and apply them to the previous value if needed\n\t\t\t\tconst history = typeHistory.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (history === RESET_VALUE) {\n\t\t\t\t\treturn fromScratchWithDiff(prevValue)\n\t\t\t\t}\n\n\t\t\t\tconst setConstructor = new IncrementalSetConstructor<IdOf<S>>(\n\t\t\t\t\tprevValue\n\t\t\t\t) as IncrementalSetConstructor<IdOf<S>>\n\n\t\t\t\tfor (const changes of history) {\n\t\t\t\t\tfor (const added of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (added.typeName === typeName && objectMatchesQuery(query, added)) {\n\t\t\t\t\t\t\tsetConstructor.add(added.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [_, updated] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (updated.typeName === typeName) {\n\t\t\t\t\t\t\tif (objectMatchesQuery(query, updated)) {\n\t\t\t\t\t\t\t\tsetConstructor.add(updated.id)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsetConstructor.remove(updated.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const removed of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (removed.typeName === typeName) {\n\t\t\t\t\t\t\tsetConstructor.remove(removed.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst result = setConstructor.get()\n\t\t\t\tif (!result) {\n\t\t\t\t\treturn prevValue\n\t\t\t\t}\n\n\t\t\t\treturn withDiff(result.value, result.diff)\n\t\t\t},\n\t\t\t{ historyLength: 50 }\n\t\t)\n\t}\n\n\t/**\n\t * Executes a one-time query against the current store state and returns matching records.\n\t * This is a non-reactive query that returns results immediately without creating a computed value.\n\t * Use this when you need a snapshot of data at a specific point in time.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param query - The query expression object to match against\n\t * @returns An array of records that match the query at the current moment\n\t *\n\t * @example\n\t * ```ts\n\t * // Get current in-stock books (non-reactive)\n\t * const currentInStockBooks = store.query.exec('book', { inStock: { eq: true } })\n\t * console.log(currentInStockBooks) // Book[]\n\t *\n\t * // Unlike records(), this won't update when the data changes\n\t * const staticBookList = store.query.exec('book', { authorId: { eq: 'author:leguin' } })\n\t * ```\n\t *\n\t * @public\n\t */\n\texec<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tquery: QueryExpression<Extract<R, { typeName: TypeName }>>\n\t): Array<Extract<R, { typeName: TypeName }>> {\n\t\tconst ids = executeQuery(this, typeName, query)\n\t\tif (ids.size === 0) {\n\t\t\treturn EMPTY_ARRAY\n\t\t}\n\t\treturn Array.from(ids, (id) => this.recordMap.get(id) as Extract<R, { typeName: TypeName }>)\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -172,9 +172,13 @@ class StoreSchema {
172
172
  if (cached) {
173
173
  return cached;
174
174
  }
175
+ const result = this.computeMigrationsSince(persistedSchema);
176
+ this.migrationCache.set(persistedSchema, result);
177
+ return result;
178
+ }
179
+ computeMigrationsSince(persistedSchema) {
175
180
  const upgradeResult = upgradeSchema(persistedSchema);
176
181
  if (!upgradeResult.ok) {
177
- this.migrationCache.set(persistedSchema, upgradeResult);
178
182
  return upgradeResult;
179
183
  }
180
184
  const schema = upgradeResult.value;
@@ -188,9 +192,7 @@ class StoreSchema {
188
192
  }
189
193
  }
190
194
  if (sequenceIdsToInclude.size === 0) {
191
- const result2 = Result.ok([]);
192
- this.migrationCache.set(persistedSchema, result2);
193
- return result2;
195
+ return Result.ok([]);
194
196
  }
195
197
  const allMigrationsToInclude = /* @__PURE__ */ new Set();
196
198
  for (const sequenceId of sequenceIdsToInclude) {
@@ -204,19 +206,13 @@ class StoreSchema {
204
206
  const theirVersionId = `${sequenceId}/${theirVersion}`;
205
207
  const idx = this.migrations[sequenceId].sequence.findIndex((m) => m.id === theirVersionId);
206
208
  if (idx === -1) {
207
- const result2 = Result.err("Incompatible schema?");
208
- this.migrationCache.set(persistedSchema, result2);
209
- return result2;
209
+ return Result.err("Incompatible schema?");
210
210
  }
211
211
  for (const migration of this.migrations[sequenceId].sequence.slice(idx + 1)) {
212
212
  allMigrationsToInclude.add(migration.id);
213
213
  }
214
214
  }
215
- const result = Result.ok(
216
- this.sortedMigrations.filter(({ id }) => allMigrationsToInclude.has(id))
217
- );
218
- this.migrationCache.set(persistedSchema, result);
219
- return result;
215
+ return Result.ok(this.sortedMigrations.filter(({ id }) => allMigrationsToInclude.has(id)));
220
216
  }
221
217
  /**
222
218
  * Migrates a single persisted record to match the current schema version.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/StoreSchema.ts"],
4
- "sourcesContent": ["import {\n\tassert,\n\texhaustiveSwitchError,\n\tgetOwnProperty,\n\tisEqual,\n\tobjectMapEntries,\n\tResult,\n\tstructuredClone,\n} from '@tldraw/utils'\nimport { UnknownRecord } from './BaseRecord'\nimport { devFreeze } from './devFreeze'\nimport {\n\tMigration,\n\tMigrationFailureReason,\n\tMigrationId,\n\tMigrationResult,\n\tMigrationSequence,\n\tparseMigrationId,\n\tsortMigrations,\n\tSynchronousStorage,\n\tvalidateMigrations,\n} from './migrate'\nimport { RecordType } from './RecordType'\nimport { SerializedStore, Store, StoreSnapshot } from './Store'\n\n/**\n * Version 1 format for serialized store schema information.\n *\n * This is the legacy format used before schema version 2. Version 1 schemas\n * separate store-level versioning from record-level versioning, and support\n * subtypes for complex record types like shapes.\n *\n * @example\n * ```ts\n * const schemaV1: SerializedSchemaV1 = {\n * schemaVersion: 1,\n * storeVersion: 2,\n * recordVersions: {\n * book: { version: 3 },\n * shape: {\n * version: 2,\n * subTypeVersions: { rectangle: 1, circle: 2 },\n * subTypeKey: 'type'\n * }\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface SerializedSchemaV1 {\n\t/** Schema version is the version for this type you're looking at right now */\n\tschemaVersion: 1\n\t/**\n\t * Store version is the version for the structure of the store. e.g. higher level structure like\n\t * removing or renaming a record type.\n\t */\n\tstoreVersion: number\n\t/** Record versions are the versions for each record type. e.g. adding a new field to a record */\n\trecordVersions: Record<\n\t\tstring,\n\t\t| {\n\t\t\t\tversion: number\n\t\t }\n\t\t| {\n\t\t\t\t// subtypes are used for migrating shape and asset props\n\t\t\t\tversion: number\n\t\t\t\tsubTypeVersions: Record<string, number>\n\t\t\t\tsubTypeKey: string\n\t\t }\n\t>\n}\n\n/**\n * Version 2 format for serialized store schema information.\n *\n * This is the current format that uses a unified sequence-based approach\n * for tracking versions across all migration sequences. Each sequence ID\n * maps to the latest version number for that sequence.\n *\n * @example\n * ```ts\n * const schemaV2: SerializedSchemaV2 = {\n * schemaVersion: 2,\n * sequences: {\n * 'com.tldraw.store': 3,\n * 'com.tldraw.book': 2,\n * 'com.tldraw.shape': 4,\n * 'com.tldraw.shape.rectangle': 1\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface SerializedSchemaV2 {\n\tschemaVersion: 2\n\tsequences: {\n\t\t[sequenceId: string]: number\n\t}\n}\n\n/**\n * Union type representing all supported serialized schema formats.\n *\n * This type allows the store to handle both legacy (V1) and current (V2)\n * schema formats during deserialization and migration.\n *\n * @example\n * ```ts\n * function handleSchema(schema: SerializedSchema) {\n * if (schema.schemaVersion === 1) {\n * // Handle V1 format\n * console.log('Store version:', schema.storeVersion)\n * } else {\n * // Handle V2 format\n * console.log('Sequences:', schema.sequences)\n * }\n * }\n * ```\n *\n * @public\n */\nexport type SerializedSchema = SerializedSchemaV1 | SerializedSchemaV2\n\n/**\n * Upgrades a serialized schema from version 1 to version 2 format.\n *\n * Version 1 schemas use separate `storeVersion` and `recordVersions` fields,\n * while version 2 schemas use a unified `sequences` object with sequence IDs.\n *\n * @param schema - The serialized schema to upgrade\n * @returns A Result containing the upgraded schema or an error message\n *\n * @example\n * ```ts\n * const v1Schema = {\n * schemaVersion: 1,\n * storeVersion: 1,\n * recordVersions: {\n * book: { version: 2 },\n * author: { version: 1, subTypeVersions: { fiction: 1 }, subTypeKey: 'genre' }\n * }\n * }\n *\n * const result = upgradeSchema(v1Schema)\n * if (result.ok) {\n * console.log(result.value.sequences)\n * // { 'com.tldraw.store': 1, 'com.tldraw.book': 2, 'com.tldraw.author': 1, 'com.tldraw.author.fiction': 1 }\n * }\n * ```\n *\n * @public\n */\nexport function upgradeSchema(schema: SerializedSchema): Result<SerializedSchemaV2, string> {\n\tif (schema.schemaVersion > 2 || schema.schemaVersion < 1) return Result.err('Bad schema version')\n\tif (schema.schemaVersion === 2) return Result.ok(schema as SerializedSchemaV2)\n\tconst result: SerializedSchemaV2 = {\n\t\tschemaVersion: 2,\n\t\tsequences: {\n\t\t\t'com.tldraw.store': schema.storeVersion,\n\t\t},\n\t}\n\n\tfor (const [typeName, recordVersion] of Object.entries(schema.recordVersions)) {\n\t\tresult.sequences[`com.tldraw.${typeName}`] = recordVersion.version\n\t\tif ('subTypeKey' in recordVersion) {\n\t\t\tfor (const [subType, version] of Object.entries(recordVersion.subTypeVersions)) {\n\t\t\t\tresult.sequences[`com.tldraw.${typeName}.${subType}`] = version\n\t\t\t}\n\t\t}\n\t}\n\treturn Result.ok(result)\n}\n\n/**\n * Information about a record validation failure that occurred in the store.\n *\n * This interface provides context about validation errors, including the failed\n * record, the store state, and the operation phase where the failure occurred.\n * It's used by validation failure handlers to implement recovery strategies.\n *\n * @example\n * ```ts\n * const schema = StoreSchema.create(\n * { book: Book },\n * {\n * onValidationFailure: (failure: StoreValidationFailure<Book>) => {\n * console.error(`Validation failed during ${failure.phase}:`, failure.error)\n * console.log('Failed record:', failure.record)\n * console.log('Previous record:', failure.recordBefore)\n *\n * // Return a corrected version of the record\n * return { ...failure.record, title: failure.record.title || 'Untitled' }\n * }\n * }\n * )\n * ```\n *\n * @public\n */\nexport interface StoreValidationFailure<R extends UnknownRecord> {\n\terror: unknown\n\tstore: Store<R>\n\trecord: R\n\tphase: 'initialize' | 'createRecord' | 'updateRecord' | 'tests'\n\trecordBefore: R | null\n}\n\n/**\n * Configuration options for creating a StoreSchema.\n *\n * These options control migration behavior, validation error handling,\n * and integrity checking for the store schema.\n *\n * @example\n * ```ts\n * const options: StoreSchemaOptions<MyRecord, MyProps> = {\n * migrations: [bookMigrations, authorMigrations],\n * onValidationFailure: (failure) => {\n * // Log the error and return a corrected record\n * console.error('Validation failed:', failure.error)\n * return sanitizeRecord(failure.record)\n * },\n * createIntegrityChecker: (store) => {\n * // Set up integrity checking logic\n * return setupIntegrityChecks(store)\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface StoreSchemaOptions<R extends UnknownRecord, P> {\n\tmigrations?: MigrationSequence[]\n\t/** @public */\n\tonValidationFailure?(data: StoreValidationFailure<R>): R\n\t/** @internal */\n\tcreateIntegrityChecker?(store: Store<R, P>): void\n}\n\n/**\n * Manages the schema definition, validation, and migration system for a Store.\n *\n * StoreSchema coordinates record types, handles data migrations between schema\n * versions, validates records, and provides the foundational structure for\n * reactive stores. It acts as the central authority for data consistency\n * and evolution within the store system.\n *\n * @example\n * ```ts\n * // Define record types\n * const Book = createRecordType<Book>('book', { scope: 'document' })\n * const Author = createRecordType<Author>('author', { scope: 'document' })\n *\n * // Create schema with migrations\n * const schema = StoreSchema.create(\n * { book: Book, author: Author },\n * {\n * migrations: [bookMigrations, authorMigrations],\n * onValidationFailure: (failure) => {\n * console.warn('Validation failed, using default:', failure.error)\n * return failure.record // or return a corrected version\n * }\n * }\n * )\n *\n * // Use with store\n * const store = new Store({ schema })\n * ```\n *\n * @public\n */\nexport class StoreSchema<R extends UnknownRecord, P = unknown> {\n\t/**\n\t * Creates a new StoreSchema with the given record types and options.\n\t *\n\t * This static factory method is the recommended way to create a StoreSchema.\n\t * It ensures type safety while providing a clean API for schema definition.\n\t *\n\t * @param types - Object mapping type names to their RecordType definitions\n\t * @param options - Optional configuration for migrations, validation, and integrity checking\n\t * @returns A new StoreSchema instance\n\t *\n\t * @example\n\t * ```ts\n\t * const Book = createRecordType<Book>('book', { scope: 'document' })\n\t * const Author = createRecordType<Author>('author', { scope: 'document' })\n\t *\n\t * const schema = StoreSchema.create(\n\t * {\n\t * book: Book,\n\t * author: Author\n\t * },\n\t * {\n\t * migrations: [bookMigrations],\n\t * onValidationFailure: (failure) => failure.record\n\t * }\n\t * )\n\t * ```\n\t *\n\t * @public\n\t */\n\tstatic create<R extends UnknownRecord, P = unknown>(\n\t\t// HACK: making this param work with RecordType is an enormous pain\n\t\t// let's just settle for making sure each typeName has a corresponding RecordType\n\t\t// and accept that this function won't be able to infer the record type from it's arguments\n\t\ttypes: { [TypeName in R['typeName']]: { createId: any } },\n\t\toptions?: StoreSchemaOptions<R, P>\n\t): StoreSchema<R, P> {\n\t\treturn new StoreSchema<R, P>(types as any, options ?? {})\n\t}\n\n\treadonly migrations: Record<string, MigrationSequence> = {}\n\treadonly sortedMigrations: readonly Migration[]\n\tprivate readonly migrationCache = new WeakMap<SerializedSchema, Result<Migration[], string>>()\n\n\tprivate constructor(\n\t\tpublic readonly types: {\n\t\t\t[Record in R as Record['typeName']]: RecordType<R, any>\n\t\t},\n\t\tprivate readonly options: StoreSchemaOptions<R, P>\n\t) {\n\t\tfor (const m of options.migrations ?? []) {\n\t\t\tassert(!this.migrations[m.sequenceId], `Duplicate migration sequenceId ${m.sequenceId}`)\n\t\t\tvalidateMigrations(m)\n\t\t\tthis.migrations[m.sequenceId] = m\n\t\t}\n\t\tconst allMigrations = Object.values(this.migrations).flatMap((m) => m.sequence)\n\t\tthis.sortedMigrations = sortMigrations(allMigrations)\n\n\t\tfor (const migration of this.sortedMigrations) {\n\t\t\tif (!migration.dependsOn?.length) continue\n\t\t\tfor (const dep of migration.dependsOn) {\n\t\t\t\tconst depMigration = allMigrations.find((m) => m.id === dep)\n\t\t\t\tassert(depMigration, `Migration '${migration.id}' depends on missing migration '${dep}'`)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validates a record using its corresponding RecordType validator.\n\t *\n\t * This method ensures that records conform to their type definitions before\n\t * being stored. If validation fails and an onValidationFailure handler is\n\t * provided, it will be called to potentially recover from the error.\n\t *\n\t * @param store - The store instance where validation is occurring\n\t * @param record - The record to validate\n\t * @param phase - The lifecycle phase where validation is happening\n\t * @param recordBefore - The previous version of the record (for updates)\n\t * @returns The validated record, potentially modified by validation failure handler\n\t *\n\t * @example\n\t * ```ts\n\t * try {\n\t * const validatedBook = schema.validateRecord(\n\t * store,\n\t * { id: 'book:1', typeName: 'book', title: '', author: 'Jane Doe' },\n\t * 'createRecord',\n\t * null\n\t * )\n\t * } catch (error) {\n\t * console.error('Record validation failed:', error)\n\t * }\n\t * ```\n\t *\n\t * @public\n\t */\n\tvalidateRecord(\n\t\tstore: Store<R>,\n\t\trecord: R,\n\t\tphase: 'initialize' | 'createRecord' | 'updateRecord' | 'tests',\n\t\trecordBefore: R | null\n\t): R {\n\t\ttry {\n\t\t\tconst recordType = getOwnProperty(this.types, record.typeName)\n\t\t\tif (!recordType) {\n\t\t\t\tthrow new Error(`Missing definition for record type ${record.typeName}`)\n\t\t\t}\n\t\t\treturn recordType.validate(record, recordBefore ?? undefined)\n\t\t} catch (error: unknown) {\n\t\t\tif (this.options.onValidationFailure) {\n\t\t\t\treturn this.options.onValidationFailure({\n\t\t\t\t\tstore,\n\t\t\t\t\trecord,\n\t\t\t\t\tphase,\n\t\t\t\t\trecordBefore,\n\t\t\t\t\terror,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Gets all migrations that need to be applied to upgrade from a persisted schema\n\t * to the current schema version.\n\t *\n\t * This method compares the persisted schema with the current schema and determines\n\t * which migrations need to be applied to bring the data up to date. It handles\n\t * both regular migrations and retroactive migrations, and caches results for\n\t * performance.\n\t *\n\t * @param persistedSchema - The schema version that was previously persisted\n\t * @returns A Result containing the list of migrations to apply, or an error message\n\t *\n\t * @example\n\t * ```ts\n\t * const persistedSchema = {\n\t * schemaVersion: 2,\n\t * sequences: { 'com.tldraw.book': 1, 'com.tldraw.author': 0 }\n\t * }\n\t *\n\t * const migrationsResult = schema.getMigrationsSince(persistedSchema)\n\t * if (migrationsResult.ok) {\n\t * console.log('Migrations to apply:', migrationsResult.value.length)\n\t * // Apply each migration to bring data up to date\n\t * }\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic getMigrationsSince(persistedSchema: SerializedSchema): Result<Migration[], string> {\n\t\t// Check cache first\n\t\tconst cached = this.migrationCache.get(persistedSchema)\n\t\tif (cached) {\n\t\t\treturn cached\n\t\t}\n\n\t\tconst upgradeResult = upgradeSchema(persistedSchema)\n\t\tif (!upgradeResult.ok) {\n\t\t\t// Cache the error result\n\t\t\tthis.migrationCache.set(persistedSchema, upgradeResult)\n\t\t\treturn upgradeResult\n\t\t}\n\t\tconst schema = upgradeResult.value\n\t\tconst sequenceIdsToInclude = new Set(\n\t\t\t// start with any shared sequences\n\t\t\tObject.keys(schema.sequences).filter((sequenceId) => this.migrations[sequenceId])\n\t\t)\n\n\t\t// also include any sequences that are not in the persisted schema but are marked as postHoc\n\t\tfor (const sequenceId in this.migrations) {\n\t\t\tif (schema.sequences[sequenceId] === undefined && this.migrations[sequenceId].retroactive) {\n\t\t\t\tsequenceIdsToInclude.add(sequenceId)\n\t\t\t}\n\t\t}\n\n\t\tif (sequenceIdsToInclude.size === 0) {\n\t\t\tconst result = Result.ok([])\n\t\t\t// Cache the empty result\n\t\t\tthis.migrationCache.set(persistedSchema, result)\n\t\t\treturn result\n\t\t}\n\n\t\tconst allMigrationsToInclude = new Set<MigrationId>()\n\t\tfor (const sequenceId of sequenceIdsToInclude) {\n\t\t\tconst theirVersion = schema.sequences[sequenceId]\n\t\t\tif (\n\t\t\t\t(typeof theirVersion !== 'number' && this.migrations[sequenceId].retroactive) ||\n\t\t\t\ttheirVersion === 0\n\t\t\t) {\n\t\t\t\tfor (const migration of this.migrations[sequenceId].sequence) {\n\t\t\t\t\tallMigrationsToInclude.add(migration.id)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst theirVersionId = `${sequenceId}/${theirVersion}`\n\t\t\tconst idx = this.migrations[sequenceId].sequence.findIndex((m) => m.id === theirVersionId)\n\t\t\t// todo: better error handling\n\t\t\tif (idx === -1) {\n\t\t\t\tconst result = Result.err('Incompatible schema?')\n\t\t\t\t// Cache the error result\n\t\t\t\tthis.migrationCache.set(persistedSchema, result)\n\t\t\t\treturn result\n\t\t\t}\n\t\t\tfor (const migration of this.migrations[sequenceId].sequence.slice(idx + 1)) {\n\t\t\t\tallMigrationsToInclude.add(migration.id)\n\t\t\t}\n\t\t}\n\n\t\t// collect any migrations\n\t\tconst result = Result.ok(\n\t\t\tthis.sortedMigrations.filter(({ id }) => allMigrationsToInclude.has(id))\n\t\t)\n\t\t// Cache the result\n\t\tthis.migrationCache.set(persistedSchema, result)\n\t\treturn result\n\t}\n\n\t/**\n\t * Migrates a single persisted record to match the current schema version.\n\t *\n\t * This method applies the necessary migrations to transform a record from an\n\t * older (or newer) schema version to the current version. It supports both\n\t * forward ('up') and backward ('down') migrations.\n\t *\n\t * @param record - The record to migrate\n\t * @param persistedSchema - The schema version the record was persisted with\n\t * @param direction - Direction to migrate ('up' for newer, 'down' for older)\n\t * @returns A MigrationResult containing the migrated record or an error\n\t *\n\t * @example\n\t * ```ts\n\t * const oldRecord = { id: 'book:1', typeName: 'book', title: 'Old Title', publishDate: '2020-01-01' }\n\t * const oldSchema = { schemaVersion: 2, sequences: { 'com.tldraw.book': 1 } }\n\t *\n\t * const result = schema.migratePersistedRecord(oldRecord, oldSchema, 'up')\n\t * if (result.type === 'success') {\n\t * console.log('Migrated record:', result.value)\n\t * // Record now has publishedYear instead of publishDate\n\t * } else {\n\t * console.error('Migration failed:', result.reason)\n\t * }\n\t * ```\n\t *\n\t * @public\n\t */\n\tmigratePersistedRecord(\n\t\trecord: R,\n\t\tpersistedSchema: SerializedSchema,\n\t\tdirection: 'up' | 'down' = 'up'\n\t): MigrationResult<R> {\n\t\tconst migrations = this.getMigrationsSince(persistedSchema)\n\t\tif (!migrations.ok) {\n\t\t\t// TODO: better error\n\t\t\tconsole.error('Error migrating record', migrations.error)\n\t\t\treturn { type: 'error', reason: MigrationFailureReason.MigrationError }\n\t\t}\n\t\tlet migrationsToApply = migrations.value\n\t\tif (migrationsToApply.length === 0) {\n\t\t\treturn { type: 'success', value: record }\n\t\t}\n\n\t\tif (!migrationsToApply.every((m) => m.scope === 'record')) {\n\t\t\treturn {\n\t\t\t\ttype: 'error',\n\t\t\t\treason:\n\t\t\t\t\tdirection === 'down'\n\t\t\t\t\t\t? MigrationFailureReason.TargetVersionTooOld\n\t\t\t\t\t\t: MigrationFailureReason.TargetVersionTooNew,\n\t\t\t}\n\t\t}\n\n\t\tif (direction === 'down') {\n\t\t\tif (!migrationsToApply.every((m) => m.scope === 'record' && m.down)) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\treason: MigrationFailureReason.TargetVersionTooOld,\n\t\t\t\t}\n\t\t\t}\n\t\t\tmigrationsToApply = migrationsToApply.slice().reverse()\n\t\t}\n\n\t\trecord = structuredClone(record)\n\t\ttry {\n\t\t\tfor (const migration of migrationsToApply) {\n\t\t\t\tif (migration.scope === 'store') throw new Error(/* won't happen, just for TS */)\n\t\t\t\tif (migration.scope === 'storage') throw new Error(/* won't happen, just for TS */)\n\t\t\t\tconst shouldApply = migration.filter ? migration.filter(record) : true\n\t\t\t\tif (!shouldApply) continue\n\t\t\t\tconst result = migration[direction]!(record)\n\t\t\t\tif (result) {\n\t\t\t\t\trecord = structuredClone(result) as any\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error('Error migrating record', e)\n\t\t\treturn { type: 'error', reason: MigrationFailureReason.MigrationError }\n\t\t}\n\n\t\treturn { type: 'success', value: record }\n\t}\n\n\tmigrateStorage(storage: SynchronousStorage<R>) {\n\t\tconst schema = storage.getSchema()\n\t\tassert(schema, 'Schema is missing.')\n\n\t\tconst migrations = this.getMigrationsSince(schema)\n\t\tif (!migrations.ok) {\n\t\t\tconsole.error('Error migrating store', migrations.error)\n\t\t\tthrow new Error(migrations.error)\n\t\t}\n\t\tconst migrationsToApply = migrations.value\n\t\tif (migrationsToApply.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tstorage.setSchema(this.serialize())\n\n\t\tfor (const migration of migrationsToApply) {\n\t\t\tif (migration.scope === 'record') {\n\t\t\t\t// Collect updates during iteration, then apply them after.\n\t\t\t\t// This avoids issues with live iterators (e.g., SQLite) where updating\n\t\t\t\t// records during iteration can cause them to be visited multiple times.\n\t\t\t\tconst updates: [string, R][] = []\n\t\t\t\tfor (const [id, state] of storage.entries()) {\n\t\t\t\t\tconst shouldApply = migration.filter ? migration.filter(state) : true\n\t\t\t\t\tif (!shouldApply) continue\n\t\t\t\t\tconst record = structuredClone(state)\n\t\t\t\t\tconst result = migration.up!(record as any) ?? record\n\t\t\t\t\tif (!isEqual(result, state)) {\n\t\t\t\t\t\tupdates.push([id, result as R])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const [id, record] of updates) {\n\t\t\t\t\tstorage.set(id, record)\n\t\t\t\t}\n\t\t\t} else if (migration.scope === 'store') {\n\t\t\t\t// legacy\n\t\t\t\tconst prevStore = Object.fromEntries(storage.entries())\n\t\t\t\tlet nextStore = structuredClone(prevStore)\n\t\t\t\tnextStore = (migration.up!(nextStore) as any) ?? nextStore\n\t\t\t\tfor (const [id, state] of Object.entries(nextStore)) {\n\t\t\t\t\tif (!state) continue // these will be deleted in the next loop\n\t\t\t\t\tif (!isEqual(state, prevStore[id])) {\n\t\t\t\t\t\tstorage.set(id, state)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const id of Object.keys(prevStore)) {\n\t\t\t\t\tif (!nextStore[id]) {\n\t\t\t\t\t\tstorage.delete(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (migration.scope === 'storage') {\n\t\t\t\tmigration.up!(storage)\n\t\t\t} else {\n\t\t\t\texhaustiveSwitchError(migration)\n\t\t\t}\n\t\t}\n\t\t// Clean up by filtering out any non-document records.\n\t\t// This is mainly legacy support for extremely early days tldraw.\n\t\tfor (const [id, state] of storage.entries()) {\n\t\t\tif (this.getType(state.typeName).scope !== 'document') {\n\t\t\t\tstorage.delete(id)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Migrates an entire store snapshot to match the current schema version.\n\t *\n\t * This method applies all necessary migrations to bring a persisted store\n\t * snapshot up to the current schema version. It handles both record-level\n\t * and store-level migrations, and can optionally mutate the input store\n\t * for performance.\n\t *\n\t * @param snapshot - The store snapshot containing data and schema information\n\t * @param opts - Options controlling migration behavior\n\t * - mutateInputStore - Whether to modify the input store directly (default: false)\n\t * @returns A MigrationResult containing the migrated store or an error\n\t *\n\t * @example\n\t * ```ts\n\t * const snapshot = {\n\t * schema: { schemaVersion: 2, sequences: { 'com.tldraw.book': 1 } },\n\t * store: {\n\t * 'book:1': { id: 'book:1', typeName: 'book', title: 'Old Book', publishDate: '2020-01-01' }\n\t * }\n\t * }\n\t *\n\t * const result = schema.migrateStoreSnapshot(snapshot)\n\t * if (result.type === 'success') {\n\t * console.log('Migrated store:', result.value)\n\t * // All records are now at current schema version\n\t * }\n\t * ```\n\t *\n\t * @public\n\t */\n\tmigrateStoreSnapshot(\n\t\tsnapshot: StoreSnapshot<R>,\n\t\topts?: { mutateInputStore?: boolean }\n\t): MigrationResult<SerializedStore<R>> {\n\t\tconst migrations = this.getMigrationsSince(snapshot.schema)\n\t\tif (!migrations.ok) {\n\t\t\t// TODO: better error\n\t\t\tconsole.error('Error migrating store', migrations.error)\n\t\t\treturn { type: 'error', reason: MigrationFailureReason.MigrationError }\n\t\t}\n\t\tconst migrationsToApply = migrations.value\n\t\tif (migrationsToApply.length === 0) {\n\t\t\treturn { type: 'success', value: snapshot.store }\n\t\t}\n\t\tconst store = Object.assign(\n\t\t\tnew Map<string, R>(objectMapEntries(snapshot.store).map(devFreeze)),\n\t\t\t{\n\t\t\t\tgetSchema: () => snapshot.schema,\n\t\t\t\tsetSchema: (_: SerializedSchema) => {},\n\t\t\t}\n\t\t)\n\t\ttry {\n\t\t\tthis.migrateStorage(store)\n\t\t\tif (opts?.mutateInputStore) {\n\t\t\t\tfor (const [id, record] of store.entries()) {\n\t\t\t\t\tsnapshot.store[id as keyof typeof snapshot.store] = record\n\t\t\t\t}\n\t\t\t\tfor (const id of Object.keys(snapshot.store)) {\n\t\t\t\t\tif (!store.has(id)) {\n\t\t\t\t\t\tdelete snapshot.store[id as keyof typeof snapshot.store]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { type: 'success', value: snapshot.store }\n\t\t\t} else {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'success',\n\t\t\t\t\tvalue: Object.fromEntries(store.entries()) as SerializedStore<R>,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error('Error migrating store', e)\n\t\t\treturn { type: 'error', reason: MigrationFailureReason.MigrationError }\n\t\t}\n\t}\n\n\t/**\n\t * Creates an integrity checker function for the given store.\n\t *\n\t * This method calls the createIntegrityChecker option if provided, allowing\n\t * custom integrity checking logic to be set up for the store. The integrity\n\t * checker is used to validate store consistency and catch data corruption.\n\t *\n\t * @param store - The store instance to create an integrity checker for\n\t * @returns An integrity checker function, or undefined if none is configured\n\t *\n\t * @internal\n\t */\n\tcreateIntegrityChecker(store: Store<R, P>): (() => void) | undefined {\n\t\treturn this.options.createIntegrityChecker?.(store) ?? undefined\n\t}\n\n\t/**\n\t * Serializes the current schema to a SerializedSchemaV2 format.\n\t *\n\t * This method creates a serialized representation of the current schema,\n\t * capturing the latest version number for each migration sequence.\n\t * The result can be persisted and later used to determine what migrations\n\t * need to be applied when loading data.\n\t *\n\t * @returns A SerializedSchemaV2 object representing the current schema state\n\t *\n\t * @example\n\t * ```ts\n\t * const serialized = schema.serialize()\n\t * console.log(serialized)\n\t * // {\n\t * // schemaVersion: 2,\n\t * // sequences: {\n\t * // 'com.tldraw.book': 3,\n\t * // 'com.tldraw.author': 2\n\t * // }\n\t * // }\n\t *\n\t * // Store this with your data for future migrations\n\t * localStorage.setItem('schema', JSON.stringify(serialized))\n\t * ```\n\t *\n\t * @public\n\t */\n\tserialize(): SerializedSchemaV2 {\n\t\treturn {\n\t\t\tschemaVersion: 2,\n\t\t\tsequences: Object.fromEntries(\n\t\t\t\tObject.values(this.migrations).map(({ sequenceId, sequence }) => [\n\t\t\t\t\tsequenceId,\n\t\t\t\t\tsequence.length ? parseMigrationId(sequence.at(-1)!.id).version : 0,\n\t\t\t\t])\n\t\t\t),\n\t\t}\n\t}\n\n\t/**\n\t * Serializes a schema representing the earliest possible version.\n\t *\n\t * This method creates a serialized schema where all migration sequences\n\t * are set to version 0, representing the state before any migrations\n\t * have been applied. This is used in specific legacy scenarios.\n\t *\n\t * @returns A SerializedSchema with all sequences set to version 0\n\t *\n\t * @deprecated This is only here for legacy reasons, don't use it unless you have david's blessing!\n\t * @internal\n\t */\n\tserializeEarliestVersion(): SerializedSchema {\n\t\treturn {\n\t\t\tschemaVersion: 2,\n\t\t\tsequences: Object.fromEntries(\n\t\t\t\tObject.values(this.migrations).map(({ sequenceId }) => [sequenceId, 0])\n\t\t\t),\n\t\t}\n\t}\n\n\t/**\n\t * Gets the RecordType definition for a given type name.\n\t *\n\t * This method retrieves the RecordType associated with the specified\n\t * type name, which contains the record's validation, creation, and\n\t * other behavioral logic.\n\t *\n\t * @param typeName - The name of the record type to retrieve\n\t * @returns The RecordType definition for the specified type\n\t *\n\t * @throws Will throw an error if the record type does not exist\n\t *\n\t * @internal\n\t */\n\tgetType(typeName: string) {\n\t\tconst type = getOwnProperty(this.types, typeName)\n\t\tassert(type, 'record type does not exist')\n\t\treturn type\n\t}\n}\n"],
5
- "mappings": "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",
6
- "names": ["result"]
4
+ "sourcesContent": ["import {\n\tassert,\n\texhaustiveSwitchError,\n\tgetOwnProperty,\n\tisEqual,\n\tobjectMapEntries,\n\tResult,\n\tstructuredClone,\n} from '@tldraw/utils'\nimport { UnknownRecord } from './BaseRecord'\nimport { devFreeze } from './devFreeze'\nimport {\n\tMigration,\n\tMigrationFailureReason,\n\tMigrationId,\n\tMigrationResult,\n\tMigrationSequence,\n\tparseMigrationId,\n\tsortMigrations,\n\tSynchronousStorage,\n\tvalidateMigrations,\n} from './migrate'\nimport { RecordType } from './RecordType'\nimport { SerializedStore, Store, StoreSnapshot } from './Store'\n\n/**\n * Version 1 format for serialized store schema information.\n *\n * This is the legacy format used before schema version 2. Version 1 schemas\n * separate store-level versioning from record-level versioning, and support\n * subtypes for complex record types like shapes.\n *\n * @example\n * ```ts\n * const schemaV1: SerializedSchemaV1 = {\n * schemaVersion: 1,\n * storeVersion: 2,\n * recordVersions: {\n * book: { version: 3 },\n * shape: {\n * version: 2,\n * subTypeVersions: { rectangle: 1, circle: 2 },\n * subTypeKey: 'type'\n * }\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface SerializedSchemaV1 {\n\t/** Schema version is the version for this type you're looking at right now */\n\tschemaVersion: 1\n\t/**\n\t * Store version is the version for the structure of the store. e.g. higher level structure like\n\t * removing or renaming a record type.\n\t */\n\tstoreVersion: number\n\t/** Record versions are the versions for each record type. e.g. adding a new field to a record */\n\trecordVersions: Record<\n\t\tstring,\n\t\t| {\n\t\t\t\tversion: number\n\t\t }\n\t\t| {\n\t\t\t\t// subtypes are used for migrating shape and asset props\n\t\t\t\tversion: number\n\t\t\t\tsubTypeVersions: Record<string, number>\n\t\t\t\tsubTypeKey: string\n\t\t }\n\t>\n}\n\n/**\n * Version 2 format for serialized store schema information.\n *\n * This is the current format that uses a unified sequence-based approach\n * for tracking versions across all migration sequences. Each sequence ID\n * maps to the latest version number for that sequence.\n *\n * @example\n * ```ts\n * const schemaV2: SerializedSchemaV2 = {\n * schemaVersion: 2,\n * sequences: {\n * 'com.tldraw.store': 3,\n * 'com.tldraw.book': 2,\n * 'com.tldraw.shape': 4,\n * 'com.tldraw.shape.rectangle': 1\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface SerializedSchemaV2 {\n\tschemaVersion: 2\n\tsequences: {\n\t\t[sequenceId: string]: number\n\t}\n}\n\n/**\n * Union type representing all supported serialized schema formats.\n *\n * This type allows the store to handle both legacy (V1) and current (V2)\n * schema formats during deserialization and migration.\n *\n * @example\n * ```ts\n * function handleSchema(schema: SerializedSchema) {\n * if (schema.schemaVersion === 1) {\n * // Handle V1 format\n * console.log('Store version:', schema.storeVersion)\n * } else {\n * // Handle V2 format\n * console.log('Sequences:', schema.sequences)\n * }\n * }\n * ```\n *\n * @public\n */\nexport type SerializedSchema = SerializedSchemaV1 | SerializedSchemaV2\n\n/**\n * Upgrades a serialized schema from version 1 to version 2 format.\n *\n * Version 1 schemas use separate `storeVersion` and `recordVersions` fields,\n * while version 2 schemas use a unified `sequences` object with sequence IDs.\n *\n * @param schema - The serialized schema to upgrade\n * @returns A Result containing the upgraded schema or an error message\n *\n * @example\n * ```ts\n * const v1Schema = {\n * schemaVersion: 1,\n * storeVersion: 1,\n * recordVersions: {\n * book: { version: 2 },\n * author: { version: 1, subTypeVersions: { fiction: 1 }, subTypeKey: 'genre' }\n * }\n * }\n *\n * const result = upgradeSchema(v1Schema)\n * if (result.ok) {\n * console.log(result.value.sequences)\n * // { 'com.tldraw.store': 1, 'com.tldraw.book': 2, 'com.tldraw.author': 1, 'com.tldraw.author.fiction': 1 }\n * }\n * ```\n *\n * @public\n */\nexport function upgradeSchema(schema: SerializedSchema): Result<SerializedSchemaV2, string> {\n\tif (schema.schemaVersion > 2 || schema.schemaVersion < 1) return Result.err('Bad schema version')\n\tif (schema.schemaVersion === 2) return Result.ok(schema as SerializedSchemaV2)\n\tconst result: SerializedSchemaV2 = {\n\t\tschemaVersion: 2,\n\t\tsequences: {\n\t\t\t'com.tldraw.store': schema.storeVersion,\n\t\t},\n\t}\n\n\tfor (const [typeName, recordVersion] of Object.entries(schema.recordVersions)) {\n\t\tresult.sequences[`com.tldraw.${typeName}`] = recordVersion.version\n\t\tif ('subTypeKey' in recordVersion) {\n\t\t\tfor (const [subType, version] of Object.entries(recordVersion.subTypeVersions)) {\n\t\t\t\tresult.sequences[`com.tldraw.${typeName}.${subType}`] = version\n\t\t\t}\n\t\t}\n\t}\n\treturn Result.ok(result)\n}\n\n/**\n * Information about a record validation failure that occurred in the store.\n *\n * This interface provides context about validation errors, including the failed\n * record, the store state, and the operation phase where the failure occurred.\n * It's used by validation failure handlers to implement recovery strategies.\n *\n * @example\n * ```ts\n * const schema = StoreSchema.create(\n * { book: Book },\n * {\n * onValidationFailure: (failure: StoreValidationFailure<Book>) => {\n * console.error(`Validation failed during ${failure.phase}:`, failure.error)\n * console.log('Failed record:', failure.record)\n * console.log('Previous record:', failure.recordBefore)\n *\n * // Return a corrected version of the record\n * return { ...failure.record, title: failure.record.title || 'Untitled' }\n * }\n * }\n * )\n * ```\n *\n * @public\n */\nexport interface StoreValidationFailure<R extends UnknownRecord> {\n\terror: unknown\n\tstore: Store<R>\n\trecord: R\n\tphase: 'initialize' | 'createRecord' | 'updateRecord' | 'tests'\n\trecordBefore: R | null\n}\n\n/**\n * Configuration options for creating a StoreSchema.\n *\n * These options control migration behavior, validation error handling,\n * and integrity checking for the store schema.\n *\n * @example\n * ```ts\n * const options: StoreSchemaOptions<MyRecord, MyProps> = {\n * migrations: [bookMigrations, authorMigrations],\n * onValidationFailure: (failure) => {\n * // Log the error and return a corrected record\n * console.error('Validation failed:', failure.error)\n * return sanitizeRecord(failure.record)\n * },\n * createIntegrityChecker: (store) => {\n * // Set up integrity checking logic\n * return setupIntegrityChecks(store)\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface StoreSchemaOptions<R extends UnknownRecord, P> {\n\tmigrations?: MigrationSequence[]\n\t/** @public */\n\tonValidationFailure?(data: StoreValidationFailure<R>): R\n\t/** @internal */\n\tcreateIntegrityChecker?(store: Store<R, P>): void\n}\n\n/**\n * Manages the schema definition, validation, and migration system for a Store.\n *\n * StoreSchema coordinates record types, handles data migrations between schema\n * versions, validates records, and provides the foundational structure for\n * reactive stores. It acts as the central authority for data consistency\n * and evolution within the store system.\n *\n * @example\n * ```ts\n * // Define record types\n * const Book = createRecordType<Book>('book', { scope: 'document' })\n * const Author = createRecordType<Author>('author', { scope: 'document' })\n *\n * // Create schema with migrations\n * const schema = StoreSchema.create(\n * { book: Book, author: Author },\n * {\n * migrations: [bookMigrations, authorMigrations],\n * onValidationFailure: (failure) => {\n * console.warn('Validation failed, using default:', failure.error)\n * return failure.record // or return a corrected version\n * }\n * }\n * )\n *\n * // Use with store\n * const store = new Store({ schema })\n * ```\n *\n * @public\n */\nexport class StoreSchema<R extends UnknownRecord, P = unknown> {\n\t/**\n\t * Creates a new StoreSchema with the given record types and options.\n\t *\n\t * This static factory method is the recommended way to create a StoreSchema.\n\t * It ensures type safety while providing a clean API for schema definition.\n\t *\n\t * @param types - Object mapping type names to their RecordType definitions\n\t * @param options - Optional configuration for migrations, validation, and integrity checking\n\t * @returns A new StoreSchema instance\n\t *\n\t * @example\n\t * ```ts\n\t * const Book = createRecordType<Book>('book', { scope: 'document' })\n\t * const Author = createRecordType<Author>('author', { scope: 'document' })\n\t *\n\t * const schema = StoreSchema.create(\n\t * {\n\t * book: Book,\n\t * author: Author\n\t * },\n\t * {\n\t * migrations: [bookMigrations],\n\t * onValidationFailure: (failure) => failure.record\n\t * }\n\t * )\n\t * ```\n\t *\n\t * @public\n\t */\n\tstatic create<R extends UnknownRecord, P = unknown>(\n\t\t// HACK: making this param work with RecordType is an enormous pain\n\t\t// let's just settle for making sure each typeName has a corresponding RecordType\n\t\t// and accept that this function won't be able to infer the record type from it's arguments\n\t\ttypes: { [TypeName in R['typeName']]: { createId: any } },\n\t\toptions?: StoreSchemaOptions<R, P>\n\t): StoreSchema<R, P> {\n\t\treturn new StoreSchema<R, P>(types as any, options ?? {})\n\t}\n\n\treadonly migrations: Record<string, MigrationSequence> = {}\n\treadonly sortedMigrations: readonly Migration[]\n\tprivate readonly migrationCache = new WeakMap<SerializedSchema, Result<Migration[], string>>()\n\n\tprivate constructor(\n\t\tpublic readonly types: {\n\t\t\t[Record in R as Record['typeName']]: RecordType<R, any>\n\t\t},\n\t\tprivate readonly options: StoreSchemaOptions<R, P>\n\t) {\n\t\tfor (const m of options.migrations ?? []) {\n\t\t\tassert(!this.migrations[m.sequenceId], `Duplicate migration sequenceId ${m.sequenceId}`)\n\t\t\tvalidateMigrations(m)\n\t\t\tthis.migrations[m.sequenceId] = m\n\t\t}\n\t\tconst allMigrations = Object.values(this.migrations).flatMap((m) => m.sequence)\n\t\tthis.sortedMigrations = sortMigrations(allMigrations)\n\n\t\tfor (const migration of this.sortedMigrations) {\n\t\t\tif (!migration.dependsOn?.length) continue\n\t\t\tfor (const dep of migration.dependsOn) {\n\t\t\t\tconst depMigration = allMigrations.find((m) => m.id === dep)\n\t\t\t\tassert(depMigration, `Migration '${migration.id}' depends on missing migration '${dep}'`)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validates a record using its corresponding RecordType validator.\n\t *\n\t * This method ensures that records conform to their type definitions before\n\t * being stored. If validation fails and an onValidationFailure handler is\n\t * provided, it will be called to potentially recover from the error.\n\t *\n\t * @param store - The store instance where validation is occurring\n\t * @param record - The record to validate\n\t * @param phase - The lifecycle phase where validation is happening\n\t * @param recordBefore - The previous version of the record (for updates)\n\t * @returns The validated record, potentially modified by validation failure handler\n\t *\n\t * @example\n\t * ```ts\n\t * try {\n\t * const validatedBook = schema.validateRecord(\n\t * store,\n\t * { id: 'book:1', typeName: 'book', title: '', author: 'Jane Doe' },\n\t * 'createRecord',\n\t * null\n\t * )\n\t * } catch (error) {\n\t * console.error('Record validation failed:', error)\n\t * }\n\t * ```\n\t *\n\t * @public\n\t */\n\tvalidateRecord(\n\t\tstore: Store<R>,\n\t\trecord: R,\n\t\tphase: 'initialize' | 'createRecord' | 'updateRecord' | 'tests',\n\t\trecordBefore: R | null\n\t): R {\n\t\ttry {\n\t\t\tconst recordType = getOwnProperty(this.types, record.typeName)\n\t\t\tif (!recordType) {\n\t\t\t\tthrow new Error(`Missing definition for record type ${record.typeName}`)\n\t\t\t}\n\t\t\treturn recordType.validate(record, recordBefore ?? undefined)\n\t\t} catch (error: unknown) {\n\t\t\tif (this.options.onValidationFailure) {\n\t\t\t\treturn this.options.onValidationFailure({\n\t\t\t\t\tstore,\n\t\t\t\t\trecord,\n\t\t\t\t\tphase,\n\t\t\t\t\trecordBefore,\n\t\t\t\t\terror,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Gets all migrations that need to be applied to upgrade from a persisted schema\n\t * to the current schema version.\n\t *\n\t * This method compares the persisted schema with the current schema and determines\n\t * which migrations need to be applied to bring the data up to date. It handles\n\t * both regular migrations and retroactive migrations, and caches results for\n\t * performance.\n\t *\n\t * @param persistedSchema - The schema version that was previously persisted\n\t * @returns A Result containing the list of migrations to apply, or an error message\n\t *\n\t * @example\n\t * ```ts\n\t * const persistedSchema = {\n\t * schemaVersion: 2,\n\t * sequences: { 'com.tldraw.book': 1, 'com.tldraw.author': 0 }\n\t * }\n\t *\n\t * const migrationsResult = schema.getMigrationsSince(persistedSchema)\n\t * if (migrationsResult.ok) {\n\t * console.log('Migrations to apply:', migrationsResult.value.length)\n\t * // Apply each migration to bring data up to date\n\t * }\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic getMigrationsSince(persistedSchema: SerializedSchema): Result<Migration[], string> {\n\t\t// Every result \u2014 success, empty, or error \u2014 is cached, so cache once around the whole\n\t\t// computation rather than at each of its exit points.\n\t\tconst cached = this.migrationCache.get(persistedSchema)\n\t\tif (cached) {\n\t\t\treturn cached\n\t\t}\n\n\t\tconst result = this.computeMigrationsSince(persistedSchema)\n\t\tthis.migrationCache.set(persistedSchema, result)\n\t\treturn result\n\t}\n\n\tprivate computeMigrationsSince(persistedSchema: SerializedSchema): Result<Migration[], string> {\n\t\tconst upgradeResult = upgradeSchema(persistedSchema)\n\t\tif (!upgradeResult.ok) {\n\t\t\treturn upgradeResult\n\t\t}\n\t\tconst schema = upgradeResult.value\n\t\tconst sequenceIdsToInclude = new Set(\n\t\t\t// start with any shared sequences\n\t\t\tObject.keys(schema.sequences).filter((sequenceId) => this.migrations[sequenceId])\n\t\t)\n\n\t\t// also include any sequences that are not in the persisted schema but are marked as postHoc\n\t\tfor (const sequenceId in this.migrations) {\n\t\t\tif (schema.sequences[sequenceId] === undefined && this.migrations[sequenceId].retroactive) {\n\t\t\t\tsequenceIdsToInclude.add(sequenceId)\n\t\t\t}\n\t\t}\n\n\t\tif (sequenceIdsToInclude.size === 0) {\n\t\t\treturn Result.ok([])\n\t\t}\n\n\t\tconst allMigrationsToInclude = new Set<MigrationId>()\n\t\tfor (const sequenceId of sequenceIdsToInclude) {\n\t\t\tconst theirVersion = schema.sequences[sequenceId]\n\t\t\tif (\n\t\t\t\t(typeof theirVersion !== 'number' && this.migrations[sequenceId].retroactive) ||\n\t\t\t\ttheirVersion === 0\n\t\t\t) {\n\t\t\t\tfor (const migration of this.migrations[sequenceId].sequence) {\n\t\t\t\t\tallMigrationsToInclude.add(migration.id)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst theirVersionId = `${sequenceId}/${theirVersion}`\n\t\t\tconst idx = this.migrations[sequenceId].sequence.findIndex((m) => m.id === theirVersionId)\n\t\t\t// todo: better error handling\n\t\t\tif (idx === -1) {\n\t\t\t\treturn Result.err('Incompatible schema?')\n\t\t\t}\n\t\t\tfor (const migration of this.migrations[sequenceId].sequence.slice(idx + 1)) {\n\t\t\t\tallMigrationsToInclude.add(migration.id)\n\t\t\t}\n\t\t}\n\n\t\t// collect any migrations\n\t\treturn Result.ok(this.sortedMigrations.filter(({ id }) => allMigrationsToInclude.has(id)))\n\t}\n\n\t/**\n\t * Migrates a single persisted record to match the current schema version.\n\t *\n\t * This method applies the necessary migrations to transform a record from an\n\t * older (or newer) schema version to the current version. It supports both\n\t * forward ('up') and backward ('down') migrations.\n\t *\n\t * @param record - The record to migrate\n\t * @param persistedSchema - The schema version the record was persisted with\n\t * @param direction - Direction to migrate ('up' for newer, 'down' for older)\n\t * @returns A MigrationResult containing the migrated record or an error\n\t *\n\t * @example\n\t * ```ts\n\t * const oldRecord = { id: 'book:1', typeName: 'book', title: 'Old Title', publishDate: '2020-01-01' }\n\t * const oldSchema = { schemaVersion: 2, sequences: { 'com.tldraw.book': 1 } }\n\t *\n\t * const result = schema.migratePersistedRecord(oldRecord, oldSchema, 'up')\n\t * if (result.type === 'success') {\n\t * console.log('Migrated record:', result.value)\n\t * // Record now has publishedYear instead of publishDate\n\t * } else {\n\t * console.error('Migration failed:', result.reason)\n\t * }\n\t * ```\n\t *\n\t * @public\n\t */\n\tmigratePersistedRecord(\n\t\trecord: R,\n\t\tpersistedSchema: SerializedSchema,\n\t\tdirection: 'up' | 'down' = 'up'\n\t): MigrationResult<R> {\n\t\tconst migrations = this.getMigrationsSince(persistedSchema)\n\t\tif (!migrations.ok) {\n\t\t\t// TODO: better error\n\t\t\tconsole.error('Error migrating record', migrations.error)\n\t\t\treturn { type: 'error', reason: MigrationFailureReason.MigrationError }\n\t\t}\n\t\tlet migrationsToApply = migrations.value\n\t\tif (migrationsToApply.length === 0) {\n\t\t\treturn { type: 'success', value: record }\n\t\t}\n\n\t\tif (!migrationsToApply.every((m) => m.scope === 'record')) {\n\t\t\treturn {\n\t\t\t\ttype: 'error',\n\t\t\t\treason:\n\t\t\t\t\tdirection === 'down'\n\t\t\t\t\t\t? MigrationFailureReason.TargetVersionTooOld\n\t\t\t\t\t\t: MigrationFailureReason.TargetVersionTooNew,\n\t\t\t}\n\t\t}\n\n\t\tif (direction === 'down') {\n\t\t\tif (!migrationsToApply.every((m) => m.scope === 'record' && m.down)) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\treason: MigrationFailureReason.TargetVersionTooOld,\n\t\t\t\t}\n\t\t\t}\n\t\t\tmigrationsToApply = migrationsToApply.slice().reverse()\n\t\t}\n\n\t\trecord = structuredClone(record)\n\t\ttry {\n\t\t\tfor (const migration of migrationsToApply) {\n\t\t\t\tif (migration.scope === 'store') throw new Error(/* won't happen, just for TS */)\n\t\t\t\tif (migration.scope === 'storage') throw new Error(/* won't happen, just for TS */)\n\t\t\t\tconst shouldApply = migration.filter ? migration.filter(record) : true\n\t\t\t\tif (!shouldApply) continue\n\t\t\t\tconst result = migration[direction]!(record)\n\t\t\t\tif (result) {\n\t\t\t\t\trecord = structuredClone(result) as any\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error('Error migrating record', e)\n\t\t\treturn { type: 'error', reason: MigrationFailureReason.MigrationError }\n\t\t}\n\n\t\treturn { type: 'success', value: record }\n\t}\n\n\tmigrateStorage(storage: SynchronousStorage<R>) {\n\t\tconst schema = storage.getSchema()\n\t\tassert(schema, 'Schema is missing.')\n\n\t\tconst migrations = this.getMigrationsSince(schema)\n\t\tif (!migrations.ok) {\n\t\t\tconsole.error('Error migrating store', migrations.error)\n\t\t\tthrow new Error(migrations.error)\n\t\t}\n\t\tconst migrationsToApply = migrations.value\n\t\tif (migrationsToApply.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tstorage.setSchema(this.serialize())\n\n\t\tfor (const migration of migrationsToApply) {\n\t\t\tif (migration.scope === 'record') {\n\t\t\t\t// Collect updates during iteration, then apply them after.\n\t\t\t\t// This avoids issues with live iterators (e.g., SQLite) where updating\n\t\t\t\t// records during iteration can cause them to be visited multiple times.\n\t\t\t\tconst updates: [string, R][] = []\n\t\t\t\tfor (const [id, state] of storage.entries()) {\n\t\t\t\t\tconst shouldApply = migration.filter ? migration.filter(state) : true\n\t\t\t\t\tif (!shouldApply) continue\n\t\t\t\t\tconst record = structuredClone(state)\n\t\t\t\t\tconst result = migration.up!(record as any) ?? record\n\t\t\t\t\tif (!isEqual(result, state)) {\n\t\t\t\t\t\tupdates.push([id, result as R])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const [id, record] of updates) {\n\t\t\t\t\tstorage.set(id, record)\n\t\t\t\t}\n\t\t\t} else if (migration.scope === 'store') {\n\t\t\t\t// legacy\n\t\t\t\tconst prevStore = Object.fromEntries(storage.entries())\n\t\t\t\tlet nextStore = structuredClone(prevStore)\n\t\t\t\tnextStore = (migration.up!(nextStore) as any) ?? nextStore\n\t\t\t\tfor (const [id, state] of Object.entries(nextStore)) {\n\t\t\t\t\tif (!state) continue // these will be deleted in the next loop\n\t\t\t\t\tif (!isEqual(state, prevStore[id])) {\n\t\t\t\t\t\tstorage.set(id, state)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const id of Object.keys(prevStore)) {\n\t\t\t\t\tif (!nextStore[id]) {\n\t\t\t\t\t\tstorage.delete(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (migration.scope === 'storage') {\n\t\t\t\tmigration.up!(storage)\n\t\t\t} else {\n\t\t\t\texhaustiveSwitchError(migration)\n\t\t\t}\n\t\t}\n\t\t// Clean up by filtering out any non-document records.\n\t\t// This is mainly legacy support for extremely early days tldraw.\n\t\tfor (const [id, state] of storage.entries()) {\n\t\t\tif (this.getType(state.typeName).scope !== 'document') {\n\t\t\t\tstorage.delete(id)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Migrates an entire store snapshot to match the current schema version.\n\t *\n\t * This method applies all necessary migrations to bring a persisted store\n\t * snapshot up to the current schema version. It handles both record-level\n\t * and store-level migrations, and can optionally mutate the input store\n\t * for performance.\n\t *\n\t * @param snapshot - The store snapshot containing data and schema information\n\t * @param opts - Options controlling migration behavior\n\t * - mutateInputStore - Whether to modify the input store directly (default: false)\n\t * @returns A MigrationResult containing the migrated store or an error\n\t *\n\t * @example\n\t * ```ts\n\t * const snapshot = {\n\t * schema: { schemaVersion: 2, sequences: { 'com.tldraw.book': 1 } },\n\t * store: {\n\t * 'book:1': { id: 'book:1', typeName: 'book', title: 'Old Book', publishDate: '2020-01-01' }\n\t * }\n\t * }\n\t *\n\t * const result = schema.migrateStoreSnapshot(snapshot)\n\t * if (result.type === 'success') {\n\t * console.log('Migrated store:', result.value)\n\t * // All records are now at current schema version\n\t * }\n\t * ```\n\t *\n\t * @public\n\t */\n\tmigrateStoreSnapshot(\n\t\tsnapshot: StoreSnapshot<R>,\n\t\topts?: { mutateInputStore?: boolean }\n\t): MigrationResult<SerializedStore<R>> {\n\t\tconst migrations = this.getMigrationsSince(snapshot.schema)\n\t\tif (!migrations.ok) {\n\t\t\t// TODO: better error\n\t\t\tconsole.error('Error migrating store', migrations.error)\n\t\t\treturn { type: 'error', reason: MigrationFailureReason.MigrationError }\n\t\t}\n\t\tconst migrationsToApply = migrations.value\n\t\tif (migrationsToApply.length === 0) {\n\t\t\treturn { type: 'success', value: snapshot.store }\n\t\t}\n\t\tconst store = Object.assign(\n\t\t\tnew Map<string, R>(objectMapEntries(snapshot.store).map(devFreeze)),\n\t\t\t{\n\t\t\t\tgetSchema: () => snapshot.schema,\n\t\t\t\tsetSchema: (_: SerializedSchema) => {},\n\t\t\t}\n\t\t)\n\t\ttry {\n\t\t\tthis.migrateStorage(store)\n\t\t\tif (opts?.mutateInputStore) {\n\t\t\t\tfor (const [id, record] of store.entries()) {\n\t\t\t\t\tsnapshot.store[id as keyof typeof snapshot.store] = record\n\t\t\t\t}\n\t\t\t\tfor (const id of Object.keys(snapshot.store)) {\n\t\t\t\t\tif (!store.has(id)) {\n\t\t\t\t\t\tdelete snapshot.store[id as keyof typeof snapshot.store]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { type: 'success', value: snapshot.store }\n\t\t\t} else {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'success',\n\t\t\t\t\tvalue: Object.fromEntries(store.entries()) as SerializedStore<R>,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error('Error migrating store', e)\n\t\t\treturn { type: 'error', reason: MigrationFailureReason.MigrationError }\n\t\t}\n\t}\n\n\t/**\n\t * Creates an integrity checker function for the given store.\n\t *\n\t * This method calls the createIntegrityChecker option if provided, allowing\n\t * custom integrity checking logic to be set up for the store. The integrity\n\t * checker is used to validate store consistency and catch data corruption.\n\t *\n\t * @param store - The store instance to create an integrity checker for\n\t * @returns An integrity checker function, or undefined if none is configured\n\t *\n\t * @internal\n\t */\n\tcreateIntegrityChecker(store: Store<R, P>): (() => void) | undefined {\n\t\treturn this.options.createIntegrityChecker?.(store) ?? undefined\n\t}\n\n\t/**\n\t * Serializes the current schema to a SerializedSchemaV2 format.\n\t *\n\t * This method creates a serialized representation of the current schema,\n\t * capturing the latest version number for each migration sequence.\n\t * The result can be persisted and later used to determine what migrations\n\t * need to be applied when loading data.\n\t *\n\t * @returns A SerializedSchemaV2 object representing the current schema state\n\t *\n\t * @example\n\t * ```ts\n\t * const serialized = schema.serialize()\n\t * console.log(serialized)\n\t * // {\n\t * // schemaVersion: 2,\n\t * // sequences: {\n\t * // 'com.tldraw.book': 3,\n\t * // 'com.tldraw.author': 2\n\t * // }\n\t * // }\n\t *\n\t * // Store this with your data for future migrations\n\t * localStorage.setItem('schema', JSON.stringify(serialized))\n\t * ```\n\t *\n\t * @public\n\t */\n\tserialize(): SerializedSchemaV2 {\n\t\treturn {\n\t\t\tschemaVersion: 2,\n\t\t\tsequences: Object.fromEntries(\n\t\t\t\tObject.values(this.migrations).map(({ sequenceId, sequence }) => [\n\t\t\t\t\tsequenceId,\n\t\t\t\t\tsequence.length ? parseMigrationId(sequence.at(-1)!.id).version : 0,\n\t\t\t\t])\n\t\t\t),\n\t\t}\n\t}\n\n\t/**\n\t * Serializes a schema representing the earliest possible version.\n\t *\n\t * This method creates a serialized schema where all migration sequences\n\t * are set to version 0, representing the state before any migrations\n\t * have been applied. This is used in specific legacy scenarios.\n\t *\n\t * @returns A SerializedSchema with all sequences set to version 0\n\t *\n\t * @deprecated This is only here for legacy reasons, don't use it unless you have david's blessing!\n\t * @internal\n\t */\n\tserializeEarliestVersion(): SerializedSchema {\n\t\treturn {\n\t\t\tschemaVersion: 2,\n\t\t\tsequences: Object.fromEntries(\n\t\t\t\tObject.values(this.migrations).map(({ sequenceId }) => [sequenceId, 0])\n\t\t\t),\n\t\t}\n\t}\n\n\t/**\n\t * Gets the RecordType definition for a given type name.\n\t *\n\t * This method retrieves the RecordType associated with the specified\n\t * type name, which contains the record's validation, creation, and\n\t * other behavioral logic.\n\t *\n\t * @param typeName - The name of the record type to retrieve\n\t * @returns The RecordType definition for the specified type\n\t *\n\t * @throws Will throw an error if the record type does not exist\n\t *\n\t * @internal\n\t */\n\tgetType(typeName: string) {\n\t\tconst type = getOwnProperty(this.types, typeName)\n\t\tassert(type, 'record type does not exist')\n\t\treturn type\n\t}\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
7
  }
@@ -1,3 +1,4 @@
1
+ import { objectMapFromEntries, objectMapValues } from "@tldraw/utils";
1
2
  import { intersectSets } from "./setUtils.mjs";
2
3
  function isQueryValueMatcher(value) {
3
4
  if (typeof value !== "object" || value === null) return false;
@@ -33,7 +34,9 @@ function objectMatchesQuery(query, object) {
33
34
  }
34
35
  function executeQuery(store, typeName, query) {
35
36
  const matcherPaths = extractMatcherPaths(query);
36
- const matchIds = Object.fromEntries(matcherPaths.map(({ path }) => [path, /* @__PURE__ */ new Set()]));
37
+ const matchIds = objectMapFromEntries(
38
+ matcherPaths.map(({ path }) => [path, /* @__PURE__ */ new Set()])
39
+ );
37
40
  for (const { path, matcher } of matcherPaths) {
38
41
  const index = store.index(typeName, path);
39
42
  if ("eq" in matcher) {
@@ -64,7 +67,7 @@ function executeQuery(store, typeName, query) {
64
67
  return /* @__PURE__ */ new Set();
65
68
  }
66
69
  }
67
- return intersectSets(Object.values(matchIds));
70
+ return intersectSets(objectMapValues(matchIds));
68
71
  }
69
72
  export {
70
73
  executeQuery,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/executeQuery.ts"],
4
- "sourcesContent": ["import { IdOf, UnknownRecord } from './BaseRecord'\nimport { intersectSets } from './setUtils'\nimport { StoreQueries } from './StoreQueries'\n\n/**\n * Defines matching criteria for query values. Supports equality, inequality, and greater-than comparisons.\n *\n * @example\n * ```ts\n * // Exact match\n * const exactMatch: QueryValueMatcher<string> = { eq: 'Science Fiction' }\n *\n * // Not equal to\n * const notMatch: QueryValueMatcher<string> = { neq: 'Romance' }\n *\n * // Greater than (numeric values only)\n * const greaterThan: QueryValueMatcher<number> = { gt: 2020 }\n * ```\n *\n * @public\n */\nexport type QueryValueMatcher<T> = { eq: T } | { neq: T } | { gt: number }\n\n/**\n * Query expression for filtering records by their property values. Maps record property names\n * to matching criteria.\n *\n * @example\n * ```ts\n * // Query for books published after 2020 that are in stock\n * const bookQuery: QueryExpression<Book> = {\n * publishedYear: { gt: 2020 },\n * inStock: { eq: true }\n * }\n *\n * // Query for books not by a specific author\n * const notByAuthor: QueryExpression<Book> = {\n * authorId: { neq: 'author:tolkien' }\n * }\n *\n * // Query with nested properties\n * const nestedQuery: QueryExpression<Book> = {\n * metadata: { sessionId: { eq: 'session:alpha' } }\n * }\n * ```\n *\n * @public\n */\n/** @public */\nexport type QueryExpression<R extends object> = {\n\t[k in keyof R & string]?: R[k] extends string | number | boolean | null | undefined\n\t\t? QueryValueMatcher<R[k]>\n\t\t: R[k] extends object\n\t\t\t? QueryExpression<R[k]>\n\t\t\t: QueryValueMatcher<R[k]>\n}\n\nfunction isQueryValueMatcher(value: unknown): value is QueryValueMatcher<unknown> {\n\tif (typeof value !== 'object' || value === null) return false\n\treturn 'eq' in value || 'neq' in value || 'gt' in value\n}\n\nfunction extractMatcherPaths(\n\tquery: QueryExpression<any>,\n\tprefix: string = ''\n): Array<{ path: string; matcher: QueryValueMatcher<any> }> {\n\tconst paths: Array<{ path: string; matcher: QueryValueMatcher<any> }> = []\n\n\tfor (const [key, value] of Object.entries(query)) {\n\t\tconst currentPath = prefix ? `${prefix}\\\\${key}` : key\n\n\t\tif (isQueryValueMatcher(value)) {\n\t\t\t// It's a direct matcher\n\t\t\tpaths.push({ path: currentPath, matcher: value })\n\t\t} else if (typeof value === 'object' && value !== null) {\n\t\t\t// It's a nested query - recurse into it\n\t\t\tpaths.push(...extractMatcherPaths(value as QueryExpression<any>, currentPath))\n\t\t}\n\t}\n\n\treturn paths\n}\n\nexport function objectMatchesQuery<T extends object>(query: QueryExpression<T>, object: T) {\n\tfor (const [key, matcher] of Object.entries(query)) {\n\t\tconst value = object[key as keyof T]\n\n\t\t// if you add matching logic here, make sure you also update executeQuery,\n\t\t// where initial data is pulled out of the indexes, since that requires different\n\t\t// matching logic\n\t\tif (isQueryValueMatcher(matcher)) {\n\t\t\tif ('eq' in matcher && value !== matcher.eq) return false\n\t\t\t// undefined values must not match neq: the indexes executeQuery reads only\n\t\t\t// track defined values, and the two matching strategies have to agree\n\t\t\tif ('neq' in matcher && (value === matcher.neq || value === undefined)) return false\n\t\t\tif ('gt' in matcher && (typeof value !== 'number' || value <= matcher.gt)) return false\n\t\t\tcontinue\n\t\t}\n\n\t\t// It's a nested query\n\t\tif (typeof value !== 'object' || value === null) return false\n\t\tif (!objectMatchesQuery(matcher as QueryExpression<any>, value as any)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n/**\n * Executes a query against the store using reactive indexes to efficiently find matching record IDs.\n * Uses the store's internal indexes for optimal performance, especially for equality matches.\n *\n * @param store - The store queries interface providing access to reactive indexes\n * @param typeName - The type name of records to query (e.g., 'book', 'author')\n * @param query - Query expression defining the matching criteria\n * @returns A Set containing the IDs of all records that match the query criteria\n *\n * @example\n * ```ts\n * // Find IDs of all books published after 2020 that are in stock\n * const bookIds = executeQuery(store, 'book', {\n * publishedYear: { gt: 2020 },\n * inStock: { eq: true }\n * })\n *\n * // Find IDs of books not by a specific author\n * const otherBookIds = executeQuery(store, 'book', {\n * authorId: { neq: 'author:tolkien' }\n * })\n *\n * // Query with nested properties\n * const nestedQueryIds = executeQuery(store, 'book', {\n * metadata: { sessionId: { eq: 'session:alpha' } }\n * })\n * ```\n *\n * @public\n */\nexport function executeQuery<R extends UnknownRecord, TypeName extends R['typeName']>(\n\tstore: StoreQueries<R>,\n\ttypeName: TypeName,\n\tquery: QueryExpression<Extract<R, { typeName: TypeName }>>\n): Set<IdOf<Extract<R, { typeName: TypeName }>>> {\n\ttype S = Extract<R, { typeName: TypeName }>\n\n\t// Extract all paths with matchers (flattens nested queries)\n\tconst matcherPaths = extractMatcherPaths(query)\n\n\t// Build a set of matching IDs for each path\n\tconst matchIds = Object.fromEntries(matcherPaths.map(({ path }) => [path, new Set<IdOf<S>>()]))\n\n\t// For each path, use the index to find matching IDs\n\tfor (const { path, matcher } of matcherPaths) {\n\t\tconst index = store.index(typeName, path as any)\n\n\t\tif ('eq' in matcher) {\n\t\t\tconst ids = index.get().get(matcher.eq)\n\t\t\tif (ids) {\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tmatchIds[path].add(id)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if ('neq' in matcher) {\n\t\t\tfor (const [value, ids] of index.get()) {\n\t\t\t\tif (value !== matcher.neq) {\n\t\t\t\t\tfor (const id of ids) {\n\t\t\t\t\t\tmatchIds[path].add(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if ('gt' in matcher) {\n\t\t\tfor (const [value, ids] of index.get()) {\n\t\t\t\tif (typeof value === 'number' && value > matcher.gt) {\n\t\t\t\t\tfor (const id of ids) {\n\t\t\t\t\t\tmatchIds[path].add(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Short-circuit if this set is empty - intersection will be empty\n\t\tif (matchIds[path].size === 0) {\n\t\t\treturn new Set()\n\t\t}\n\t}\n\n\t// Intersect all the match sets\n\treturn intersectSets(Object.values(matchIds)) as Set<IdOf<S>>\n}\n"],
5
- "mappings": "AACA,SAAS,qBAAqB;AAwD9B,SAAS,oBAAoB,OAAqD;AACjF,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,SAAO,QAAQ,SAAS,SAAS,SAAS,QAAQ;AACnD;AAEA,SAAS,oBACR,OACA,SAAiB,IAC0C;AAC3D,QAAM,QAAkE,CAAC;AAEzE,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACjD,UAAM,cAAc,SAAS,GAAG,MAAM,KAAK,GAAG,KAAK;AAEnD,QAAI,oBAAoB,KAAK,GAAG;AAE/B,YAAM,KAAK,EAAE,MAAM,aAAa,SAAS,MAAM,CAAC;AAAA,IACjD,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AAEvD,YAAM,KAAK,GAAG,oBAAoB,OAA+B,WAAW,CAAC;AAAA,IAC9E;AAAA,EACD;AAEA,SAAO;AACR;AAEO,SAAS,mBAAqC,OAA2B,QAAW;AAC1F,aAAW,CAAC,KAAK,OAAO,KAAK,OAAO,QAAQ,KAAK,GAAG;AACnD,UAAM,QAAQ,OAAO,GAAc;AAKnC,QAAI,oBAAoB,OAAO,GAAG;AACjC,UAAI,QAAQ,WAAW,UAAU,QAAQ,GAAI,QAAO;AAGpD,UAAI,SAAS,YAAY,UAAU,QAAQ,OAAO,UAAU,QAAY,QAAO;AAC/E,UAAI,QAAQ,YAAY,OAAO,UAAU,YAAY,SAAS,QAAQ,IAAK,QAAO;AAClF;AAAA,IACD;AAGA,QAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAI,CAAC,mBAAmB,SAAiC,KAAY,GAAG;AACvE,aAAO;AAAA,IACR;AAAA,EACD;AACA,SAAO;AACR;AAgCO,SAAS,aACf,OACA,UACA,OACgD;AAIhD,QAAM,eAAe,oBAAoB,KAAK;AAG9C,QAAM,WAAW,OAAO,YAAY,aAAa,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,MAAM,oBAAI,IAAa,CAAC,CAAC,CAAC;AAG9F,aAAW,EAAE,MAAM,QAAQ,KAAK,cAAc;AAC7C,UAAM,QAAQ,MAAM,MAAM,UAAU,IAAW;AAE/C,QAAI,QAAQ,SAAS;AACpB,YAAM,MAAM,MAAM,IAAI,EAAE,IAAI,QAAQ,EAAE;AACtC,UAAI,KAAK;AACR,mBAAW,MAAM,KAAK;AACrB,mBAAS,IAAI,EAAE,IAAI,EAAE;AAAA,QACtB;AAAA,MACD;AAAA,IACD,WAAW,SAAS,SAAS;AAC5B,iBAAW,CAAC,OAAO,GAAG,KAAK,MAAM,IAAI,GAAG;AACvC,YAAI,UAAU,QAAQ,KAAK;AAC1B,qBAAW,MAAM,KAAK;AACrB,qBAAS,IAAI,EAAE,IAAI,EAAE;AAAA,UACtB;AAAA,QACD;AAAA,MACD;AAAA,IACD,WAAW,QAAQ,SAAS;AAC3B,iBAAW,CAAC,OAAO,GAAG,KAAK,MAAM,IAAI,GAAG;AACvC,YAAI,OAAO,UAAU,YAAY,QAAQ,QAAQ,IAAI;AACpD,qBAAW,MAAM,KAAK;AACrB,qBAAS,IAAI,EAAE,IAAI,EAAE;AAAA,UACtB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAGA,QAAI,SAAS,IAAI,EAAE,SAAS,GAAG;AAC9B,aAAO,oBAAI,IAAI;AAAA,IAChB;AAAA,EACD;AAGA,SAAO,cAAc,OAAO,OAAO,QAAQ,CAAC;AAC7C;",
4
+ "sourcesContent": ["import { objectMapFromEntries, objectMapValues } from '@tldraw/utils'\nimport { IdOf, UnknownRecord } from './BaseRecord'\nimport { intersectSets } from './setUtils'\nimport { StoreQueries } from './StoreQueries'\n\n/**\n * Defines matching criteria for query values. Supports equality, inequality, and greater-than comparisons.\n *\n * @example\n * ```ts\n * // Exact match\n * const exactMatch: QueryValueMatcher<string> = { eq: 'Science Fiction' }\n *\n * // Not equal to\n * const notMatch: QueryValueMatcher<string> = { neq: 'Romance' }\n *\n * // Greater than (numeric values only)\n * const greaterThan: QueryValueMatcher<number> = { gt: 2020 }\n * ```\n *\n * @public\n */\nexport type QueryValueMatcher<T> = { eq: T } | { neq: T } | { gt: number }\n\n/**\n * Query expression for filtering records by their property values. Maps record property names\n * to matching criteria.\n *\n * @example\n * ```ts\n * // Query for books published after 2020 that are in stock\n * const bookQuery: QueryExpression<Book> = {\n * publishedYear: { gt: 2020 },\n * inStock: { eq: true }\n * }\n *\n * // Query for books not by a specific author\n * const notByAuthor: QueryExpression<Book> = {\n * authorId: { neq: 'author:tolkien' }\n * }\n *\n * // Query with nested properties\n * const nestedQuery: QueryExpression<Book> = {\n * metadata: { sessionId: { eq: 'session:alpha' } }\n * }\n * ```\n *\n * @public\n */\n/** @public */\nexport type QueryExpression<R extends object> = {\n\t[k in keyof R & string]?: R[k] extends string | number | boolean | null | undefined\n\t\t? QueryValueMatcher<R[k]>\n\t\t: R[k] extends object\n\t\t\t? QueryExpression<R[k]>\n\t\t\t: QueryValueMatcher<R[k]>\n}\n\nfunction isQueryValueMatcher(value: unknown): value is QueryValueMatcher<unknown> {\n\tif (typeof value !== 'object' || value === null) return false\n\treturn 'eq' in value || 'neq' in value || 'gt' in value\n}\n\nfunction extractMatcherPaths(\n\tquery: QueryExpression<any>,\n\tprefix: string = ''\n): Array<{ path: string; matcher: QueryValueMatcher<any> }> {\n\tconst paths: Array<{ path: string; matcher: QueryValueMatcher<any> }> = []\n\n\tfor (const [key, value] of Object.entries(query)) {\n\t\tconst currentPath = prefix ? `${prefix}\\\\${key}` : key\n\n\t\tif (isQueryValueMatcher(value)) {\n\t\t\t// It's a direct matcher\n\t\t\tpaths.push({ path: currentPath, matcher: value })\n\t\t} else if (typeof value === 'object' && value !== null) {\n\t\t\t// It's a nested query - recurse into it\n\t\t\tpaths.push(...extractMatcherPaths(value as QueryExpression<any>, currentPath))\n\t\t}\n\t}\n\n\treturn paths\n}\n\nexport function objectMatchesQuery<T extends object>(query: QueryExpression<T>, object: T) {\n\tfor (const [key, matcher] of Object.entries(query)) {\n\t\tconst value = object[key as keyof T]\n\n\t\t// if you add matching logic here, make sure you also update executeQuery,\n\t\t// where initial data is pulled out of the indexes, since that requires different\n\t\t// matching logic\n\t\tif (isQueryValueMatcher(matcher)) {\n\t\t\tif ('eq' in matcher && value !== matcher.eq) return false\n\t\t\t// undefined values must not match neq: the indexes executeQuery reads only\n\t\t\t// track defined values, and the two matching strategies have to agree\n\t\t\tif ('neq' in matcher && (value === matcher.neq || value === undefined)) return false\n\t\t\tif ('gt' in matcher && (typeof value !== 'number' || value <= matcher.gt)) return false\n\t\t\tcontinue\n\t\t}\n\n\t\t// It's a nested query\n\t\tif (typeof value !== 'object' || value === null) return false\n\t\tif (!objectMatchesQuery(matcher as QueryExpression<any>, value as any)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n/**\n * Executes a query against the store using reactive indexes to efficiently find matching record IDs.\n * Uses the store's internal indexes for optimal performance, especially for equality matches.\n *\n * @param store - The store queries interface providing access to reactive indexes\n * @param typeName - The type name of records to query (e.g., 'book', 'author')\n * @param query - Query expression defining the matching criteria\n * @returns A Set containing the IDs of all records that match the query criteria\n *\n * @example\n * ```ts\n * // Find IDs of all books published after 2020 that are in stock\n * const bookIds = executeQuery(store, 'book', {\n * publishedYear: { gt: 2020 },\n * inStock: { eq: true }\n * })\n *\n * // Find IDs of books not by a specific author\n * const otherBookIds = executeQuery(store, 'book', {\n * authorId: { neq: 'author:tolkien' }\n * })\n *\n * // Query with nested properties\n * const nestedQueryIds = executeQuery(store, 'book', {\n * metadata: { sessionId: { eq: 'session:alpha' } }\n * })\n * ```\n *\n * @public\n */\nexport function executeQuery<R extends UnknownRecord, TypeName extends R['typeName']>(\n\tstore: StoreQueries<R>,\n\ttypeName: TypeName,\n\tquery: QueryExpression<Extract<R, { typeName: TypeName }>>\n): Set<IdOf<Extract<R, { typeName: TypeName }>>> {\n\ttype S = Extract<R, { typeName: TypeName }>\n\n\t// Extract all paths with matchers (flattens nested queries)\n\tconst matcherPaths = extractMatcherPaths(query)\n\n\t// Build a set of matching IDs for each path\n\tconst matchIds = objectMapFromEntries(\n\t\tmatcherPaths.map(({ path }) => [path, new Set<IdOf<S>>()] as const)\n\t)\n\n\t// For each path, use the index to find matching IDs\n\tfor (const { path, matcher } of matcherPaths) {\n\t\tconst index = store.index(typeName, path as any)\n\n\t\tif ('eq' in matcher) {\n\t\t\tconst ids = index.get().get(matcher.eq)\n\t\t\tif (ids) {\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tmatchIds[path].add(id)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if ('neq' in matcher) {\n\t\t\tfor (const [value, ids] of index.get()) {\n\t\t\t\tif (value !== matcher.neq) {\n\t\t\t\t\tfor (const id of ids) {\n\t\t\t\t\t\tmatchIds[path].add(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if ('gt' in matcher) {\n\t\t\tfor (const [value, ids] of index.get()) {\n\t\t\t\tif (typeof value === 'number' && value > matcher.gt) {\n\t\t\t\t\tfor (const id of ids) {\n\t\t\t\t\t\tmatchIds[path].add(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Short-circuit if this set is empty - intersection will be empty\n\t\tif (matchIds[path].size === 0) {\n\t\t\treturn new Set()\n\t\t}\n\t}\n\n\t// Intersect all the match sets\n\treturn intersectSets(objectMapValues(matchIds)) as Set<IdOf<S>>\n}\n"],
5
+ "mappings": "AAAA,SAAS,sBAAsB,uBAAuB;AAEtD,SAAS,qBAAqB;AAwD9B,SAAS,oBAAoB,OAAqD;AACjF,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,SAAO,QAAQ,SAAS,SAAS,SAAS,QAAQ;AACnD;AAEA,SAAS,oBACR,OACA,SAAiB,IAC0C;AAC3D,QAAM,QAAkE,CAAC;AAEzE,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACjD,UAAM,cAAc,SAAS,GAAG,MAAM,KAAK,GAAG,KAAK;AAEnD,QAAI,oBAAoB,KAAK,GAAG;AAE/B,YAAM,KAAK,EAAE,MAAM,aAAa,SAAS,MAAM,CAAC;AAAA,IACjD,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AAEvD,YAAM,KAAK,GAAG,oBAAoB,OAA+B,WAAW,CAAC;AAAA,IAC9E;AAAA,EACD;AAEA,SAAO;AACR;AAEO,SAAS,mBAAqC,OAA2B,QAAW;AAC1F,aAAW,CAAC,KAAK,OAAO,KAAK,OAAO,QAAQ,KAAK,GAAG;AACnD,UAAM,QAAQ,OAAO,GAAc;AAKnC,QAAI,oBAAoB,OAAO,GAAG;AACjC,UAAI,QAAQ,WAAW,UAAU,QAAQ,GAAI,QAAO;AAGpD,UAAI,SAAS,YAAY,UAAU,QAAQ,OAAO,UAAU,QAAY,QAAO;AAC/E,UAAI,QAAQ,YAAY,OAAO,UAAU,YAAY,SAAS,QAAQ,IAAK,QAAO;AAClF;AAAA,IACD;AAGA,QAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAI,CAAC,mBAAmB,SAAiC,KAAY,GAAG;AACvE,aAAO;AAAA,IACR;AAAA,EACD;AACA,SAAO;AACR;AAgCO,SAAS,aACf,OACA,UACA,OACgD;AAIhD,QAAM,eAAe,oBAAoB,KAAK;AAG9C,QAAM,WAAW;AAAA,IAChB,aAAa,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,MAAM,oBAAI,IAAa,CAAC,CAAU;AAAA,EACnE;AAGA,aAAW,EAAE,MAAM,QAAQ,KAAK,cAAc;AAC7C,UAAM,QAAQ,MAAM,MAAM,UAAU,IAAW;AAE/C,QAAI,QAAQ,SAAS;AACpB,YAAM,MAAM,MAAM,IAAI,EAAE,IAAI,QAAQ,EAAE;AACtC,UAAI,KAAK;AACR,mBAAW,MAAM,KAAK;AACrB,mBAAS,IAAI,EAAE,IAAI,EAAE;AAAA,QACtB;AAAA,MACD;AAAA,IACD,WAAW,SAAS,SAAS;AAC5B,iBAAW,CAAC,OAAO,GAAG,KAAK,MAAM,IAAI,GAAG;AACvC,YAAI,UAAU,QAAQ,KAAK;AAC1B,qBAAW,MAAM,KAAK;AACrB,qBAAS,IAAI,EAAE,IAAI,EAAE;AAAA,UACtB;AAAA,QACD;AAAA,MACD;AAAA,IACD,WAAW,QAAQ,SAAS;AAC3B,iBAAW,CAAC,OAAO,GAAG,KAAK,MAAM,IAAI,GAAG;AACvC,YAAI,OAAO,UAAU,YAAY,QAAQ,QAAQ,IAAI;AACpD,qBAAW,MAAM,KAAK;AACrB,qBAAS,IAAI,EAAE,IAAI,EAAE;AAAA,UACtB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAGA,QAAI,SAAS,IAAI,EAAE,SAAS,GAAG;AAC9B,aAAO,oBAAI,IAAI;AAAA,IAChB;AAAA,EACD;AAGA,SAAO,cAAc,gBAAgB,QAAQ,CAAC;AAC/C;",
6
6
  "names": []
7
7
  }
@@ -41,13 +41,11 @@ function createRecordMigrationSequence(opts) {
41
41
  return createMigrationSequence({
42
42
  sequenceId,
43
43
  retroactive: opts.retroactive ?? true,
44
- sequence: opts.sequence.map(
45
- (m) => "id" in m ? {
46
- ...m,
47
- scope: "record",
48
- filter: (r) => r.typeName === opts.recordType && (m.filter?.(r) ?? true) && (opts.filter?.(r) ?? true)
49
- } : m
50
- )
44
+ sequence: opts.sequence.map((m) => ({
45
+ ...m,
46
+ scope: "record",
47
+ filter: (r) => r.typeName === opts.recordType && (m.filter?.(r) ?? true) && (opts.filter?.(r) ?? true)
48
+ }))
51
49
  });
52
50
  }
53
51
  function sortMigrations(migrations) {