@tldraw/store 4.1.0-canary.ccd6179e1cb2 → 4.1.0-canary.e23ee15a46bc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist-cjs/index.d.ts +1884 -153
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/AtomMap.js +241 -1
  4. package/dist-cjs/lib/AtomMap.js.map +2 -2
  5. package/dist-cjs/lib/BaseRecord.js.map +2 -2
  6. package/dist-cjs/lib/ImmutableMap.js +141 -0
  7. package/dist-cjs/lib/ImmutableMap.js.map +2 -2
  8. package/dist-cjs/lib/IncrementalSetConstructor.js +45 -5
  9. package/dist-cjs/lib/IncrementalSetConstructor.js.map +2 -2
  10. package/dist-cjs/lib/RecordType.js +116 -21
  11. package/dist-cjs/lib/RecordType.js.map +2 -2
  12. package/dist-cjs/lib/RecordsDiff.js.map +2 -2
  13. package/dist-cjs/lib/Store.js +233 -39
  14. package/dist-cjs/lib/Store.js.map +2 -2
  15. package/dist-cjs/lib/StoreQueries.js +135 -22
  16. package/dist-cjs/lib/StoreQueries.js.map +2 -2
  17. package/dist-cjs/lib/StoreSchema.js +207 -2
  18. package/dist-cjs/lib/StoreSchema.js.map +2 -2
  19. package/dist-cjs/lib/StoreSideEffects.js +102 -10
  20. package/dist-cjs/lib/StoreSideEffects.js.map +2 -2
  21. package/dist-cjs/lib/executeQuery.js.map +2 -2
  22. package/dist-cjs/lib/migrate.js.map +2 -2
  23. package/dist-cjs/lib/setUtils.js.map +2 -2
  24. package/dist-esm/index.d.mts +1884 -153
  25. package/dist-esm/index.mjs +1 -1
  26. package/dist-esm/lib/AtomMap.mjs +241 -1
  27. package/dist-esm/lib/AtomMap.mjs.map +2 -2
  28. package/dist-esm/lib/BaseRecord.mjs.map +2 -2
  29. package/dist-esm/lib/ImmutableMap.mjs +141 -0
  30. package/dist-esm/lib/ImmutableMap.mjs.map +2 -2
  31. package/dist-esm/lib/IncrementalSetConstructor.mjs +45 -5
  32. package/dist-esm/lib/IncrementalSetConstructor.mjs.map +2 -2
  33. package/dist-esm/lib/RecordType.mjs +116 -21
  34. package/dist-esm/lib/RecordType.mjs.map +2 -2
  35. package/dist-esm/lib/RecordsDiff.mjs.map +2 -2
  36. package/dist-esm/lib/Store.mjs +233 -39
  37. package/dist-esm/lib/Store.mjs.map +2 -2
  38. package/dist-esm/lib/StoreQueries.mjs +135 -22
  39. package/dist-esm/lib/StoreQueries.mjs.map +2 -2
  40. package/dist-esm/lib/StoreSchema.mjs +207 -2
  41. package/dist-esm/lib/StoreSchema.mjs.map +2 -2
  42. package/dist-esm/lib/StoreSideEffects.mjs +102 -10
  43. package/dist-esm/lib/StoreSideEffects.mjs.map +2 -2
  44. package/dist-esm/lib/executeQuery.mjs.map +2 -2
  45. package/dist-esm/lib/migrate.mjs.map +2 -2
  46. package/dist-esm/lib/setUtils.mjs.map +2 -2
  47. package/package.json +3 -3
  48. package/src/lib/AtomMap.ts +241 -1
  49. package/src/lib/BaseRecord.test.ts +44 -0
  50. package/src/lib/BaseRecord.ts +118 -4
  51. package/src/lib/ImmutableMap.test.ts +103 -0
  52. package/src/lib/ImmutableMap.ts +212 -0
  53. package/src/lib/IncrementalSetConstructor.test.ts +111 -0
  54. package/src/lib/IncrementalSetConstructor.ts +63 -6
  55. package/src/lib/RecordType.ts +149 -25
  56. package/src/lib/RecordsDiff.test.ts +144 -0
  57. package/src/lib/RecordsDiff.ts +145 -10
  58. package/src/lib/Store.test.ts +827 -0
  59. package/src/lib/Store.ts +533 -67
  60. package/src/lib/StoreQueries.test.ts +627 -0
  61. package/src/lib/StoreQueries.ts +194 -27
  62. package/src/lib/StoreSchema.test.ts +226 -0
  63. package/src/lib/StoreSchema.ts +386 -8
  64. package/src/lib/StoreSideEffects.test.ts +239 -19
  65. package/src/lib/StoreSideEffects.ts +266 -19
  66. package/src/lib/devFreeze.test.ts +137 -0
  67. package/src/lib/executeQuery.test.ts +481 -0
  68. package/src/lib/executeQuery.ts +80 -2
  69. package/src/lib/migrate.test.ts +400 -0
  70. package/src/lib/migrate.ts +187 -14
  71. package/src/lib/setUtils.test.ts +105 -0
  72. package/src/lib/setUtils.ts +44 -4
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/RecordsDiff.ts"],
4
- "sourcesContent": ["import { objectMapEntries } from '@tldraw/utils'\nimport { IdOf, UnknownRecord } from './BaseRecord'\n\n/**\n * A diff describing the changes to a record.\n *\n * @public\n */\nexport interface RecordsDiff<R extends UnknownRecord> {\n\tadded: Record<IdOf<R>, R>\n\tupdated: Record<IdOf<R>, [from: R, to: R]>\n\tremoved: Record<IdOf<R>, R>\n}\n\n/** @internal */\nexport function createEmptyRecordsDiff<R extends UnknownRecord>(): RecordsDiff<R> {\n\treturn { added: {}, updated: {}, removed: {} } as RecordsDiff<R>\n}\n\n/** @public */\nexport function reverseRecordsDiff(diff: RecordsDiff<any>) {\n\tconst result: RecordsDiff<any> = { added: diff.removed, removed: diff.added, updated: {} }\n\tfor (const [from, to] of Object.values(diff.updated)) {\n\t\tresult.updated[from.id] = [to, from]\n\t}\n\treturn result\n}\n\n/**\n * Is a records diff empty?\n * @internal\n */\nexport function isRecordsDiffEmpty<T extends UnknownRecord>(diff: RecordsDiff<T>) {\n\treturn (\n\t\tObject.keys(diff.added).length === 0 &&\n\t\tObject.keys(diff.updated).length === 0 &&\n\t\tObject.keys(diff.removed).length === 0\n\t)\n}\n\n/**\n * Squash a collection of diffs into a single diff.\n *\n * @param diffs - An array of diffs to squash.\n * @param options - An optional object with a `mutateFirstDiff` property. If `mutateFirstDiff` is true, the first diff in the array will be mutated in-place.\n * @returns A single diff that represents the squashed diffs.\n * @public\n */\nexport function squashRecordDiffs<T extends UnknownRecord>(\n\tdiffs: RecordsDiff<T>[],\n\toptions?: {\n\t\tmutateFirstDiff?: boolean\n\t}\n): RecordsDiff<T> {\n\tconst result = options?.mutateFirstDiff\n\t\t? diffs[0]\n\t\t: ({ added: {}, removed: {}, updated: {} } as RecordsDiff<T>)\n\n\tsquashRecordDiffsMutable(result, options?.mutateFirstDiff ? diffs.slice(1) : diffs)\n\treturn result\n}\n\n/**\n * Apply the array `diffs` to the `target` diff, mutating it in-place.\n * @internal\n */\nexport function squashRecordDiffsMutable<T extends UnknownRecord>(\n\ttarget: RecordsDiff<T>,\n\tdiffs: RecordsDiff<T>[]\n): void {\n\tfor (const diff of diffs) {\n\t\tfor (const [id, value] of objectMapEntries(diff.added)) {\n\t\t\tif (target.removed[id]) {\n\t\t\t\tconst original = target.removed[id]\n\t\t\t\tdelete target.removed[id]\n\t\t\t\tif (original !== value) {\n\t\t\t\t\ttarget.updated[id] = [original, value]\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttarget.added[id] = value\n\t\t\t}\n\t\t}\n\n\t\tfor (const [id, [_from, to]] of objectMapEntries(diff.updated)) {\n\t\t\tif (target.added[id]) {\n\t\t\t\ttarget.added[id] = to\n\t\t\t\tdelete target.updated[id]\n\t\t\t\tdelete target.removed[id]\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (target.updated[id]) {\n\t\t\t\ttarget.updated[id] = [target.updated[id][0], to]\n\t\t\t\tdelete target.removed[id]\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\ttarget.updated[id] = diff.updated[id]\n\t\t\tdelete target.removed[id]\n\t\t}\n\n\t\tfor (const [id, value] of objectMapEntries(diff.removed)) {\n\t\t\t// the same record was added in this diff sequence, just drop it\n\t\t\tif (target.added[id]) {\n\t\t\t\tdelete target.added[id]\n\t\t\t} else if (target.updated[id]) {\n\t\t\t\ttarget.removed[id] = target.updated[id][0]\n\t\t\t\tdelete target.updated[id]\n\t\t\t} else {\n\t\t\t\ttarget.removed[id] = value\n\t\t\t}\n\t\t}\n\t}\n}\n"],
5
- "mappings": "AAAA,SAAS,wBAAwB;AAe1B,SAAS,yBAAkE;AACjF,SAAO,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,EAAE;AAC9C;AAGO,SAAS,mBAAmB,MAAwB;AAC1D,QAAM,SAA2B,EAAE,OAAO,KAAK,SAAS,SAAS,KAAK,OAAO,SAAS,CAAC,EAAE;AACzF,aAAW,CAAC,MAAM,EAAE,KAAK,OAAO,OAAO,KAAK,OAAO,GAAG;AACrD,WAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,IAAI,IAAI;AAAA,EACpC;AACA,SAAO;AACR;AAMO,SAAS,mBAA4C,MAAsB;AACjF,SACC,OAAO,KAAK,KAAK,KAAK,EAAE,WAAW,KACnC,OAAO,KAAK,KAAK,OAAO,EAAE,WAAW,KACrC,OAAO,KAAK,KAAK,OAAO,EAAE,WAAW;AAEvC;AAUO,SAAS,kBACf,OACA,SAGiB;AACjB,QAAM,SAAS,SAAS,kBACrB,MAAM,CAAC,IACN,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,EAAE;AAE1C,2BAAyB,QAAQ,SAAS,kBAAkB,MAAM,MAAM,CAAC,IAAI,KAAK;AAClF,SAAO;AACR;AAMO,SAAS,yBACf,QACA,OACO;AACP,aAAW,QAAQ,OAAO;AACzB,eAAW,CAAC,IAAI,KAAK,KAAK,iBAAiB,KAAK,KAAK,GAAG;AACvD,UAAI,OAAO,QAAQ,EAAE,GAAG;AACvB,cAAM,WAAW,OAAO,QAAQ,EAAE;AAClC,eAAO,OAAO,QAAQ,EAAE;AACxB,YAAI,aAAa,OAAO;AACvB,iBAAO,QAAQ,EAAE,IAAI,CAAC,UAAU,KAAK;AAAA,QACtC;AAAA,MACD,OAAO;AACN,eAAO,MAAM,EAAE,IAAI;AAAA,MACpB;AAAA,IACD;AAEA,eAAW,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,iBAAiB,KAAK,OAAO,GAAG;AAC/D,UAAI,OAAO,MAAM,EAAE,GAAG;AACrB,eAAO,MAAM,EAAE,IAAI;AACnB,eAAO,OAAO,QAAQ,EAAE;AACxB,eAAO,OAAO,QAAQ,EAAE;AACxB;AAAA,MACD;AACA,UAAI,OAAO,QAAQ,EAAE,GAAG;AACvB,eAAO,QAAQ,EAAE,IAAI,CAAC,OAAO,QAAQ,EAAE,EAAE,CAAC,GAAG,EAAE;AAC/C,eAAO,OAAO,QAAQ,EAAE;AACxB;AAAA,MACD;AAEA,aAAO,QAAQ,EAAE,IAAI,KAAK,QAAQ,EAAE;AACpC,aAAO,OAAO,QAAQ,EAAE;AAAA,IACzB;AAEA,eAAW,CAAC,IAAI,KAAK,KAAK,iBAAiB,KAAK,OAAO,GAAG;AAEzD,UAAI,OAAO,MAAM,EAAE,GAAG;AACrB,eAAO,OAAO,MAAM,EAAE;AAAA,MACvB,WAAW,OAAO,QAAQ,EAAE,GAAG;AAC9B,eAAO,QAAQ,EAAE,IAAI,OAAO,QAAQ,EAAE,EAAE,CAAC;AACzC,eAAO,OAAO,QAAQ,EAAE;AAAA,MACzB,OAAO;AACN,eAAO,QAAQ,EAAE,IAAI;AAAA,MACtB;AAAA,IACD;AAAA,EACD;AACD;",
4
+ "sourcesContent": ["import { objectMapEntries } from '@tldraw/utils'\nimport { IdOf, UnknownRecord } from './BaseRecord'\n\n/**\n * A diff describing the changes to records, containing collections of records that were added,\n * updated, or removed. This is the fundamental data structure used throughout the store system\n * to track and communicate changes.\n *\n * @example\n * ```ts\n * const diff: RecordsDiff<Book> = {\n * added: {\n * 'book:1': { id: 'book:1', typeName: 'book', title: 'New Book' }\n * },\n * updated: {\n * 'book:2': [\n * { id: 'book:2', typeName: 'book', title: 'Old Title' }, // from\n * { id: 'book:2', typeName: 'book', title: 'New Title' } // to\n * ]\n * },\n * removed: {\n * 'book:3': { id: 'book:3', typeName: 'book', title: 'Deleted Book' }\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface RecordsDiff<R extends UnknownRecord> {\n\t/** Records that were created, keyed by their ID */\n\tadded: Record<IdOf<R>, R>\n\t/** Records that were modified, keyed by their ID. Each entry contains [from, to] tuple */\n\tupdated: Record<IdOf<R>, [from: R, to: R]>\n\t/** Records that were deleted, keyed by their ID */\n\tremoved: Record<IdOf<R>, R>\n}\n\n/**\n * Creates an empty RecordsDiff with no added, updated, or removed records.\n * This is useful as a starting point when building diffs programmatically.\n *\n * @returns An empty RecordsDiff with all collections initialized to empty objects\n * @example\n * ```ts\n * const emptyDiff = createEmptyRecordsDiff<Book>()\n * // Result: { added: {}, updated: {}, removed: {} }\n * ```\n *\n * @internal\n */\nexport function createEmptyRecordsDiff<R extends UnknownRecord>(): RecordsDiff<R> {\n\treturn { added: {}, updated: {}, removed: {} } as RecordsDiff<R>\n}\n\n/**\n * Creates the inverse of a RecordsDiff, effectively reversing all changes.\n * Added records become removed, removed records become added, and updated records\n * have their from/to values swapped. This is useful for implementing undo operations.\n *\n * @param diff - The diff to reverse\n * @returns A new RecordsDiff that represents the inverse of the input diff\n * @example\n * ```ts\n * const originalDiff: RecordsDiff<Book> = {\n * added: { 'book:1': newBook },\n * updated: { 'book:2': [oldBook, updatedBook] },\n * removed: { 'book:3': deletedBook }\n * }\n *\n * const reversedDiff = reverseRecordsDiff(originalDiff)\n * // Result: {\n * // added: { 'book:3': deletedBook },\n * // updated: { 'book:2': [updatedBook, oldBook] },\n * // removed: { 'book:1': newBook }\n * // }\n * ```\n *\n * @public\n */\nexport function reverseRecordsDiff(diff: RecordsDiff<any>) {\n\tconst result: RecordsDiff<any> = { added: diff.removed, removed: diff.added, updated: {} }\n\tfor (const [from, to] of Object.values(diff.updated)) {\n\t\tresult.updated[from.id] = [to, from]\n\t}\n\treturn result\n}\n\n/**\n * Checks whether a RecordsDiff contains any changes. A diff is considered empty\n * if it has no added, updated, or removed records.\n *\n * @param diff - The diff to check\n * @returns True if the diff contains no changes, false otherwise\n * @example\n * ```ts\n * const emptyDiff = createEmptyRecordsDiff<Book>()\n * console.log(isRecordsDiffEmpty(emptyDiff)) // true\n *\n * const nonEmptyDiff: RecordsDiff<Book> = {\n * added: { 'book:1': someBook },\n * updated: {},\n * removed: {}\n * }\n * console.log(isRecordsDiffEmpty(nonEmptyDiff)) // false\n * ```\n *\n * @public\n */\nexport function isRecordsDiffEmpty<T extends UnknownRecord>(diff: RecordsDiff<T>) {\n\treturn (\n\t\tObject.keys(diff.added).length === 0 &&\n\t\tObject.keys(diff.updated).length === 0 &&\n\t\tObject.keys(diff.removed).length === 0\n\t)\n}\n\n/**\n * Combines multiple RecordsDiff objects into a single consolidated diff.\n * This function intelligently merges changes, handling cases where the same record\n * is modified multiple times across different diffs. For example, if a record is\n * added in one diff and then updated in another, the result will show it as added\n * with the final state.\n *\n * @param diffs - An array of diffs to combine into a single diff\n * @param options - Configuration options for the squashing operation\n * - mutateFirstDiff - If true, modifies the first diff in place instead of creating a new one\n * @returns A single diff that represents the cumulative effect of all input diffs\n * @example\n * ```ts\n * const diff1: RecordsDiff<Book> = {\n * added: { 'book:1': { id: 'book:1', title: 'New Book' } },\n * updated: {},\n * removed: {}\n * }\n *\n * const diff2: RecordsDiff<Book> = {\n * added: {},\n * updated: { 'book:1': [{ id: 'book:1', title: 'New Book' }, { id: 'book:1', title: 'Updated Title' }] },\n * removed: {}\n * }\n *\n * const squashed = squashRecordDiffs([diff1, diff2])\n * // Result: {\n * // added: { 'book:1': { id: 'book:1', title: 'Updated Title' } },\n * // updated: {},\n * // removed: {}\n * // }\n * ```\n *\n * @public\n */\nexport function squashRecordDiffs<T extends UnknownRecord>(\n\tdiffs: RecordsDiff<T>[],\n\toptions?: {\n\t\tmutateFirstDiff?: boolean\n\t}\n): RecordsDiff<T> {\n\tconst result = options?.mutateFirstDiff\n\t\t? diffs[0]\n\t\t: ({ added: {}, removed: {}, updated: {} } as RecordsDiff<T>)\n\n\tsquashRecordDiffsMutable(result, options?.mutateFirstDiff ? diffs.slice(1) : diffs)\n\treturn result\n}\n\n/**\n * Applies an array of diffs to a target diff by mutating the target in-place.\n * This is the core implementation used by squashRecordDiffs. It handles complex\n * scenarios where records move between added/updated/removed states across multiple diffs.\n *\n * The function processes each diff sequentially, applying the following logic:\n * - Added records: If the record was previously removed, convert to an update; otherwise add it\n * - Updated records: Chain updates together, preserving the original 'from' state\n * - Removed records: If the record was added in this sequence, cancel both operations\n *\n * @param target - The diff to modify in-place (will be mutated)\n * @param diffs - Array of diffs to apply to the target\n * @example\n * ```ts\n * const targetDiff: RecordsDiff<Book> = {\n * added: {},\n * updated: {},\n * removed: { 'book:1': oldBook }\n * }\n *\n * const newDiffs = [{\n * added: { 'book:1': newBook },\n * updated: {},\n * removed: {}\n * }]\n *\n * squashRecordDiffsMutable(targetDiff, newDiffs)\n * // targetDiff is now: {\n * // added: {},\n * // updated: { 'book:1': [oldBook, newBook] },\n * // removed: {}\n * // }\n * ```\n *\n * @internal\n */\nexport function squashRecordDiffsMutable<T extends UnknownRecord>(\n\ttarget: RecordsDiff<T>,\n\tdiffs: RecordsDiff<T>[]\n): void {\n\tfor (const diff of diffs) {\n\t\tfor (const [id, value] of objectMapEntries(diff.added)) {\n\t\t\tif (target.removed[id]) {\n\t\t\t\tconst original = target.removed[id]\n\t\t\t\tdelete target.removed[id]\n\t\t\t\tif (original !== value) {\n\t\t\t\t\ttarget.updated[id] = [original, value]\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttarget.added[id] = value\n\t\t\t}\n\t\t}\n\n\t\tfor (const [id, [_from, to]] of objectMapEntries(diff.updated)) {\n\t\t\tif (target.added[id]) {\n\t\t\t\ttarget.added[id] = to\n\t\t\t\tdelete target.updated[id]\n\t\t\t\tdelete target.removed[id]\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (target.updated[id]) {\n\t\t\t\ttarget.updated[id] = [target.updated[id][0], to]\n\t\t\t\tdelete target.removed[id]\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\ttarget.updated[id] = diff.updated[id]\n\t\t\tdelete target.removed[id]\n\t\t}\n\n\t\tfor (const [id, value] of objectMapEntries(diff.removed)) {\n\t\t\t// the same record was added in this diff sequence, just drop it\n\t\t\tif (target.added[id]) {\n\t\t\t\tdelete target.added[id]\n\t\t\t} else if (target.updated[id]) {\n\t\t\t\ttarget.removed[id] = target.updated[id][0]\n\t\t\t\tdelete target.updated[id]\n\t\t\t} else {\n\t\t\t\ttarget.removed[id] = value\n\t\t\t}\n\t\t}\n\t}\n}\n"],
5
+ "mappings": "AAAA,SAAS,wBAAwB;AAkD1B,SAAS,yBAAkE;AACjF,SAAO,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,EAAE;AAC9C;AA2BO,SAAS,mBAAmB,MAAwB;AAC1D,QAAM,SAA2B,EAAE,OAAO,KAAK,SAAS,SAAS,KAAK,OAAO,SAAS,CAAC,EAAE;AACzF,aAAW,CAAC,MAAM,EAAE,KAAK,OAAO,OAAO,KAAK,OAAO,GAAG;AACrD,WAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,IAAI,IAAI;AAAA,EACpC;AACA,SAAO;AACR;AAuBO,SAAS,mBAA4C,MAAsB;AACjF,SACC,OAAO,KAAK,KAAK,KAAK,EAAE,WAAW,KACnC,OAAO,KAAK,KAAK,OAAO,EAAE,WAAW,KACrC,OAAO,KAAK,KAAK,OAAO,EAAE,WAAW;AAEvC;AAqCO,SAAS,kBACf,OACA,SAGiB;AACjB,QAAM,SAAS,SAAS,kBACrB,MAAM,CAAC,IACN,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,EAAE;AAE1C,2BAAyB,QAAQ,SAAS,kBAAkB,MAAM,MAAM,CAAC,IAAI,KAAK;AAClF,SAAO;AACR;AAsCO,SAAS,yBACf,QACA,OACO;AACP,aAAW,QAAQ,OAAO;AACzB,eAAW,CAAC,IAAI,KAAK,KAAK,iBAAiB,KAAK,KAAK,GAAG;AACvD,UAAI,OAAO,QAAQ,EAAE,GAAG;AACvB,cAAM,WAAW,OAAO,QAAQ,EAAE;AAClC,eAAO,OAAO,QAAQ,EAAE;AACxB,YAAI,aAAa,OAAO;AACvB,iBAAO,QAAQ,EAAE,IAAI,CAAC,UAAU,KAAK;AAAA,QACtC;AAAA,MACD,OAAO;AACN,eAAO,MAAM,EAAE,IAAI;AAAA,MACpB;AAAA,IACD;AAEA,eAAW,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,iBAAiB,KAAK,OAAO,GAAG;AAC/D,UAAI,OAAO,MAAM,EAAE,GAAG;AACrB,eAAO,MAAM,EAAE,IAAI;AACnB,eAAO,OAAO,QAAQ,EAAE;AACxB,eAAO,OAAO,QAAQ,EAAE;AACxB;AAAA,MACD;AACA,UAAI,OAAO,QAAQ,EAAE,GAAG;AACvB,eAAO,QAAQ,EAAE,IAAI,CAAC,OAAO,QAAQ,EAAE,EAAE,CAAC,GAAG,EAAE;AAC/C,eAAO,OAAO,QAAQ,EAAE;AACxB;AAAA,MACD;AAEA,aAAO,QAAQ,EAAE,IAAI,KAAK,QAAQ,EAAE;AACpC,aAAO,OAAO,QAAQ,EAAE;AAAA,IACzB;AAEA,eAAW,CAAC,IAAI,KAAK,KAAK,iBAAiB,KAAK,OAAO,GAAG;AAEzD,UAAI,OAAO,MAAM,EAAE,GAAG;AACrB,eAAO,OAAO,MAAM,EAAE;AAAA,MACvB,WAAW,OAAO,QAAQ,EAAE,GAAG;AAC9B,eAAO,QAAQ,EAAE,IAAI,OAAO,QAAQ,EAAE,EAAE,CAAC;AACzC,eAAO,OAAO,QAAQ,EAAE;AAAA,MACzB,OAAO;AACN,eAAO,QAAQ,EAAE,IAAI;AAAA,MACtB;AAAA,IACD;AAAA,EACD;AACD;",
6
6
  "names": []
