@tldraw/store 4.2.2 → 4.2.3

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 (43) hide show
  1. package/dist-cjs/index.d.ts +1 -49
  2. package/dist-cjs/index.js +1 -3
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/ImmutableMap.js +23 -25
  5. package/dist-cjs/lib/ImmutableMap.js.map +2 -2
  6. package/dist-cjs/lib/StoreSchema.js +24 -80
  7. package/dist-cjs/lib/StoreSchema.js.map +2 -2
  8. package/dist-cjs/lib/StoreSideEffects.js +1 -1
  9. package/dist-cjs/lib/StoreSideEffects.js.map +1 -1
  10. package/dist-cjs/lib/devFreeze.js +3 -5
  11. package/dist-cjs/lib/devFreeze.js.map +2 -2
  12. package/dist-cjs/lib/migrate.js.map +2 -2
  13. package/dist-esm/index.d.mts +1 -49
  14. package/dist-esm/index.mjs +1 -3
  15. package/dist-esm/index.mjs.map +2 -2
  16. package/dist-esm/lib/ImmutableMap.mjs +23 -25
  17. package/dist-esm/lib/ImmutableMap.mjs.map +2 -2
  18. package/dist-esm/lib/StoreSchema.mjs +25 -83
  19. package/dist-esm/lib/StoreSchema.mjs.map +2 -2
  20. package/dist-esm/lib/StoreSideEffects.mjs +1 -1
  21. package/dist-esm/lib/StoreSideEffects.mjs.map +1 -1
  22. package/dist-esm/lib/devFreeze.mjs +3 -5
  23. package/dist-esm/lib/devFreeze.mjs.map +2 -2
  24. package/dist-esm/lib/migrate.mjs.map +2 -2
  25. package/package.json +4 -4
  26. package/src/index.ts +0 -3
  27. package/src/lib/ImmutableMap.ts +33 -25
  28. package/src/lib/StoreSchema.ts +30 -90
  29. package/src/lib/StoreSideEffects.ts +1 -1
  30. package/src/lib/devFreeze.test.ts +2 -6
  31. package/src/lib/devFreeze.ts +3 -7
  32. package/src/lib/migrate.ts +0 -29
  33. package/src/lib/test/recordStore.test.ts +0 -182
  34. package/dist-cjs/lib/AtomSet.js +0 -68
  35. package/dist-cjs/lib/AtomSet.js.map +0 -7
  36. package/dist-cjs/lib/isDev.js +0 -37
  37. package/dist-cjs/lib/isDev.js.map +0 -7
  38. package/dist-esm/lib/AtomSet.mjs +0 -48
  39. package/dist-esm/lib/AtomSet.mjs.map +0 -7
  40. package/dist-esm/lib/isDev.mjs +0 -16
  41. package/dist-esm/lib/isDev.mjs.map +0 -7
  42. package/src/lib/AtomSet.ts +0 -52
  43. package/src/lib/isDev.ts +0 -20
@@ -174,10 +174,20 @@ function SetRef(ref?: Ref): void {
174
174
  }
175
175
  }
176
176
 
