@contember/bindx 0.1.52 → 0.1.54
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/core/ActionDispatcher.d.ts.map +1 -1
- package/dist/core/EntityLoader.d.ts +7 -0
- package/dist/core/EntityLoader.d.ts.map +1 -1
- package/dist/core/actions.d.ts +1 -2
- package/dist/core/actions.d.ts.map +1 -1
- package/dist/dataview/types.d.ts +6 -6
- package/dist/dataview/types.d.ts.map +1 -1
- package/dist/handles/EntityHandle.d.ts.map +1 -1
- package/dist/handles/HasManyListHandle.d.ts +8 -0
- package/dist/handles/HasManyListHandle.d.ts.map +1 -1
- package/dist/handles/HasOneHandle.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/store/HasManyStore.d.ts +94 -78
- package/dist/store/HasManyStore.d.ts.map +1 -1
- package/dist/store/RelationStore.d.ts +21 -12
- package/dist/store/RelationStore.d.ts.map +1 -1
- package/dist/store/ResponseOccurrenceIndex.d.ts +65 -0
- package/dist/store/ResponseOccurrenceIndex.d.ts.map +1 -0
- package/dist/store/SnapshotStore.d.ts +49 -14
- package/dist/store/SnapshotStore.d.ts.map +1 -1
- package/dist/store/hasManyState.d.ts +164 -0
- package/dist/store/hasManyState.d.ts.map +1 -0
- package/dist/store/relationKey.d.ts +11 -0
- package/dist/store/relationKey.d.ts.map +1 -1
- package/dist/undo/UndoJournal.d.ts.map +1 -1
- package/dist/undo/rekeyJournalEntry.d.ts +4 -4
- package/dist/undo/rekeyJournalEntry.d.ts.map +1 -1
- package/dist/utils/aliasGenerator.d.ts +1 -1
- package/dist/utils/aliasGenerator.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/core/ActionDispatcher.ts +0 -1
- package/src/core/EntityLoader.ts +7 -0
- package/src/core/actions.ts +1 -3
- package/src/dataview/types.ts +6 -6
- package/src/handles/EntityHandle.ts +11 -0
- package/src/handles/HasManyListHandle.ts +15 -17
- package/src/handles/HasOneHandle.ts +4 -2
- package/src/index.ts +6 -0
- package/src/store/HasManyStore.ts +312 -488
- package/src/store/RelationStore.ts +60 -24
- package/src/store/ResponseOccurrenceIndex.ts +224 -0
- package/src/store/SnapshotStore.ts +144 -73
- package/src/store/hasManyState.ts +408 -0
- package/src/store/relationKey.ts +17 -0
- package/src/undo/UndoJournal.ts +3 -1
- package/src/undo/UndoManager.ts +1 -1
- package/src/undo/rekeyJournalEntry.ts +31 -43
- package/src/utils/aliasGenerator.ts +1 -1
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import type { EntitySnapshot, LoadStatus } from './snapshots.js'
|
|
2
2
|
import { createEntitySnapshot } from './snapshots.js'
|
|
3
3
|
import type { FieldError, FieldErrorFilter } from '../errors/types.js'
|
|
4
|
+
import type { SelectionMeta } from '../selection/types.js'
|
|
4
5
|
import { SubscriptionManager, type SnapshotVersionBumper, type SynchronousResult } from './SubscriptionManager.js'
|
|
5
6
|
import { ErrorStore } from './ErrorStore.js'
|
|
6
7
|
import {
|
|
7
8
|
RelationStore,
|
|
9
|
+
type HasManyRelationProjection,
|
|
8
10
|
type HasManyRemovalType,
|
|
11
|
+
type HasManyViewMembership,
|
|
12
|
+
type HasManyViewProjection,
|
|
9
13
|
type RelationReconciliationResult,
|
|
10
14
|
type SentHasManyDelta,
|
|
11
15
|
type SentHasOneTransition,
|
|
@@ -18,6 +22,7 @@ import { generateTempId } from './entityId.js'
|
|
|
18
22
|
import { DirtyTracker } from './DirtyTracker.js'
|
|
19
23
|
import { EntitySnapshotStore } from './EntitySnapshotStore.js'
|
|
20
24
|
import { RootRegistry } from './RootRegistry.js'
|
|
25
|
+
import { ResponseOccurrenceIndex, type RelationTargetResolver } from './ResponseOccurrenceIndex.js'
|
|
21
26
|
import { ReachabilityAnalyzer } from './ReachabilityAnalyzer.js'
|
|
22
27
|
import { RekeyOrchestrator } from './RekeyOrchestrator.js'
|
|
23
28
|
import type { RekeyContext, Rekeyable } from './RekeyOrchestrator.js'
|
|
@@ -32,7 +37,13 @@ import type {
|
|
|
32
37
|
} from '../undo/UndoJournal.js'
|
|
33
38
|
|
|
34
39
|
export type {
|
|
40
|
+
HasManyAdditionKind,
|
|
41
|
+
HasManyRelationProjection,
|
|
35
42
|
HasManyRemovalType,
|
|
43
|
+
HasManyView,
|
|
44
|
+
HasManyViewMembership,
|
|
45
|
+
HasManyViewProjection,
|
|
46
|
+
PlannedHasManyAddition,
|
|
36
47
|
RelationReconciliationResult,
|
|
37
48
|
SentHasManyAddition,
|
|
38
49
|
SentHasManyDelta,
|
|
@@ -83,6 +94,8 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
83
94
|
*/
|
|
84
95
|
private readonly lastPropagatedData = new Map<string, unknown>()
|
|
85
96
|
|
|
97
|
+
private readonly responseOccurrences = new ResponseOccurrenceIndex()
|
|
98
|
+
|
|
86
99
|
/**
|
|
87
100
|
* Optional write-journal. When set (by an attached UndoManager), mutating
|
|
88
101
|
* methods record editable-layer pre-images of the cells they touch so a gesture
|
|
@@ -309,6 +322,23 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
309
322
|
return newSnapshot as EntitySnapshot<T>
|
|
310
323
|
}
|
|
311
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Unites the occurrences of each entity within one server response, so the
|
|
327
|
+
* narrower of two occurrences cannot replace fields the wider one carried.
|
|
328
|
+
* Call it before any of the response is written. See {@link ResponseOccurrenceIndex}.
|
|
329
|
+
*/
|
|
330
|
+
indexServerResponse(entityType: string, data: unknown, selection: SelectionMeta, schema: RelationTargetResolver): void {
|
|
331
|
+
this.responseOccurrences.index(entityType, data, selection, schema)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* What to write for an entity occurrence of a server response: the union of
|
|
336
|
+
* its occurrences in that response, or the occurrence itself.
|
|
337
|
+
*/
|
|
338
|
+
resolveServerOccurrence(occurrence: Record<string, unknown>): Record<string, unknown> {
|
|
339
|
+
return this.responseOccurrences.resolve(occurrence)
|
|
340
|
+
}
|
|
341
|
+
|
|
312
342
|
/**
|
|
313
343
|
* Refreshes server data from a revalidation read while preserving the user's
|
|
314
344
|
* local dirty edits. See {@link EntitySnapshotStore.refreshServerData}.
|
|
@@ -530,25 +560,74 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
530
560
|
|
|
531
561
|
// ==================== Has-Many State (delegated to RelationStore) ====================
|
|
532
562
|
|
|
563
|
+
/**
|
|
564
|
+
* Declares what a view read under {@link alias} can leave out, so the store can tell
|
|
565
|
+
* a filtered view from one that merely orders or renames. Only the caller that
|
|
566
|
+
* selected the relation knows the params; the alias is a hash of them.
|
|
567
|
+
*/
|
|
568
|
+
declareHasManyViewMembership(
|
|
569
|
+
entityType: string,
|
|
570
|
+
fieldName: string,
|
|
571
|
+
alias: string,
|
|
572
|
+
membership: HasManyViewMembership,
|
|
573
|
+
): void {
|
|
574
|
+
this.relations.declareHasManyViewMembership(entityType, fieldName, alias, membership)
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Materializes the relation and one args-view of it, refreshing that view's
|
|
579
|
+
* server baseline when one is supplied.
|
|
580
|
+
*
|
|
581
|
+
* `alias` addresses a VIEW inside the relation state, never a key of its own:
|
|
582
|
+
* the state is always keyed by the schema field name, so everything that names
|
|
583
|
+
* the relation the way the schema and the server do finds it.
|
|
584
|
+
*/
|
|
533
585
|
getOrCreateHasMany(
|
|
534
586
|
parentType: string,
|
|
535
587
|
parentId: string,
|
|
536
588
|
fieldName: string,
|
|
537
589
|
serverIds?: string[],
|
|
538
590
|
alias?: string,
|
|
539
|
-
):
|
|
540
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
541
|
-
|
|
591
|
+
): void {
|
|
592
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
593
|
+
this.relations.getOrCreateHasMany(key, alias ?? fieldName, serverIds)
|
|
542
594
|
}
|
|
543
595
|
|
|
596
|
+
/** The relation as persistence and dirty tracking see it — server baseline unioned across views. */
|
|
544
597
|
getHasMany(
|
|
545
598
|
parentType: string,
|
|
546
599
|
parentId: string,
|
|
547
600
|
fieldName: string,
|
|
601
|
+
): HasManyRelationProjection | undefined {
|
|
602
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
603
|
+
return this.relations.getHasManyRelation(key)
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/** Whether the relation carries writes the next persist would send. */
|
|
607
|
+
hasPendingHasManyWrites(parentType: string, parentId: string, fieldName: string): boolean {
|
|
608
|
+
return this.relations.hasPendingHasManyWrites(this.getRelationKey(parentType, parentId, fieldName))
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/** Whether one view carries an order its user arranged rather than the default one. */
|
|
612
|
+
hasExplicitHasManyOrder(
|
|
613
|
+
parentType: string,
|
|
614
|
+
parentId: string,
|
|
615
|
+
fieldName: string,
|
|
616
|
+
alias?: string,
|
|
617
|
+
): boolean {
|
|
618
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
619
|
+
return this.relations.hasExplicitHasManyOrder(key, alias ?? fieldName)
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/** The relation as ONE mounted has-many handle sees it. */
|
|
623
|
+
getHasManyView(
|
|
624
|
+
parentType: string,
|
|
625
|
+
parentId: string,
|
|
626
|
+
fieldName: string,
|
|
548
627
|
alias?: string,
|
|
549
|
-
):
|
|
550
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
551
|
-
return this.relations.
|
|
628
|
+
): HasManyViewProjection {
|
|
629
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
630
|
+
return this.relations.getHasManyView(key, alias ?? fieldName)
|
|
552
631
|
}
|
|
553
632
|
|
|
554
633
|
setHasManyServerIds(
|
|
@@ -558,8 +637,8 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
558
637
|
serverIds: string[],
|
|
559
638
|
alias?: string,
|
|
560
639
|
): void {
|
|
561
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
562
|
-
this.relations.setHasManyServerIds(key, serverIds)
|
|
640
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
641
|
+
this.relations.setHasManyServerIds(key, alias ?? fieldName, serverIds)
|
|
563
642
|
this.notifyRelationSubscribers(key)
|
|
564
643
|
}
|
|
565
644
|
|
|
@@ -569,9 +648,8 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
569
648
|
fieldName: string,
|
|
570
649
|
itemId: string,
|
|
571
650
|
type: HasManyRemovalType,
|
|
572
|
-
alias?: string,
|
|
573
651
|
): void {
|
|
574
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
652
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
575
653
|
this.journal?.recordHasMany(key)
|
|
576
654
|
this.relations.planHasManyRemoval(key, itemId, type)
|
|
577
655
|
this.notifyRelationSubscribers(key)
|
|
@@ -581,10 +659,9 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
581
659
|
parentType: string,
|
|
582
660
|
parentId: string,
|
|
583
661
|
fieldName: string,
|
|
584
|
-
alias?: string,
|
|
585
662
|
): Map<string, HasManyRemovalType> | undefined {
|
|
586
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
587
|
-
return this.relations.
|
|
663
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
664
|
+
return this.relations.getHasManyRelation(key)?.plannedRemovals
|
|
588
665
|
}
|
|
589
666
|
|
|
590
667
|
/**
|
|
@@ -603,34 +680,22 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
603
680
|
itemId: string,
|
|
604
681
|
alias?: string,
|
|
605
682
|
): void {
|
|
606
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
683
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
607
684
|
this.journal?.recordHasMany(key)
|
|
608
|
-
this.relations.planHasManyConnection(key, itemId)
|
|
685
|
+
this.relations.planHasManyConnection(key, alias ?? fieldName, itemId)
|
|
609
686
|
this.notifyRelationSubscribers(key)
|
|
610
687
|
}
|
|
611
688
|
|
|
689
|
+
/** The connections the next persist will send — relation-level, not per view. */
|
|
612
690
|
getHasManyPlannedConnections(
|
|
613
691
|
parentType: string,
|
|
614
692
|
parentId: string,
|
|
615
693
|
fieldName: string,
|
|
616
|
-
alias?: string,
|
|
617
694
|
): Set<string> | undefined {
|
|
618
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
619
|
-
const
|
|
620
|
-
if (!
|
|
621
|
-
return new Set(
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
commitHasMany(
|
|
625
|
-
parentType: string,
|
|
626
|
-
parentId: string,
|
|
627
|
-
fieldName: string,
|
|
628
|
-
newServerIds: string[],
|
|
629
|
-
alias?: string,
|
|
630
|
-
): void {
|
|
631
|
-
const key = this.getRelationKey(parentType, parentId, alias ?? fieldName)
|
|
632
|
-
this.relations.commitHasMany(key, newServerIds)
|
|
633
|
-
this.notifyRelationSubscribers(key)
|
|
695
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
696
|
+
const relation = this.relations.getHasManyRelation(key)
|
|
697
|
+
if (!relation) return undefined
|
|
698
|
+
return new Set(relation.plannedAdditions.keys())
|
|
634
699
|
}
|
|
635
700
|
|
|
636
701
|
reconcileSentHasMany(
|
|
@@ -638,9 +703,8 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
638
703
|
parentId: string,
|
|
639
704
|
fieldName: string,
|
|
640
705
|
delta: SentHasManyDelta,
|
|
641
|
-
alias?: string,
|
|
642
706
|
): RelationReconciliationResult {
|
|
643
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
707
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
644
708
|
const result = this.relations.reconcileSentHasMany(key, delta)
|
|
645
709
|
this.notifyRelationSubscribers(key)
|
|
646
710
|
return result
|
|
@@ -650,9 +714,8 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
650
714
|
parentType: string,
|
|
651
715
|
parentId: string,
|
|
652
716
|
fieldName: string,
|
|
653
|
-
alias?: string,
|
|
654
717
|
): void {
|
|
655
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
718
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
656
719
|
this.journal?.recordHasMany(key)
|
|
657
720
|
this.relations.resetHasMany(key)
|
|
658
721
|
this.notifyRelationSubscribers(key)
|
|
@@ -665,9 +728,9 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
665
728
|
itemId: string,
|
|
666
729
|
alias?: string,
|
|
667
730
|
): void {
|
|
668
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
731
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
669
732
|
this.journal?.recordHasMany(key)
|
|
670
|
-
this.relations.addToHasMany(key, itemId)
|
|
733
|
+
this.relations.addToHasMany(key, alias ?? fieldName, itemId)
|
|
671
734
|
this.notifyRelationSubscribers(key)
|
|
672
735
|
}
|
|
673
736
|
|
|
@@ -679,7 +742,7 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
679
742
|
): void {
|
|
680
743
|
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
681
744
|
this.journal?.recordHasMany(key)
|
|
682
|
-
this.relations.connectExistingToHasMany(key, itemId)
|
|
745
|
+
this.relations.connectExistingToHasMany(key, fieldName, itemId)
|
|
683
746
|
this.notifyRelationSubscribers(key)
|
|
684
747
|
}
|
|
685
748
|
|
|
@@ -689,9 +752,8 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
689
752
|
fieldName: string,
|
|
690
753
|
itemId: string,
|
|
691
754
|
removalType: HasManyRemovalType,
|
|
692
|
-
alias?: string,
|
|
693
755
|
): void {
|
|
694
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
756
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
695
757
|
this.journal?.recordHasMany(key)
|
|
696
758
|
// Cancelling the add of a never-persisted child just removes it from the
|
|
697
759
|
// list; its now-unreachable snapshot is no longer reported as a `create` and
|
|
@@ -709,9 +771,9 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
709
771
|
toIndex: number,
|
|
710
772
|
alias?: string,
|
|
711
773
|
): void {
|
|
712
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
774
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
713
775
|
this.journal?.recordHasMany(key)
|
|
714
|
-
this.relations.moveInHasMany(key, fromIndex, toIndex)
|
|
776
|
+
this.relations.moveInHasMany(key, alias ?? fieldName, fromIndex, toIndex)
|
|
715
777
|
this.notifyRelationSubscribers(key)
|
|
716
778
|
}
|
|
717
779
|
|
|
@@ -721,8 +783,8 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
721
783
|
fieldName: string,
|
|
722
784
|
alias?: string,
|
|
723
785
|
): string[] {
|
|
724
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
725
|
-
return this.relations.getHasManyOrderedIds(key)
|
|
786
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
787
|
+
return this.relations.getHasManyOrderedIds(key, alias ?? fieldName)
|
|
726
788
|
}
|
|
727
789
|
|
|
728
790
|
getPresentationHasManyOrderedIds(
|
|
@@ -731,13 +793,13 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
731
793
|
fieldName: string,
|
|
732
794
|
alias?: string,
|
|
733
795
|
): string[] {
|
|
734
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
796
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
797
|
+
const view = alias ?? fieldName
|
|
735
798
|
if (!this.meta.isPessimisticInFlight(this.getEntityKey(parentType, parentId))) {
|
|
736
|
-
return this.relations.getHasManyOrderedIds(key)
|
|
799
|
+
return this.relations.getHasManyOrderedIds(key, view)
|
|
737
800
|
}
|
|
738
801
|
|
|
739
|
-
|
|
740
|
-
return state ? [...state.serverIds] : []
|
|
802
|
+
return [...this.relations.getHasManyView(key, view).serverIds]
|
|
741
803
|
}
|
|
742
804
|
|
|
743
805
|
isHasManyItemCreated(
|
|
@@ -745,24 +807,21 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
745
807
|
parentId: string,
|
|
746
808
|
fieldName: string,
|
|
747
809
|
itemId: string,
|
|
748
|
-
alias?: string,
|
|
749
810
|
): boolean {
|
|
750
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
751
|
-
|
|
752
|
-
return state?.plannedAdditions.get(itemId) === 'created'
|
|
811
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
812
|
+
return this.relations.getHasManyRelation(key)?.plannedAdditions.get(itemId) === 'created'
|
|
753
813
|
}
|
|
754
814
|
|
|
755
815
|
getHasManyCreatedEntities(
|
|
756
816
|
parentType: string,
|
|
757
817
|
parentId: string,
|
|
758
818
|
fieldName: string,
|
|
759
|
-
alias?: string,
|
|
760
819
|
): Set<string> | undefined {
|
|
761
|
-
const key = this.getRelationKey(parentType, parentId,
|
|
762
|
-
const
|
|
763
|
-
if (!
|
|
820
|
+
const key = this.getRelationKey(parentType, parentId, fieldName)
|
|
821
|
+
const relation = this.relations.getHasManyRelation(key)
|
|
822
|
+
if (!relation) return undefined
|
|
764
823
|
const created = new Set<string>()
|
|
765
|
-
for (const [id, kind] of
|
|
824
|
+
for (const [id, kind] of relation.plannedAdditions) {
|
|
766
825
|
if (kind === 'created') created.add(id)
|
|
767
826
|
}
|
|
768
827
|
return created
|
|
@@ -1161,25 +1220,19 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
1161
1220
|
* journal's rekey to rebase a pre-image when a just-persisted create became a
|
|
1162
1221
|
* permanent member of the list (membership rebase for sealed creates).
|
|
1163
1222
|
*/
|
|
1164
|
-
getLiveHasManyServerIds(relationKey: string):
|
|
1165
|
-
|
|
1166
|
-
return new Set(state?.serverIds ?? [])
|
|
1223
|
+
getLiveHasManyServerIds(relationKey: string): ReadonlyMap<string, ReadonlySet<string>> {
|
|
1224
|
+
return this.relations.collectHasManyViewServerIds(relationKey)
|
|
1167
1225
|
}
|
|
1168
1226
|
|
|
1169
1227
|
exportHasManyCell(key: string): HasManyCellImage {
|
|
1170
|
-
const state = this.relations.
|
|
1228
|
+
const state = this.relations.getHasManyState(key)
|
|
1171
1229
|
if (!state) return { kind: 'hasMany', key, present: false }
|
|
1172
1230
|
return {
|
|
1173
1231
|
kind: 'hasMany',
|
|
1174
1232
|
key,
|
|
1175
1233
|
present: true,
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
orderedIds: state.orderedIds ? [...state.orderedIds] : null,
|
|
1179
|
-
plannedRemovals: new Map(state.plannedRemovals),
|
|
1180
|
-
plannedAdditions: new Map(state.plannedAdditions),
|
|
1181
|
-
version: state.version,
|
|
1182
|
-
},
|
|
1234
|
+
// getHasMany already returns a deep copy; the image owns it outright.
|
|
1235
|
+
state,
|
|
1183
1236
|
}
|
|
1184
1237
|
}
|
|
1185
1238
|
|
|
@@ -1218,7 +1271,9 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
1218
1271
|
const image = this.exportHasManyCell(hasManyKey)
|
|
1219
1272
|
images.push(image)
|
|
1220
1273
|
if (image.state) {
|
|
1221
|
-
|
|
1274
|
+
for (const view of image.state.views.values()) {
|
|
1275
|
+
if (view.orderedIds) worklist.push(...view.orderedIds)
|
|
1276
|
+
}
|
|
1222
1277
|
for (const additionId of image.state.plannedAdditions.keys()) worklist.push(additionId)
|
|
1223
1278
|
}
|
|
1224
1279
|
}
|
|
@@ -1318,10 +1373,26 @@ export class SnapshotStore implements SnapshotVersionBumper, JournalTarget {
|
|
|
1318
1373
|
}]]))
|
|
1319
1374
|
} else {
|
|
1320
1375
|
const s = img.state!
|
|
1321
|
-
const live = this.relations.
|
|
1376
|
+
const live = this.relations.getHasManyState(img.key)
|
|
1377
|
+
// The editable layer (planned writes, manual ordering) comes from the image;
|
|
1378
|
+
// the server baseline always from the live state, which may have advanced
|
|
1379
|
+
// since the gesture. A view the image does not know did not exist before the
|
|
1380
|
+
// gesture — the gesture itself may have created it — so it had no arranged
|
|
1381
|
+
// order to restore and falls back to the default order of the restored writes.
|
|
1382
|
+
const views = new Map(live?.views ?? [])
|
|
1383
|
+
for (const [alias, view] of views) {
|
|
1384
|
+
const recorded = s.views.get(alias)
|
|
1385
|
+
views.set(alias, {
|
|
1386
|
+
serverIds: new Set(view.serverIds),
|
|
1387
|
+
orderedIds: recorded?.orderedIds ? [...recorded.orderedIds] : null,
|
|
1388
|
+
membership: view.membership,
|
|
1389
|
+
})
|
|
1390
|
+
}
|
|
1391
|
+
for (const [alias, recorded] of s.views) {
|
|
1392
|
+
if (!views.has(alias)) views.set(alias, recorded)
|
|
1393
|
+
}
|
|
1322
1394
|
this.relations.importHasManyStates(new Map([[img.key, {
|
|
1323
|
-
|
|
1324
|
-
orderedIds: s.orderedIds ? [...s.orderedIds] : null,
|
|
1395
|
+
views,
|
|
1325
1396
|
plannedRemovals: new Map(s.plannedRemovals),
|
|
1326
1397
|
plannedAdditions: new Map(s.plannedAdditions),
|
|
1327
1398
|
version: (live?.version ?? s.version) + 1,
|