7
7
  }
@@ -18,7 +18,9 @@ import { StoreSideEffects } from "./StoreSideEffects.mjs";
18
18
  import { devFreeze } from "./devFreeze.mjs";
19
19
  class Store {
20
20
  /**
21
- * The random id of the store.
21
+ * The unique identifier of the store instance.
22
+ *
23
+ * @public
22
24
  */
23
25
  id;
24
26
  /**
@@ -38,7 +40,19 @@ class Store {
38
40
  historyLength: 1e3
39
41
  });
40
42
  /**
41
- * A StoreQueries instance for this store.
43
+ * Reactive queries and indexes for efficiently accessing store data.
44
+ * Provides methods for filtering, indexing, and subscribing to subsets of records.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * // Create an index by a property
49
+ * const booksByAuthor = store.query.index('book', 'author')
50
+ *
51
+ * // Get records matching criteria
52
+ * const inStockBooks = store.query.records('book', () => ({
53
+ * inStock: { eq: true }
54
+ * }))
55
+ * ```
42
56
  *
43
57
  * @public
44
58
  * @readonly
@@ -70,10 +84,53 @@ class Store {
70
84
  */
71
85
  cancelHistoryReactor() {
72
86
  }
87
+ /**
88
+ * The schema that defines the structure and validation rules for records in this store.
89
+ *
90
+ * @public
91
+ */
73
92
  schema;
93
+ /**
94
+ * Custom properties associated with this store instance.
95
+ *
96
+ * @public
97
+ */
74
98
  props;
99
+ /**
100
+ * A mapping of record scopes to the set of record type names that belong to each scope.
101
+ * Used to filter records by their persistence and synchronization behavior.
102
+ *
103
+ * @public
104
+ */
75
105
  scopedTypes;
106
+ /**
107
+ * Side effects manager that handles lifecycle events for record operations.
108
+ * Allows registration of callbacks for create, update, delete, and validation events.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * store.sideEffects.registerAfterCreateHandler('book', (book) => {
113
+ * console.log('Book created:', book.title)
114
+ * })
115
+ * ```
116
+ *
117
+ * @public
118
+ */
76
119
  sideEffects = new StoreSideEffects(this);
120
+ /**
121
+ * Creates a new Store instance.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const store = new Store({
126
+ * schema: StoreSchema.create({ book: Book }),
127
+ * props: { appName: 'MyLibrary' },
128
+ * initialData: savedData
129
+ * })
130
+ * ```
131
+ *
132
+ * @param config - Configuration object for the store
133
+ */
77
134
  constructor(config) {
78
135
  const { initialData, schema, id } = config;
79
136
  this.id = id ?? uniqueId();
@@ -182,10 +239,21 @@ class Store {
182
239
  this.allRecords().forEach((record) => this.schema.validateRecord(this, record, phase, null));
183
240
  }
184
241
  /**
185
- * Add some records to the store. It's an error if they already exist.
242
+ * Add or update records in the store. If a record with the same ID already exists, it will be updated.
243
+ * Otherwise, a new record will be created.
244
+ *
245
+ * @example
246
+ * ```ts
247
+ * // Add new records
248
+ * const book = Book.create({ title: 'Lathe Of Heaven', author: 'Le Guin' })
249
+ * store.put([book])
186
250
  *
187
- * @param records - The records to add.
188
- * @param phaseOverride - The phase override.
251
+ * // Update existing record
252
+ * store.put([{ ...book, title: 'The Lathe of Heaven' }])
253
+ * ```
254
+ *
255
+ * @param records - The records to add or update
256
+ * @param phaseOverride - Override the validation phase (used internally)
189
257
  * @public
190
258
  */
191
259
  put(records, phaseOverride) {
@@ -236,9 +304,18 @@ class Store {
236
304
  });
237
305
  }
238
306
  /**
239
- * Remove some records from the store via their ids.
307
+ * Remove records from the store by their IDs.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * // Remove a single record
312
+ * store.remove([book.id])
313
+ *
314
+ * // Remove multiple records
315
+ * store.remove([book1.id, book2.id, book3.id])
316
+ * ```
240
317
  *
241
- * @param ids - The ids of the records to remove.
318
+ * @param ids - The IDs of the records to remove
242
319
  * @public
243
320
  */
244
321
  remove(ids) {
@@ -265,28 +342,56 @@ class Store {
265
342
  });
266
343
  }