177
- function arrCopy<I>(arr: Array<I>, offset = 0): Array<I> {
178
- return arr.slice(offset)
177
+ // http://jsperf.com/copy-array-inline
178
+ function arrCopy<I>(arr: Array<I>, offset?: number): Array<I> {
179
+ offset = offset || 0
180
+ const len = Math.max(0, arr.length - offset)
181
+ const newArr: Array<I> = new Array(len)
182
+ for (let ii = 0; ii < len; ii++) {
183
+ // We may want to guard for undefined values with `if (arr[ii + offset] !== undefined`, but ths should not happen by design
184
+ newArr[ii] = arr[ii + offset]
185
+ }
186
+ return newArr
179
187
  }
180
188
 
189
+ const is = Object.is
190
+
181
191
  class OwnerID {}
182
192
 
183
193
  /**
@@ -471,7 +481,7 @@ class ArrayMapNode<K, V> {
471
481
  get(_shift: unknown, _keyHash: unknown, key: K, notSetValue?: V) {
472
482
  const entries = this.entries
473
483
  for (let ii = 0, len = entries.length; ii < len; ii++) {
474
- if (Object.is(key, entries[ii][0])) {
484
+ if (is(key, entries[ii][0])) {
475
485
  return entries[ii][1]
476
486
  }
477
487
  }
@@ -493,7 +503,7 @@ class ArrayMapNode<K, V> {
493
503
  let idx = 0
494
504
  const len = entries.length
495
505
  for (; idx < len; idx++) {
496
- if (Object.is(key, entries[idx][0])) {
506
+ if (is(key, entries[idx][0])) {
497
507
  break
498
508
  }
499
509
  }
@@ -504,7 +514,8 @@ class ArrayMapNode<K, V> {
504
514
  }
505
515
 
506
516
  SetRef(didAlter)
507
- if (removed || !exists) SetRef(didChangeSize)
517
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- TODO enable eslint here
518
+ ;(removed || !exists) && SetRef(didChangeSize)
508
519
 
509
520
  if (removed && entries.length === 1) {
510
521
  return // undefined
@@ -519,11 +530,8 @@ class ArrayMapNode<K, V> {
519
530
 
520
531
  if (exists) {
521
532
  if (removed) {
522
- if (idx === len - 1) {
523
- newEntries.pop()
524
- } else {
525
- newEntries[idx] = newEntries.pop()!
526
- }
533
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- TODO enable eslint here
534
+ idx === len - 1 ? newEntries.pop() : (newEntries[idx] = newEntries.pop()!)
527
535
  } else {
528
536
  newEntries[idx] = [key, value]
529
537
  }
@@ -711,7 +719,7 @@ class HashCollisionNode<K, V> {
711
719
  get(shift: number, keyHash: number, key: K, notSetValue?: V) {
712
720
  const entries = this.entries
713
721
  for (let ii = 0, len = entries.length; ii < len; ii++) {
714
- if (Object.is(key, entries[ii][0])) {
722
+ if (is(key, entries[ii][0])) {
715
723
  return entries[ii][1]
716
724
  }
717
725
  }
@@ -746,7 +754,7 @@ class HashCollisionNode<K, V> {
746
754
  let idx = 0
747
755
  const len = entries.length
748
756
  for (; idx < len; idx++) {
749
- if (Object.is(key, entries[idx][0])) {
757
+ if (is(key, entries[idx][0])) {
750
758
  break
751
759
  }
752
760
  }
@@ -757,7 +765,8 @@ class HashCollisionNode<K, V> {
757
765
  }
758
766
 
759
767
  SetRef(didAlter)
760
- if (removed || !exists) SetRef(didChangeSize)
768
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- TODO enable eslint here
769
+ ;(removed || !exists) && SetRef(didChangeSize)
761
770
 
762
771
  if (removed && len === 2) {
763
772
  return new ValueNode(ownerID, this.keyHash, entries[idx ^ 1])
@@ -768,11 +777,8 @@ class HashCollisionNode<K, V> {
768
777
 
769
778
  if (exists) {
770
779
  if (removed) {
771
- if (idx === len - 1) {
772
- newEntries.pop()
773
- } else {
774
- newEntries[idx] = newEntries.pop()!
775
- }
780
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- TODO enable eslint here
781
+ idx === len - 1 ? newEntries.pop() : (newEntries[idx] = newEntries.pop()!)
776
782
  } else {
777
783
  newEntries[idx] = [key, value]
778
784
  }
@@ -797,7 +803,7 @@ class ValueNode<K, V> {
797
803
  ) {}
798
804
 
799
805
  get(shift: number, keyHash: number, key: K, notSetValue?: V) {
800
- return Object.is(key, this.entry[0]) ? this.entry[1] : notSetValue
806
+ return is(key, this.entry[0]) ? this.entry[1] : notSetValue
801
807
  }
802
808
 
803
809
  update(
@@ -810,7 +816,7 @@ class ValueNode<K, V> {
810
816
  didAlter?: Ref
811
817
  ) {
812
818
  const removed = value === NOT_SET
813
- const keyMatch = Object.is(key, this.entry[0])
819
+ const keyMatch = is(key, this.entry[0])
814
820
  if (keyMatch ? value === this.entry[1] : removed) {
815
821
  return this
816
822
  }
@@ -921,11 +927,13 @@ function iteratorValue<K, V>(
921
927
  iteratorResult?: IteratorResult<any>
922
928
  ) {
923
929
  const value = type === ITERATE_KEYS ? k : type === ITERATE_VALUES ? v : [k, v]
924
- if (iteratorResult) {
925
- iteratorResult.value = value
926
- } else {
927
- iteratorResult = { value, done: false }
928
- }
930
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- TODO enable eslint here
931
+ iteratorResult
932
+ ? (iteratorResult.value = value)
933
+ : (iteratorResult = {
934
+ value: value,
935
+ done: false,
936
+ })
929
937
  return iteratorResult
930
938
  }
931
939
 
@@ -1,14 +1,13 @@
1
1
  import {
2
+ Result,
2
3
  assert,
3
4
  exhaustiveSwitchError,
4
5
  getOwnProperty,
5
- isEqual,
6
- objectMapEntries,
7
- Result,
8
6
  structuredClone,
9
7
  } from '@tldraw/utils'
10
8
  import { UnknownRecord } from './BaseRecord'
11
- import { devFreeze } from './devFreeze'
9
+ import { RecordType } from './RecordType'
10
+ import { SerializedStore, Store, StoreSnapshot } from './Store'
12
11
  import {
13
12
  Migration,
14
13
  MigrationFailureReason,
@@ -17,11 +16,8 @@ import {
17
16
  MigrationSequence,
18
17
  parseMigrationId,
19
18
  sortMigrations,
20
- SynchronousStorage,
21
19
  validateMigrations,
22
20
  } from './migrate'
23
- import { RecordType } from './RecordType'
24
- import { SerializedStore, Store, StoreSnapshot } from './Store'
25
21
 
26
22
  /**
27
23
  * Version 1 format for serialized store schema information.
@@ -534,7 +530,7 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
534
530
  return { type: 'success', value: record }
535
531
  }
536
532
 
537
- if (!migrationsToApply.every((m) => m.scope === 'record')) {
533
+ if (migrationsToApply.some((m) => m.scope === 'store')) {
538
534
  return {
539
535
  type: 'error',
540
536
  reason:
@@ -545,7 +541,7 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
545
541
  }
546
542
 
547
543
  if (direction === 'down') {
548
- if (!migrationsToApply.every((m) => m.scope === 'record' && m.down)) {
544
+ if (!migrationsToApply.every((m) => m.down)) {
549
545
  return {
550
546
  type: 'error',
551
547
  reason: MigrationFailureReason.TargetVersionTooOld,
@@ -558,7 +554,6 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
558
554
  try {
559
555
  for (const migration of migrationsToApply) {
560
556
  if (migration.scope === 'store') throw new Error(/* won't happen, just for TS */)
