@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.
- package/dist-cjs/index.d.ts +1 -49
- package/dist-cjs/index.js +1 -3
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/ImmutableMap.js +23 -25
- package/dist-cjs/lib/ImmutableMap.js.map +2 -2
- package/dist-cjs/lib/StoreSchema.js +24 -80
- package/dist-cjs/lib/StoreSchema.js.map +2 -2
- package/dist-cjs/lib/StoreSideEffects.js +1 -1
- package/dist-cjs/lib/StoreSideEffects.js.map +1 -1
- package/dist-cjs/lib/devFreeze.js +3 -5
- package/dist-cjs/lib/devFreeze.js.map +2 -2
- package/dist-cjs/lib/migrate.js.map +2 -2
- package/dist-esm/index.d.mts +1 -49
- package/dist-esm/index.mjs +1 -3
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/ImmutableMap.mjs +23 -25
- package/dist-esm/lib/ImmutableMap.mjs.map +2 -2
- package/dist-esm/lib/StoreSchema.mjs +25 -83
- package/dist-esm/lib/StoreSchema.mjs.map +2 -2
- package/dist-esm/lib/StoreSideEffects.mjs +1 -1
- package/dist-esm/lib/StoreSideEffects.mjs.map +1 -1
- package/dist-esm/lib/devFreeze.mjs +3 -5
- package/dist-esm/lib/devFreeze.mjs.map +2 -2
- package/dist-esm/lib/migrate.mjs.map +2 -2
- package/package.json +4 -4
- package/src/index.ts +0 -3
- package/src/lib/ImmutableMap.ts +33 -25
- package/src/lib/StoreSchema.ts +30 -90
- package/src/lib/StoreSideEffects.ts +1 -1
- package/src/lib/devFreeze.test.ts +2 -6
- package/src/lib/devFreeze.ts +3 -7
- package/src/lib/migrate.ts +0 -29
- package/src/lib/test/recordStore.test.ts +0 -182
- package/dist-cjs/lib/AtomSet.js +0 -68
- package/dist-cjs/lib/AtomSet.js.map +0 -7
- package/dist-cjs/lib/isDev.js +0 -37
- package/dist-cjs/lib/isDev.js.map +0 -7
- package/dist-esm/lib/AtomSet.mjs +0 -48
- package/dist-esm/lib/AtomSet.mjs.map +0 -7
- package/dist-esm/lib/isDev.mjs +0 -16
- package/dist-esm/lib/isDev.mjs.map +0 -7
- package/src/lib/AtomSet.ts +0 -52
- package/src/lib/isDev.ts +0 -20
package/src/lib/ImmutableMap.ts
CHANGED
|
@@ -174,10 +174,20 @@ function SetRef(ref?: Ref): void {
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
523
|
-
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
772
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
iteratorResult = {
|
|
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
|
|
package/src/lib/StoreSchema.ts
CHANGED
|
@@ -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 {
|
|
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 (
|
|
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.
|
|
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:
|
|
616
|
+
return { type: 'success', value: store }
|
|
679
617
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
)
|
|
618
|
+
|
|
619
|
+
if (!opts?.mutateInputStore) {
|
|
620
|
+
store = structuredClone(store)
|
|
621
|
+
}
|
|
622
|
+
|
|
687
623
|
try {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
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,
|
|
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
|
-
|
|
16
|
+
vi.stubGlobal('process', { env: { NODE_ENV: 'production' } })
|
|
21
17
|
})
|
|
22
18
|
|
|
23
19
|
it('should return objects unchanged in production mode', () => {
|
package/src/lib/devFreeze.ts
CHANGED
|
@@ -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 (
|
|
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
|
|
package/src/lib/migrate.ts
CHANGED
|
@@ -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', () => {
|
package/dist-cjs/lib/AtomSet.js
DELETED
|
@@ -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
|