267
344
  /**
268
- * Get the value of a store record by its id.
345
+ * Get a record by its ID. This creates a reactive subscription to the record.
269
346
  *
270
- * @param id - The id of the record to get.
347
+ * @example
348
+ * ```ts
349
+ * const book = store.get(bookId)
350
+ * if (book) {
351
+ * console.log(book.title)
352
+ * }
353
+ * ```
354
+ *
355
+ * @param id - The ID of the record to get
356
+ * @returns The record if it exists, undefined otherwise
271
357
  * @public
272
358
  */
273
359
  get(id) {
274
360
  return this.records.get(id);
275
361
  }
276
362
  /**
277
- * Get the value of a store record by its id without updating its epoch.
363
+ * Get a record by its ID without creating a reactive subscription.
364
+ * Use this when you need to access a record but don't want reactive updates.
278
365
  *
279
- * @param id - The id of the record to get.
366
+ * @example
367
+ * ```ts
368
+ * // Won't trigger reactive updates when this record changes
369
+ * const book = store.unsafeGetWithoutCapture(bookId)
370
+ * ```
371
+ *
372
+ * @param id - The ID of the record to get
373
+ * @returns The record if it exists, undefined otherwise
280
374
  * @public
281
375
  */
282
376
  unsafeGetWithoutCapture(id) {
283
377
  return this.records.__unsafe__getWithoutCapture(id);
284
378
  }