561
- if (migration.scope === 'storage') throw new Error(/* won't happen, just for TS */)
562
557
  const shouldApply = migration.filter ? migration.filter(record) : true
563
558
  if (!shouldApply) continue
564
559
  const result = migration[direction]!(record)
@@ -574,64 +569,6 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
574
569
  return { type: 'success', value: record }
575
570
  }
576
571
 
577
- migrateStorage(storage: SynchronousStorage<R>) {
578
- const schema = storage.getSchema()
579
- assert(schema, 'Schema is missing.')
580
-
581
- const migrations = this.getMigrationsSince(schema)
582
- if (!migrations.ok) {
583
- console.error('Error migrating store', migrations.error)
584
- throw new Error(migrations.error)
585
- }
586
- const migrationsToApply = migrations.value
587
- if (migrationsToApply.length === 0) {
588
- return
589
- }
590
-
591
- storage.setSchema(this.serialize())
592
-
593
- for (const migration of migrationsToApply) {
594
- if (migration.scope === 'record') {
595
- for (const [id, state] of storage.entries()) {
596
- const shouldApply = migration.filter ? migration.filter(state) : true
597
- if (!shouldApply) continue
598
- const record = structuredClone(state)
599
- const result = migration.up!(record as any) ?? record
600
- if (!isEqual(result, state)) {
601
- storage.set(id, result as R)
602
- }
603
- }
604
- } else if (migration.scope === 'store') {
605
- // legacy
606
- const prevStore = Object.fromEntries(storage.entries())
607
- let nextStore = structuredClone(prevStore)
608
- nextStore = (migration.up!(nextStore) as any) ?? nextStore
609
- for (const [id, state] of Object.entries(nextStore)) {
610
- if (!state) continue // these will be deleted in the next loop
611
- if (!isEqual(state, prevStore[id])) {
612
- storage.set(id, state)
613
- }
614
- }
615
- for (const id of Object.keys(prevStore)) {
616
- if (!nextStore[id]) {
617
- storage.delete(id)
618
- }
619
- }
620
- } else if (migration.scope === 'storage') {
621
- migration.up!(storage)
622
- } else {
623
- exhaustiveSwitchError(migration)
624
- }
625
- }
626
- // Clean up by filtering out any non-document records.
627
- // This is mainly legacy support for extremely early days tldraw.
628
- for (const [id, state] of storage.entries()) {
629
- if (this.getType(state.typeName).scope !== 'document') {
630
- storage.delete(id)
631
- }
632
- }
633
- }
634
-
635
572
  /**
636
573
  * Migrates an entire store snapshot to match the current schema version.
637
574
  *
@@ -667,6 +604,7 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
667
604
  snapshot: StoreSnapshot<R>,
668
605
  opts?: { mutateInputStore?: boolean }
669
606
  ): MigrationResult<SerializedStore<R>> {
607
+ let { store } = snapshot
670
608
  const migrations = this.getMigrationsSince(snapshot.schema)
671
609
  if (!migrations.ok) {
672
610
  // TODO: better error
@@ -675,37 +613,39 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
675
613
  }
676
614
  const migrationsToApply = migrations.value
677
615
  if (migrationsToApply.length === 0) {
678
- return { type: 'success', value: snapshot.store }
616
+ return { type: 'success', value: store }
679
617
  }
680
- const store = Object.assign(
681
- new Map<string, R>(objectMapEntries(snapshot.store).map(devFreeze)),
682
- {
683
- getSchema: () => snapshot.schema,
684
- setSchema: (_: SerializedSchema) => {},
685
- }
686
- )
618
+
619
+ if (!opts?.mutateInputStore) {
620
+ store = structuredClone(store)
621
+ }
622
+
687
623
  try {
688
- this.migrateStorage(store)
689
- if (opts?.mutateInputStore) {
690
- for (const [id, record] of store.entries()) {
691
- snapshot.store[id as keyof typeof snapshot.store] = record
692
- }
693
- for (const id of Object.keys(snapshot.store)) {
694
- if (!store.has(id)) {
695
- delete snapshot.store[id as keyof typeof snapshot.store]
624
+ for (const migration of migrationsToApply) {
625
+ if (migration.scope === 'record') {
626
+ for (const [id, record] of Object.entries(store)) {
627
+ const shouldApply = migration.filter ? migration.filter(record as UnknownRecord) : true
628
+ if (!shouldApply) continue
629
+ const result = migration.up!(record as any)
630
+ if (result) {
631
+ store[id as keyof typeof store] = result as any
632
+ }
696
633
  }
697
- }
698
- return { type: 'success', value: snapshot.store }
699
- } else {
700
- return {
701
- type: 'success',
702
- value: Object.fromEntries(store.entries()) as SerializedStore<R>,
634
+ } else if (migration.scope === 'store') {
635
+ const result = migration.up!(store)
636
+ if (result) {
637
+ store = result as any
638
+ }
639
+ } else {
640
+ exhaustiveSwitchError(migration)
703
641
  }
704
642
  }
705
643
  } catch (e) {
706
644
  console.error('Error migrating store', e)
707
645
  return { type: 'error', reason: MigrationFailureReason.MigrationError }
708
646
  }
647
+
648
+ return { type: 'success', value: store }
709
649
  }
710
650
 
711
651
  /**
@@ -493,7 +493,7 @@ export class StoreSideEffects<R extends UnknownRecord> {
493
493
  * ```ts
494
494
  * editor.sideEffects.registerAfterCreateHandler('page', (page, source) => {
495
495
  * // Automatically create a shape when a page is created
496
- * editor.createShape({
496
+ * editor.createShape<TLTextShape>({
497
497
  * id: createShapeId(),
498
498
  * type: 'text',
499
499
  * props: { richText: toRichText(page.name) },
@@ -1,12 +1,8 @@
1
- import { afterAll, beforeEach, describe, expect, it, MockedFunction, vi } from 'vitest'
1
+ import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
2
2
  import { devFreeze } from './devFreeze'
3
- import { isDev } from './isDev'
4
3
 
5
4
  // Mock process.env for testing
6
5
  const originalEnv = process.env.NODE_ENV
7
- vi.mock('./isDev', () => ({
8
- isDev: vi.fn(() => true),
9
- }))
10
6
 
11
7
  describe('devFreeze', () => {
12
8
  beforeEach(() => {
@@ -17,7 +13,7 @@ describe('devFreeze', () => {
17
13
  describe('production mode behavior', () => {
18
14
  beforeEach(() => {
19
15
  // Mock production environment
20
- ;(isDev as MockedFunction<typeof isDev>).mockReturnValue(false)
16
+ vi.stubGlobal('process', { env: { NODE_ENV: 'production' } })
21
17
  })
22
18
 
23
19
  it('should return objects unchanged in production mode', () => {
@@ -1,5 +1,4 @@
1
1
  import { STRUCTURED_CLONE_OBJECT_PROTOTYPE } from '@tldraw/utils'
2
- import { isDev } from './isDev'
3
2
 
4
3
  /**
5
4
  * Freeze an object when in development mode. Copied from
@@ -16,8 +15,9 @@ import { isDev } from './isDev'
16
15
  * @public
17
16
  */