285
379
  /**
286
- * Creates a JSON payload from the record store.
380
+ * Serialize the store's records to a plain JavaScript object.
381
+ * Only includes records matching the specified scope.
382
+ *
383
+ * @example
384
+ * ```ts
385
+ * // Serialize only document records (default)
386
+ * const documentData = store.serialize('document')
287
387
  *
288
- * @param scope - The scope of records to serialize. Defaults to 'document'.
289
- * @returns The record store snapshot as a JSON payload.
388
+ * // Serialize all records
389
+ * const allData = store.serialize('all')
390
+ * ```
391
+ *
392
+ * @param scope - The scope of records to serialize. Defaults to 'document'
393
+ * @returns The serialized store data
394
+ * @public
290
395
  */
291
396
  serialize(scope = "document") {
292
397
  const result = {};
@@ -299,14 +404,20 @@ class Store {
299
404
  }
300
405
  /**
301
406
  * Get a serialized snapshot of the store and its schema.
407
+ * This includes both the data and schema information needed for proper migration.
302
408
  *
409
+ * @example
303
410
  * ```ts
304
411
  * const snapshot = store.getStoreSnapshot()
305
- * store.loadStoreSnapshot(snapshot)
306
- * ```
412
+ * localStorage.setItem('myApp', JSON.stringify(snapshot))
307
413
  *
308
- * @param scope - The scope of records to serialize. Defaults to 'document'.
414
+ * // Later...
415
+ * const saved = JSON.parse(localStorage.getItem('myApp'))
416
+ * store.loadStoreSnapshot(saved)
417
+ * ```
309
418
  *
419
+ * @param scope - The scope of records to serialize. Defaults to 'document'
420
+ * @returns A snapshot containing both store data and schema information
310
421
  * @public
311
422
  */
312
423
  getStoreSnapshot(scope = "document") {
@@ -316,14 +427,18 @@ class Store {
316
427
  };
317
428
  }
318
429
  /**
319
- * Migrate a serialized snapshot of the store and its schema.
430
+ * Migrate a serialized snapshot to the current schema version.
431
+ * This applies any necessary migrations to bring old data up to date.
320
432
  *
433
+ * @example
321
434
  * ```ts
322
- * const snapshot = store.getStoreSnapshot()
323
- * store.migrateSnapshot(snapshot)
435
+ * const oldSnapshot = JSON.parse(localStorage.getItem('myApp'))
436
+ * const migratedSnapshot = store.migrateSnapshot(oldSnapshot)
324
437
  * ```
325
438
  *
326
- * @param snapshot - The snapshot to load.
439
+ * @param snapshot - The snapshot to migrate
440
+ * @returns The migrated snapshot with current schema version
441
+ * @throws Error if migration fails
327
442
  * @public
328
443
  */
329
444
  migrateSnapshot(snapshot) {
@@ -337,14 +452,17 @@ class Store {
337
452
  };
338
453
  }
339
454
  /**
340
- * Load a serialized snapshot.
455
+ * Load a serialized snapshot into the store, replacing all current data.
456
+ * The snapshot will be automatically migrated to the current schema version if needed.
341
457
  *
458
+ * @example
342
459
  * ```ts
343
- * const snapshot = store.getStoreSnapshot()
460
+ * const snapshot = JSON.parse(localStorage.getItem('myApp'))
344
461
  * store.loadStoreSnapshot(snapshot)
345
462
  * ```
346
463
  *
347
- * @param snapshot - The snapshot to load.
464
+ * @param snapshot - The snapshot to load
465
+ * @throws Error if migration fails or snapshot is invalid
348
466
  * @public
349
467
  */
350
468
  loadStoreSnapshot(snapshot) {
@@ -365,16 +483,28 @@ class Store {
365
483
  }
366
484
  }
367
485
  /**
368
- * Get an array of all values in the store.
486
+ * Get an array of all records in the store.
369
487
  *
370
- * @returns An array of all values in the store.
488
+ * @example
489
+ * ```ts
490
+ * const allRecords = store.allRecords()
491
+ * const books = allRecords.filter(r => r.typeName === 'book')
492
+ * ```
493
+ *
494
+ * @returns An array containing all records in the store
371
495
  * @public
372
496
  */
373
497
  allRecords() {
374
498
  return Array.from(this.records.values());
375
499
  }
376
500
  /**
377
- * Removes all records from the store.
501
+ * Remove all records from the store.
502
+ *
503
+ * @example
504
+ * ```ts
505
+ * store.clear()
506
+ * console.log(store.allRecords().length) // 0
507
+ * ```
378
508
  *
379
509
  * @public
380
510
  */
@@ -382,11 +512,20 @@ class Store {
382
512
  this.remove(Array.from(this.records.keys()));
383
513
  }
384
514
  /**
385
- * Update a record. To update multiple records at once, use the `update` method of the
386
- * `TypedStore` class.
515
+ * Update a single record using an updater function. To update multiple records at once,
516
+ * use the `update` method of the `TypedStore` class.
387
517
  *
388
- * @param id - The id of the record to update.
389
- * @param updater - A function that updates the record.
518
+ * @example
519
+ * ```ts
520
+ * store.update(book.id, (book) => ({
521
+ * ...book,
522
+ * title: 'Updated Title'
523
+ * }))
524
+ * ```
525
+ *
526
+ * @param id - The ID of the record to update
527
+ * @param updater - A function that receives the current record and returns the updated record
528
+ * @public
390
529
  */
391
530
  update(id, updater) {
392
531
  const existing = this.unsafeGetWithoutCapture(id);
@@ -397,20 +536,47 @@ class Store {
397
536
  this.put([updater(existing)]);
398
537
  }
399
538
  /**
400
- * Get whether the record store has a id.
539
+ * Check whether a record with the given ID exists in the store.
401
540
  *
402
- * @param id - The id of the record to check.
541
+ * @example
542
+ * ```ts
543
+ * if (store.has(bookId)) {
544
+ * console.log('Book exists!')
545
+ * }
546
+ * ```
547
+ *
548
+ * @param id - The ID of the record to check
549
+ * @returns True if the record exists, false otherwise
403
550
  * @public
404
551
  */
405
552
  has(id) {
406
553
  return this.records.has(id);
407
554
  }
408
555
  /**
409
- * Add a new listener to the store.
556
+ * Add a listener that will be called when the store changes.
557
+ * Returns a function to remove the listener.
558
+ *
559
+ * @example
560
+ * ```ts
561
+ * const removeListener = store.listen((entry) => {
562
+ * console.log('Changes:', entry.changes)
563
+ * console.log('Source:', entry.source)
564
+ * })
565
+ *
566
+ * // Listen only to user changes to document records
567
+ * const removeDocumentListener = store.listen(
568
+ * (entry) => console.log('Document changed:', entry),
569
+ * { source: 'user', scope: 'document' }
570
+ * )
410
571
  *
411
- * @param onHistory - The listener to call when the store updates.
412
- * @param filters - Filters to apply to the listener.
413
- * @returns A function to remove the listener.
572
+ * // Later, remove the listener
573
+ * removeListener()
574
+ * ```
575
+ *
576
+ * @param onHistory - The listener function to call when changes occur
577
+ * @param filters - Optional filters to control when the listener is called
578
+ * @returns A function that removes the listener when called
579
+ * @public
414
580
  */
415
581
  listen(onHistory, filters) {
416
582
  this._flushHistory();
@@ -435,9 +601,19 @@ class Store {
435
601
  }
436
602
  isMergingRemoteChanges = false;
437
603
  /**
438
- * Merge changes from a remote source
604
+ * Merge changes from a remote source. Changes made within the provided function
605
+ * will be marked with source 'remote' instead of 'user'.
606
+ *
607
+ * @example
608
+ * ```ts
609
+ * // Changes from sync/collaboration
610
+ * store.mergeRemoteChanges(() => {
611
+ * store.put(remoteRecords)
612
+ * store.remove(deletedIds)
613
+ * })
614
+ * ```
439
615
  *
440
- * @param fn - A function that merges the external changes.
616
+ * @param fn - A function that applies the remote changes
441
617
  * @public
442
618
  */
443
619
  mergeRemoteChanges(fn) {
@@ -666,26 +842,44 @@ function squashHistoryEntries(entries) {
666
842
  class HistoryAccumulator {
667
843
  _history = [];
668
844
  _interceptors = /* @__PURE__ */ new Set();
845
+ /**
846
+ * Add an interceptor that will be called for each history entry.
847
+ * Returns a function to remove the interceptor.
848
+ */
669
849
  addInterceptor(fn) {
670
850
  this._interceptors.add(fn);
671
851
  return () => {
672
852
  this._interceptors.delete(fn);
673
853
  };
674
854
  }
855
+ /**
856
+ * Add a history entry to the accumulator.
857
+ * Calls all registered interceptors with the entry.
858
+ */
675
859
  add(entry) {
676
860
  this._history.push(entry);
677
861
  for (const interceptor of this._interceptors) {
678
862
  interceptor(entry);
679
863
  }
680
864
  }
865
+ /**
866
+ * Flush all accumulated history entries, squashing adjacent entries from the same source.
867
+ * Clears the internal history buffer.
868
+ */
681
869
  flush() {
682
870
  const history = squashHistoryEntries(this._history);
683
871
  this._history = [];
684
872
  return history;
685
873
  }
874
+ /**
875
+ * Clear all accumulated history entries without flushing.
876
+ */
686
877
  clear() {
687
878
  this._history = [];
688
879
  }
880
+ /**
881
+ * Check if there are any accumulated history entries.
882
+ */
689
883
  hasChanges() {
690
884
  return this._history.length > 0;
691
885
  }