18
17
  export function devFreeze<T>(object: T): T {
19
- if (!isDev()) return object
20
-
18
+ if (process.env.NODE_ENV === 'production') {
19
+ return object
20
+ }
21
21
  const proto = Object.getPrototypeOf(object)
22
22
  if (
23
23
  proto &&
@@ -32,10 +32,6 @@ export function devFreeze<T>(object: T): T {
32
32
  throw new Error('cannot include non-js data in a record')
33
33
  }
34
34
 
35
- if (Object.isFrozen(object)) {
36
- return object
37
- }
38
-
39
35
  // Retrieve the property names defined on object
40
36
  const propNames = Object.getOwnPropertyNames(object)
41
37
 
@@ -1,7 +1,6 @@
1
1
  import { assert, objectMapEntries } from '@tldraw/utils'
2
2
  import { UnknownRecord } from './BaseRecord'
3
3
  import { SerializedStore } from './Store'
4
- import { SerializedSchema } from './StoreSchema'
5
4
 
6
5
  function squashDependsOn(sequence: Array<Migration | StandaloneDependsOn>): Migration[] {
7
6
  const result: Migration[] = []
@@ -220,36 +219,8 @@ export type Migration = {
220
219
  newState: SerializedStore<UnknownRecord>
221
220
  ) => void | SerializedStore<UnknownRecord>
222
221
  }
223
- | {
224
- readonly scope: 'storage'
225
- // eslint-disable-next-line @typescript-eslint/method-signature-style
226
- readonly up: (storage: SynchronousRecordStorage<UnknownRecord>) => void
227
- readonly down?: never
228
- }
229
222
  )
230
223
 
231
- /**
232
- * Abstraction over the store that can be used to perform migrations.
233
- * @public
234
- */
235
- export interface SynchronousRecordStorage<R extends UnknownRecord> {
236
- get(id: string): R | undefined
237
- set(id: string, record: R): void
238
- delete(id: string): void
239
- keys(): Iterable<string>
240
- values(): Iterable<R>
241
- entries(): Iterable<[string, R]>
242
- }
243
-
244
- /**
245
- * Abstraction over the storage that can be used to perform migrations.
246
- * @public
247
- */
248
- export interface SynchronousStorage<R extends UnknownRecord> extends SynchronousRecordStorage<R> {
249
- getSchema(): SerializedSchema
250
- setSchema(schema: SerializedSchema): void
251
- }
252
-
253
224
  /**
254
225
  * Base interface for legacy migration information.
255
226
  *
@@ -883,188 +883,6 @@ describe('snapshots', () => {
883
883
  expect(up).toHaveBeenCalledTimes(1)
884
884
  expect(store2.get(Book.createId('lotr'))!.numPages).toBe(42)
885
885
  })
886
-
887
- it('migrates the snapshot with storage scope', () => {
888
- const snapshot1 = store.getStoreSnapshot()
889
- const up = vi.fn((storage: any) => {
890
- const book = storage.get('book:lotr')
891
- storage.set('book:lotr', { ...book, numPages: 42 })
892
- })
893
-
894
- expect((snapshot1.store as any)['book:lotr'].numPages).toBe(1000)
895
-
896
- const store2 = new Store({
897
- props: {},
898
- schema: StoreSchema.create<Book | Author>(
899
- {
900
- book: Book,
901
- author: Author,
902
- },
903
- {
904
- migrations: [
905
- createMigrationSequence({
906
- sequenceId: 'com.tldraw',
907
- retroactive: true,
908
- sequence: [
909
- {
910
- id: `com.tldraw/1`,
911
- scope: 'storage',
912
- up,
913
- },
914
- ],
915
- }),
916
- ],
917
- }
918
- ),
919
- })
920
-
921
- expect(() => {
922
- store2.loadStoreSnapshot(snapshot1)
923
- }).not.toThrow()
924
-
925
- expect(up).toHaveBeenCalledTimes(1)
926
- expect(store2.get(Book.createId('lotr'))!.numPages).toBe(42)
927
- })
928
-
929
- it('storage scope migration can delete records', () => {
930
- const snapshot1 = store.getStoreSnapshot()
931
- const up = vi.fn((storage: any) => {
932
- storage.delete('author:mcavoy')
933
- })
934
-
935
- expect((snapshot1.store as any)['author:mcavoy']).toBeDefined()
936
-
937
- const store2 = new Store({
938
- props: {},
939
- schema: StoreSchema.create<Book | Author>(
940
- {
941
- book: Book,
942
- author: Author,
943
- },
944
- {
945
- migrations: [
946
- createMigrationSequence({
947
- sequenceId: 'com.tldraw',
948
- retroactive: true,
949
- sequence: [
950
- {
951
- id: `com.tldraw/1`,
952
- scope: 'storage',
953
- up,
954
- },
955
- ],
956
- }),
957
- ],
958
- }
959
- ),
960
- })
961
-
962
- expect(() => {
963
- store2.loadStoreSnapshot(snapshot1)
964
- }).not.toThrow()
965
-
966
- expect(up).toHaveBeenCalledTimes(1)
967
- expect(store2.get(Author.createId('mcavoy'))).toBeUndefined()
968
- })
969
-
970
- it('storage scope migration can iterate records', () => {
971
- const snapshot1 = store.getStoreSnapshot()
972
- const up = vi.fn((storage: any) => {
973
- for (const [id, record] of storage.entries()) {
974
- if (record.typeName === 'book') {
975
- storage.set(id, { ...record, numPages: record.numPages + 100 })
976
- }
977
- }
978
- })
979
-
980
- expect((snapshot1.store as any)['book:lotr'].numPages).toBe(1000)
981
- expect((snapshot1.store as any)['book:hobbit'].numPages).toBe(300)
982
-
983
- const store2 = new Store({
984
- props: {},
985
- schema: StoreSchema.create<Book | Author>(
986
- {
987
- book: Book,
988
- author: Author,
989
- },
990
- {
991
- migrations: [
992
- createMigrationSequence({
993
- sequenceId: 'com.tldraw',
994
- retroactive: true,
995
- sequence: [
996
- {
997
- id: `com.tldraw/1`,
998
- scope: 'storage',
999
- up,
1000
- },
1001
- ],
1002
- }),
1003
- ],
1004
- }
1005
- ),
1006
- })
1007
-
1008
- expect(() => {
1009
- store2.loadStoreSnapshot(snapshot1)
1010
- }).not.toThrow()
1011
-
1012
- expect(up).toHaveBeenCalledTimes(1)
1013
- expect(store2.get(Book.createId('lotr'))!.numPages).toBe(1100)
1014
- expect(store2.get(Book.createId('hobbit'))!.numPages).toBe(400)
1015
- })
1016
-
1017
- it('storage scope migration can use values() and keys()', () => {
1018
- const snapshot1 = store.getStoreSnapshot()
1019
- const keysCollected: string[] = []
1020
- const valuesCollected: any[] = []
1021
-
1022
- const up = vi.fn((storage: any) => {
1023
- for (const key of storage.keys()) {
1024
- keysCollected.push(key)
1025
- }
1026
- for (const value of storage.values()) {
1027
- valuesCollected.push(value)
1028
- }
1029
- })
1030
-
1031
- const store2 = new Store({
1032
- props: {},
1033
- schema: StoreSchema.create<Book | Author>(
1034
- {
1035
- book: Book,
1036
- author: Author,
1037
- },
1038
- {
1039
- migrations: [
1040
- createMigrationSequence({
1041
- sequenceId: 'com.tldraw',
1042
- retroactive: true,
1043
- sequence: [
1044
- {
1045
- id: `com.tldraw/1`,
1046
- scope: 'storage',
1047
- up,
1048
- },
1049
- ],
1050
- }),
1051
- ],
1052
- }
1053
- ),
1054
- })
1055
-
1056
- expect(() => {
1057
- store2.loadStoreSnapshot(snapshot1)
1058
- }).not.toThrow()
1059
-
1060
- expect(up).toHaveBeenCalledTimes(1)
1061
- expect(keysCollected).toContain('book:lotr')
1062
- expect(keysCollected).toContain('book:hobbit')
1063
- expect(keysCollected).toContain('author:tolkein')
1064
- expect(keysCollected).toContain('author:mcavoy')
1065
- expect(keysCollected).toContain('author:cassidy')
1066
- expect(valuesCollected.length).toBe(5)
1067
- })
1068
886
  })
1069
887
 
1070
888
  describe('diffs', () => {
@@ -1,68 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var AtomSet_exports = {};
20
- __export(AtomSet_exports, {
21
- AtomSet: () => AtomSet
22
- });
23
- module.exports = __toCommonJS(AtomSet_exports);
24
- var import_AtomMap = require("./AtomMap");
25
- class AtomSet {
26
- constructor(name, keys) {
27
- this.name = name;
28
- const entries = keys ? Array.from(keys, (k) => [k, k]) : void 0;
29
- this.map = new import_AtomMap.AtomMap(name, entries);
30
- }
31
- map;
32
- add(value) {
33
- this.map.set(value, value);
34
- return this;
35
- }
36
- clear() {
37
- this.map.clear();
38
- }
39
- delete(value) {
40
- return this.map.delete(value);
41
- }
42
- forEach(callbackfn, thisArg) {
43
- for (const value of this) {
44
- callbackfn.call(thisArg, value, value, this);
45
- }
46
- }
47
- has(value) {
48
- return this.map.has(value);
49
- }
50
- // eslint-disable-next-line no-restricted-syntax
51
- get size() {
52
- return this.map.size;
53
- }
54
- entries() {
55
- return this.map.entries();
56
- }
57
- keys() {
58
- return this.map.keys();
59
- }
60
- values() {
61
- return this.map.keys();
62
- }
63
- [Symbol.iterator]() {
64
- return this.map.keys();
65
- }
66
- [Symbol.toStringTag] = "AtomSet";
67
- }
68
- //# sourceMappingURL=AtomSet.js.map