@contember/bindx 0.1.46 → 0.1.48
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/adapter/MockAdapter.d.ts +4 -1
- package/dist/adapter/MockAdapter.d.ts.map +1 -1
- package/dist/core/ActionDispatcher.d.ts +4 -1
- package/dist/core/ActionDispatcher.d.ts.map +1 -1
- package/dist/core/actions.d.ts +7 -1
- package/dist/core/actions.d.ts.map +1 -1
- package/dist/events/EventEmitter.d.ts +13 -1
- package/dist/events/EventEmitter.d.ts.map +1 -1
- package/dist/handles/BaseHandle.d.ts +14 -3
- package/dist/handles/BaseHandle.d.ts.map +1 -1
- package/dist/handles/EntityHandle.d.ts +14 -5
- package/dist/handles/EntityHandle.d.ts.map +1 -1
- package/dist/handles/FieldHandle.d.ts +11 -4
- package/dist/handles/FieldHandle.d.ts.map +1 -1
- package/dist/handles/HasManyListHandle.d.ts +36 -2
- package/dist/handles/HasManyListHandle.d.ts.map +1 -1
- package/dist/handles/HasOneHandle.d.ts +52 -9
- package/dist/handles/HasOneHandle.d.ts.map +1 -1
- package/dist/handles/PlaceholderHandle.d.ts +25 -13
- package/dist/handles/PlaceholderHandle.d.ts.map +1 -1
- package/dist/handles/index.d.ts +1 -1
- package/dist/handles/index.d.ts.map +1 -1
- package/dist/handles/proxyFactory.d.ts +7 -1
- package/dist/handles/proxyFactory.d.ts.map +1 -1
- package/dist/handles/types.d.ts +33 -0
- package/dist/handles/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/persistence/BatchPersister.d.ts +53 -37
- package/dist/persistence/BatchPersister.d.ts.map +1 -1
- package/dist/persistence/ChangeRegistry.d.ts +15 -0
- package/dist/persistence/ChangeRegistry.d.ts.map +1 -1
- package/dist/persistence/MutationCollector.d.ts +106 -3
- package/dist/persistence/MutationCollector.d.ts.map +1 -1
- package/dist/persistence/PersistExecution.d.ts +26 -0
- package/dist/persistence/PersistExecution.d.ts.map +1 -0
- package/dist/persistence/types.d.ts +8 -2
- package/dist/persistence/types.d.ts.map +1 -1
- package/dist/store/EntityMetaStore.d.ts +32 -10
- package/dist/store/EntityMetaStore.d.ts.map +1 -1
- package/dist/store/EntitySnapshotStore.d.ts +50 -3
- package/dist/store/EntitySnapshotStore.d.ts.map +1 -1
- package/dist/store/ErrorStore.d.ts +3 -2
- package/dist/store/ErrorStore.d.ts.map +1 -1
- package/dist/store/HasManyStore.d.ts +214 -0
- package/dist/store/HasManyStore.d.ts.map +1 -0
- package/dist/store/HasOneStore.d.ts +159 -0
- package/dist/store/HasOneStore.d.ts.map +1 -0
- package/dist/store/ReachabilityAnalyzer.d.ts +19 -0
- package/dist/store/ReachabilityAnalyzer.d.ts.map +1 -1
- package/dist/store/RekeyOrchestrator.d.ts +76 -0
- package/dist/store/RekeyOrchestrator.d.ts.map +1 -0
- package/dist/store/RelationEdgeIndex.d.ts +34 -0
- package/dist/store/RelationEdgeIndex.d.ts.map +1 -0
- package/dist/store/RelationStore.d.ts +74 -137
- package/dist/store/RelationStore.d.ts.map +1 -1
- package/dist/store/RootRegistry.d.ts +25 -4
- package/dist/store/RootRegistry.d.ts.map +1 -1
- package/dist/store/SnapshotStore.d.ts +137 -13
- package/dist/store/SnapshotStore.d.ts.map +1 -1
- package/dist/store/SubscriptionManager.d.ts +63 -14
- package/dist/store/SubscriptionManager.d.ts.map +1 -1
- package/dist/store/TouchedStore.d.ts +3 -2
- package/dist/store/TouchedStore.d.ts.map +1 -1
- package/dist/store/relationKey.d.ts +15 -0
- package/dist/store/relationKey.d.ts.map +1 -0
- package/dist/undo/UndoJournal.d.ts +133 -0
- package/dist/undo/UndoJournal.d.ts.map +1 -0
- package/dist/undo/UndoManager.d.ts +25 -82
- package/dist/undo/UndoManager.d.ts.map +1 -1
- package/dist/undo/UnrecordedWriteError.d.ts +16 -0
- package/dist/undo/UnrecordedWriteError.d.ts.map +1 -0
- package/dist/undo/index.d.ts +3 -1
- package/dist/undo/index.d.ts.map +1 -1
- package/dist/undo/rekeyJournalEntry.d.ts +20 -0
- package/dist/undo/rekeyJournalEntry.d.ts.map +1 -0
- package/dist/undo/types.d.ts +0 -48
- package/dist/undo/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/adapter/MockAdapter.ts +15 -5
- package/src/core/ActionDispatcher.ts +56 -36
- package/src/core/actions.ts +8 -1
- package/src/events/EventEmitter.ts +78 -24
- package/src/handles/BaseHandle.ts +19 -2
- package/src/handles/EntityHandle.ts +48 -17
- package/src/handles/FieldHandle.ts +36 -10
- package/src/handles/HasManyListHandle.ts +120 -16
- package/src/handles/HasOneHandle.ts +177 -44
- package/src/handles/PlaceholderHandle.ts +184 -36
- package/src/handles/index.ts +3 -0
- package/src/handles/proxyFactory.ts +27 -0
- package/src/handles/types.ts +39 -0
- package/src/index.ts +4 -4
- package/src/persistence/BatchPersister.ts +810 -446
- package/src/persistence/ChangeRegistry.ts +27 -3
- package/src/persistence/MutationCollector.ts +265 -31
- package/src/persistence/PersistExecution.ts +217 -0
- package/src/persistence/types.ts +8 -2
- package/src/store/EntityMetaStore.ts +89 -45
- package/src/store/EntitySnapshotStore.ts +102 -19
- package/src/store/ErrorStore.ts +6 -5
- package/src/store/HasManyStore.ts +810 -0
- package/src/store/HasOneStore.ts +454 -0
- package/src/store/ReachabilityAnalyzer.ts +37 -0
- package/src/store/RekeyOrchestrator.ts +123 -0
- package/src/store/RelationEdgeIndex.ts +79 -0
- package/src/store/RelationStore.ts +176 -725
- package/src/store/RootRegistry.ts +50 -8
- package/src/store/SnapshotStore.ts +572 -101
- package/src/store/SubscriptionManager.ts +122 -75
- package/src/store/TouchedStore.ts +5 -2
- package/src/store/relationKey.ts +20 -0
- package/src/undo/UndoJournal.ts +258 -0
- package/src/undo/UndoManager.ts +151 -281
- package/src/undo/UnrecordedWriteError.ts +23 -0
- package/src/undo/index.ts +8 -7
- package/src/undo/rekeyJournalEntry.ts +145 -0
- package/src/undo/types.ts +0 -58
- package/dist/core/actionClassification.d.ts +0 -48
- package/dist/core/actionClassification.d.ts.map +0 -1
- package/src/core/actionClassification.ts +0 -199
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
type ReconciliationResult = 'applied' | 'conflict';
|
|
2
|
+
/**
|
|
3
|
+
* Removal type for has-many items
|
|
4
|
+
*/
|
|
5
|
+
export type HasManyRemovalType = 'disconnect' | 'delete';
|
|
6
|
+
/**
|
|
7
|
+
* Kind of a planned has-many addition:
|
|
8
|
+
* - 'created': a newly created entity (via add())
|
|
9
|
+
* - 'connected': an existing persisted entity being connected (via connect())
|
|
10
|
+
*/
|
|
11
|
+
export type HasManyAdditionKind = 'created' | 'connected';
|
|
12
|
+
export interface SentHasManyAddition {
|
|
13
|
+
itemId: string;
|
|
14
|
+
kind: HasManyAdditionKind;
|
|
15
|
+
}
|
|
16
|
+
export interface SentHasManyRemoval {
|
|
17
|
+
itemId: string;
|
|
18
|
+
type: HasManyRemovalType;
|
|
19
|
+
}
|
|
20
|
+
export interface SentHasManyDelta {
|
|
21
|
+
additions: readonly SentHasManyAddition[];
|
|
22
|
+
removals: readonly SentHasManyRemoval[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Has-many list state stored in SnapshotStore
|
|
26
|
+
*/
|
|
27
|
+
export interface StoredHasManyState {
|
|
28
|
+
/** IDs of items from server */
|
|
29
|
+
serverIds: Set<string>;
|
|
30
|
+
/** Explicit ordered list of item IDs, null means use default order (serverIds + plannedAdditions) */
|
|
31
|
+
orderedIds: string[] | null;
|
|
32
|
+
/** Planned removals (disconnect or delete) keyed by entity ID */
|
|
33
|
+
plannedRemovals: Map<string, HasManyRemovalType>;
|
|
34
|
+
/**
|
|
35
|
+
* Planned additions (IDs to add to the list) keyed by entity ID, with the
|
|
36
|
+
* value distinguishing newly CREATED entities (add()) from existing PERSISTED
|
|
37
|
+
* entities being CONNECTED (connect()). The keys are exactly the connections;
|
|
38
|
+
* the keys whose value is 'created' are exactly the created entities, so the
|
|
39
|
+
* "created ⊆ connections" invariant is structural.
|
|
40
|
+
*/
|
|
41
|
+
plannedAdditions: Map<string, HasManyAdditionKind>;
|
|
42
|
+
version: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Computes the default ordered IDs for a has-many relation.
|
|
46
|
+
* Order is: serverIds (minus removals) + plannedAdditions
|
|
47
|
+
*/
|
|
48
|
+
export declare function computeDefaultOrderedIds(state: StoredHasManyState): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Owns has-many list state ("parentType:parentId:fieldName" → {@link StoredHasManyState}).
|
|
51
|
+
*
|
|
52
|
+
* Has its own monotonic {@link mutationVersion} bumped on every actual write
|
|
53
|
+
* (funnelled through {@link writeHasMany} plus the delete/clear paths); the
|
|
54
|
+
* facade sums this with the has-one counter for {@link ReachabilityAnalyzer}.
|
|
55
|
+
*/
|
|
56
|
+
export declare class HasManyStore {
|
|
57
|
+
/** Has-many list states keyed by "parentType:parentId:fieldName" */
|
|
58
|
+
private readonly hasManyStates;
|
|
59
|
+
/**
|
|
60
|
+
* Bidirectional live-edge index, maintained by {@link writeHasMany} /
|
|
61
|
+
* {@link deleteHasMany} so the parent↔child queries are O(degree) and the two
|
|
62
|
+
* directions stay consistent by construction.
|
|
63
|
+
*/
|
|
64
|
+
private readonly edges;
|
|
65
|
+
private mutationVersion;
|
|
66
|
+
/**
|
|
67
|
+
* Monotonic counter bumped on EDITABLE-layer has-many writes (plan add/remove,
|
|
68
|
+
* add, connectExisting, remove, move, reset) — the writes an undo gesture must
|
|
69
|
+
* first record. Server- id ingestion (setHasManyServerIds), materialization
|
|
70
|
+
* (getOrCreateHasMany), post-persist commit, import, replaceEntityId, rekey and
|
|
71
|
+
* delete deliberately do NOT bump it. Read by the undo write-guard (see UndoJournal).
|
|
72
|
+
*/
|
|
73
|
+
private editableWriteVersion;
|
|
74
|
+
getMutationVersion(): number;
|
|
75
|
+
getEditableWriteVersion(): number;
|
|
76
|
+
/**
|
|
77
|
+
* The single write chokepoint. Reconciles the edge index by diffing the live
|
|
78
|
+
* members of the previous state against the next, so every state-changing path
|
|
79
|
+
* (server ids / planned add/remove / move / import / replaceEntityId / ...)
|
|
80
|
+
* keeps the index correct without tracking the reverse direction itself.
|
|
81
|
+
*/
|
|
82
|
+
private writeHasMany;
|
|
83
|
+
/**
|
|
84
|
+
* The single delete chokepoint — removes an entry and its live edges. Used by
|
|
85
|
+
* the bulk remove and rekey-owner paths so they don't leak edges.
|
|
86
|
+
*/
|
|
87
|
+
private deleteHasMany;
|
|
88
|
+
/**
|
|
89
|
+
* Gets or creates has-many list state.
|
|
90
|
+
*/
|
|
91
|
+
getOrCreateHasMany(key: string, serverIds?: string[]): StoredHasManyState;
|
|
92
|
+
/**
|
|
93
|
+
* Gets has-many list state.
|
|
94
|
+
*/
|
|
95
|
+
getHasMany(key: string): StoredHasManyState | undefined;
|
|
96
|
+
/**
|
|
97
|
+
* Sets server IDs for a has-many relation.
|
|
98
|
+
*/
|
|
99
|
+
setHasManyServerIds(key: string, serverIds: string[]): void;
|
|
100
|
+
/**
|
|
101
|
+
* Plans a removal for a has-many item.
|
|
102
|
+
*/
|
|
103
|
+
planHasManyRemoval(key: string, itemId: string, type: HasManyRemovalType): void;
|
|
104
|
+
/**
|
|
105
|
+
* Plans a connection for a has-many item.
|
|
106
|
+
*/
|
|
107
|
+
planHasManyConnection(key: string, itemId: string): void;
|
|
108
|
+
/**
|
|
109
|
+
* Commits has-many state after successful persist.
|
|
110
|
+
*/
|
|
111
|
+
commitHasMany(key: string, newServerIds: string[]): void;
|
|
112
|
+
/**
|
|
113
|
+
* Advances the server baseline by the confirmed sent delta and rebases local
|
|
114
|
+
* edits made after the request started onto that new baseline.
|
|
115
|
+
*/
|
|
116
|
+
reconcileSentDelta(key: string, delta: SentHasManyDelta): ReconciliationResult;
|
|
117
|
+
/**
|
|
118
|
+
* Resets has-many state to server state (clears planned operations).
|
|
119
|
+
*/
|
|
120
|
+
resetHasMany(key: string): void;
|
|
121
|
+
/**
|
|
122
|
+
* Adds a newly created entity to a has-many relation.
|
|
123
|
+
* Used by HasManyListHandle.add() for inline entity creation.
|
|
124
|
+
*/
|
|
125
|
+
addToHasMany(key: string, itemId: string): void;
|
|
126
|
+
/**
|
|
127
|
+
* Connects an existing (persisted) entity to a has-many relation.
|
|
128
|
+
* Unlike addToHasMany, records the addition as 'connected' (not 'created') —
|
|
129
|
+
* used for materializing embedded connect references to existing entities.
|
|
130
|
+
*/
|
|
131
|
+
connectExistingToHasMany(key: string, itemId: string): void;
|
|
132
|
+
/**
|
|
133
|
+
* Removes an entity from a has-many relation.
|
|
134
|
+
* For newly created entities (via add()), cancels the connection.
|
|
135
|
+
* For existing server entities, plans the specified removal type.
|
|
136
|
+
* Returns true if the state changed (caller should notify), false if it was a no-op.
|
|
137
|
+
*/
|
|
138
|
+
removeFromHasMany(key: string, itemId: string, removalType: HasManyRemovalType): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Moves an item within a has-many relation from one index to another.
|
|
141
|
+
*/
|
|
142
|
+
moveInHasMany(key: string, fromIndex: number, toIndex: number): void;
|
|
143
|
+
/**
|
|
144
|
+
* Gets the ordered list of item IDs for a has-many relation.
|
|
145
|
+
*/
|
|
146
|
+
getHasManyOrderedIds(key: string): string[];
|
|
147
|
+
/**
|
|
148
|
+
* Collects the ids of child entities reachable through LIVE has-many edges
|
|
149
|
+
* (key prefix "parentType:parentId:") — an O(degree) read of the edge index.
|
|
150
|
+
*/
|
|
151
|
+
collectLiveChildIds(keyPrefix: string, ids: Set<string>): void;
|
|
152
|
+
/**
|
|
153
|
+
* Adds the composite parent keys of every LIVE has-many edge containing
|
|
154
|
+
* {@link childId} — an O(degree) read of the edge index, the exact reverse of
|
|
155
|
+
* {@link collectLiveChildIds}.
|
|
156
|
+
*/
|
|
157
|
+
collectParentKeysForChild(childId: string, parents: Set<string>): void;
|
|
158
|
+
/**
|
|
159
|
+
* Collects the keys of every has-many list owned by an entity (keys under the
|
|
160
|
+
* given owner prefix). Mirrors {@link removeOwnedRelations} but only enumerates.
|
|
161
|
+
*/
|
|
162
|
+
collectOwnedKeys(keyPrefix: string, keys: string[]): void;
|
|
163
|
+
/**
|
|
164
|
+
* Removes a single has-many list state (and its live edges). Used by undo
|
|
165
|
+
* restore to drop a list that did not exist before the gesture.
|
|
166
|
+
*/
|
|
167
|
+
removeHasMany(key: string): void;
|
|
168
|
+
/**
|
|
169
|
+
* Removes all has-many state owned by an entity (keys under the given owner
|
|
170
|
+
* prefix), dropping each entry's edges through {@link deleteHasMany}.
|
|
171
|
+
*/
|
|
172
|
+
removeOwnedRelations(keyPrefix: string): void;
|
|
173
|
+
/**
|
|
174
|
+
* Commits all has-many relations for an entity.
|
|
175
|
+
*
|
|
176
|
+
* `pendingItems` (by relation key) names planned additions/removals that were NOT
|
|
177
|
+
* sent — they stay planned instead of being folded into the server baseline.
|
|
178
|
+
*/
|
|
179
|
+
commitAllRelations(keyPrefix: string, pendingItems?: ReadonlyMap<string, ReadonlySet<string>>): void;
|
|
180
|
+
/**
|
|
181
|
+
* Resets all has-many relations for an entity to server state.
|
|
182
|
+
*/
|
|
183
|
+
resetAllRelations(keyPrefix: string): void;
|
|
184
|
+
/**
|
|
185
|
+
* Collects the field names of dirty has-many relations for an entity.
|
|
186
|
+
*/
|
|
187
|
+
collectDirtyRelations(keyPrefix: string, dirtyRelations: string[]): void;
|
|
188
|
+
/**
|
|
189
|
+
* Exports has-many states for given keys.
|
|
190
|
+
*/
|
|
191
|
+
exportHasManyStates(keys: string[]): Map<string, StoredHasManyState>;
|
|
192
|
+
/**
|
|
193
|
+
* Imports has-many states from a snapshot.
|
|
194
|
+
* Returns the keys that were imported for notification.
|
|
195
|
+
*/
|
|
196
|
+
importHasManyStates(states: Map<string, StoredHasManyState>): string[];
|
|
197
|
+
/**
|
|
198
|
+
* Replaces all occurrences of oldId with newId across has-many states
|
|
199
|
+
* (serverIds, orderedIds, plannedAdditions, plannedRemovals).
|
|
200
|
+
*/
|
|
201
|
+
replaceEntityId(oldId: string, newId: string): void;
|
|
202
|
+
/**
|
|
203
|
+
* Rekeys has-many entries owned by an entity (changes the parent ID in the key).
|
|
204
|
+
* Routing through {@link deleteHasMany} + {@link writeHasMany} migrates the edge
|
|
205
|
+
* index from the old parent key to the new one for free.
|
|
206
|
+
*/
|
|
207
|
+
rekeyOwner(oldKeyPrefix: string, newKeyPrefix: string): void;
|
|
208
|
+
/**
|
|
209
|
+
* Clears all has-many relation data.
|
|
210
|
+
*/
|
|
211
|
+
clear(): void;
|
|
212
|
+
}
|
|
213
|
+
export {};
|
|
214
|
+
//# sourceMappingURL=HasManyStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HasManyStore.d.ts","sourceRoot":"","sources":["../../src/store/HasManyStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { HasOneRelationState } from '../handles/types.js';
|
|
2
|
+
import type { EntitySnapshot } from './snapshots.js';
|
|
3
|
+
/**
|
|
4
|
+
* Relation state stored in SnapshotStore
|
|
5
|
+
*/
|
|
6
|
+
export interface StoredRelationState {
|
|
7
|
+
currentId: string | null;
|
|
8
|
+
serverId: string | null;
|
|
9
|
+
state: HasOneRelationState;
|
|
10
|
+
serverState: HasOneRelationState;
|
|
11
|
+
placeholderData: Record<string, unknown>;
|
|
12
|
+
version: number;
|
|
13
|
+
}
|
|
14
|
+
type ReconciliationResult = 'applied' | 'conflict';
|
|
15
|
+
export type SentHasOneTransition = {
|
|
16
|
+
operation: 'connect';
|
|
17
|
+
targetId: string;
|
|
18
|
+
} | {
|
|
19
|
+
operation: 'create';
|
|
20
|
+
targetId: string;
|
|
21
|
+
} | {
|
|
22
|
+
operation: 'disconnect';
|
|
23
|
+
targetId: string;
|
|
24
|
+
} | {
|
|
25
|
+
operation: 'delete';
|
|
26
|
+
targetId: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Owns has-one relation state ("parentType:parentId:fieldName" → {@link StoredRelationState}).
|
|
30
|
+
*
|
|
31
|
+
* Has its own monotonic {@link mutationVersion} bumped on every actual write
|
|
32
|
+
* (funnelled through {@link writeRelation} plus the delete/clear paths); the
|
|
33
|
+
* facade sums this with the has-many counter for {@link ReachabilityAnalyzer}.
|
|
34
|
+
*/
|
|
35
|
+
export declare class HasOneStore {
|
|
36
|
+
/** Relation states keyed by "parentType:parentId:fieldName" */
|
|
37
|
+
private readonly relationStates;
|
|
38
|
+
/**
|
|
39
|
+
* Bidirectional live-edge index, maintained by {@link writeRelation} /
|
|
40
|
+
* {@link deleteRelation} so the parent↔child queries are O(degree) and the two
|
|
41
|
+
* directions stay consistent by construction.
|
|
42
|
+
*/
|
|
43
|
+
private readonly edges;
|
|
44
|
+
private mutationVersion;
|
|
45
|
+
/**
|
|
46
|
+
* Monotonic counter bumped on EDITABLE-layer has-one writes (setRelation /
|
|
47
|
+
* resetRelation) — the writes an undo gesture must first record. Materialization
|
|
48
|
+
* (getOrCreateRelation), post-persist commit, import, replaceEntityId, rekey and
|
|
49
|
+
* delete deliberately do NOT bump it. Read by the undo write-guard (see UndoJournal).
|
|
50
|
+
*/
|
|
51
|
+
private editableWriteVersion;
|
|
52
|
+
getMutationVersion(): number;
|
|
53
|
+
getEditableWriteVersion(): number;
|
|
54
|
+
/**
|
|
55
|
+
* The single write chokepoint. Reconciles the edge index by diffing the live
|
|
56
|
+
* child of the previous state against the next, so every state-changing path
|
|
57
|
+
* (set/commit/reset/import/replaceEntityId/...) keeps the index correct without
|
|
58
|
+
* tracking the reverse direction itself.
|
|
59
|
+
*/
|
|
60
|
+
private writeRelation;
|
|
61
|
+
/**
|
|
62
|
+
* The single delete chokepoint — removes an entry and its live edge. Used by
|
|
63
|
+
* the bulk remove and rekey-owner paths so they don't leak edges.
|
|
64
|
+
*/
|
|
65
|
+
private deleteRelation;
|
|
66
|
+
/**
|
|
67
|
+
* Gets or creates relation state.
|
|
68
|
+
*/
|
|
69
|
+
getOrCreateRelation(key: string, initial: Omit<StoredRelationState, 'version'>): StoredRelationState;
|
|
70
|
+
/**
|
|
71
|
+
* Gets relation state.
|
|
72
|
+
*/
|
|
73
|
+
getRelation(key: string): StoredRelationState | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Updates relation state.
|
|
76
|
+
* If the relation state doesn't exist, creates it using entity snapshot for server data.
|
|
77
|
+
*/
|
|
78
|
+
setRelation(key: string, updates: Partial<Omit<StoredRelationState, 'version'>>, entitySnapshot: EntitySnapshot | undefined, fieldName: string): void;
|
|
79
|
+
/**
|
|
80
|
+
* Commits relation state (server = current).
|
|
81
|
+
*/
|
|
82
|
+
commitRelation(key: string): void;
|
|
83
|
+
/**
|
|
84
|
+
* Advances the server baseline by one confirmed sent transition while keeping
|
|
85
|
+
* edits made after the request started as pending local state.
|
|
86
|
+
*/
|
|
87
|
+
reconcileSentTransition(key: string, transition: SentHasOneTransition): ReconciliationResult;
|
|
88
|
+
/**
|
|
89
|
+
* Resets relation to server state.
|
|
90
|
+
*/
|
|
91
|
+
resetRelation(key: string): void;
|
|
92
|
+
/**
|
|
93
|
+
* Collects the ids of child entities reachable through LIVE has-one edges
|
|
94
|
+
* (key prefix "parentType:parentId:") — an O(degree) read of the edge index.
|
|
95
|
+
*/
|
|
96
|
+
collectLiveChildIds(keyPrefix: string, ids: Set<string>): void;
|
|
97
|
+
/**
|
|
98
|
+
* Adds the composite parent keys of every LIVE has-one edge pointing at
|
|
99
|
+
* {@link childId} — an O(degree) read of the edge index, the exact reverse of
|
|
100
|
+
* {@link collectLiveChildIds}.
|
|
101
|
+
*/
|
|
102
|
+
collectParentKeysForChild(childId: string, parents: Set<string>): void;
|
|
103
|
+
/**
|
|
104
|
+
* Collects the keys of every has-one relation owned by an entity (keys under the
|
|
105
|
+
* given owner prefix). Mirrors {@link removeOwnedRelations} but only enumerates.
|
|
106
|
+
*/
|
|
107
|
+
collectOwnedKeys(keyPrefix: string, keys: string[]): void;
|
|
108
|
+
/**
|
|
109
|
+
* Removes a single has-one relation state (and its live edge). Used by undo
|
|
110
|
+
* restore to drop a relation that did not exist before the gesture.
|
|
111
|
+
*/
|
|
112
|
+
removeRelation(key: string): void;
|
|
113
|
+
/**
|
|
114
|
+
* Removes all has-one state owned by an entity (keys under the given owner
|
|
115
|
+
* prefix), dropping each entry's edge through {@link deleteRelation}.
|
|
116
|
+
*/
|
|
117
|
+
removeOwnedRelations(keyPrefix: string): void;
|
|
118
|
+
/**
|
|
119
|
+
* Commits all has-one relations for an entity.
|
|
120
|
+
*
|
|
121
|
+
* A relation whose planned op targets an item in `pendingItems` (by relation key)
|
|
122
|
+
* was not sent and is left uncommitted, so the op is retried on the next persist.
|
|
123
|
+
*/
|
|
124
|
+
commitAllRelations(keyPrefix: string, pendingItems?: ReadonlyMap<string, ReadonlySet<string>>): void;
|
|
125
|
+
/**
|
|
126
|
+
* Resets all has-one relations for an entity to server state.
|
|
127
|
+
*/
|
|
128
|
+
resetAllRelations(keyPrefix: string): void;
|
|
129
|
+
/**
|
|
130
|
+
* Collects the field names of dirty has-one relations for an entity.
|
|
131
|
+
*/
|
|
132
|
+
collectDirtyRelations(keyPrefix: string, dirtyRelations: string[]): void;
|
|
133
|
+
/**
|
|
134
|
+
* Exports relation states for given keys.
|
|
135
|
+
*/
|
|
136
|
+
exportRelationStates(keys: string[]): Map<string, StoredRelationState>;
|
|
137
|
+
/**
|
|
138
|
+
* Imports relation states from a snapshot.
|
|
139
|
+
* Returns the keys that were imported for notification.
|
|
140
|
+
*/
|
|
141
|
+
importRelationStates(states: Map<string, StoredRelationState>): string[];
|
|
142
|
+
/**
|
|
143
|
+
* Replaces all occurrences of oldId with newId across has-one relation states
|
|
144
|
+
* (currentId, serverId).
|
|
145
|
+
*/
|
|
146
|
+
replaceEntityId(oldId: string, newId: string): void;
|
|
147
|
+
/**
|
|
148
|
+
* Rekeys has-one entries owned by an entity (changes the parent ID in the key).
|
|
149
|
+
* Routing through {@link deleteRelation} + {@link writeRelation} migrates the
|
|
150
|
+
* edge index from the old parent key to the new one for free.
|
|
151
|
+
*/
|
|
152
|
+
rekeyOwner(oldKeyPrefix: string, newKeyPrefix: string): void;
|
|
153
|
+
/**
|
|
154
|
+
* Clears all has-one relation data.
|
|
155
|
+
*/
|
|
156
|
+
clear(): void;
|
|
157
|
+
}
|
|
158
|
+
export {};
|
|
159
|
+
//# sourceMappingURL=HasOneStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HasOneStore.d.ts","sourceRoot":"","sources":["../../src/store/HasOneStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,14 @@ import type { RootRegistry } from './RootRegistry.js';
|
|
|
17
17
|
* The graph is read from {@link RelationStore} (the source of truth for live
|
|
18
18
|
* relation membership), never from the subscription parent-child registry,
|
|
19
19
|
* which is append-mostly for notifications and does not reflect disconnects.
|
|
20
|
+
*
|
|
21
|
+
* The walk is O(E+R) and runs on every dirty check and post-persist sweep, so
|
|
22
|
+
* its result is memoized. Each sub-store the walk reads exposes a monotonic
|
|
23
|
+
* `getMutationVersion()` that bumps only when graph-relevant state changes
|
|
24
|
+
* (entity key set, `existsOnServer`/`isPersisting`, roots, relation edges).
|
|
25
|
+
* Their sum is a cache key: it is strictly increasing, so an unchanged sum
|
|
26
|
+
* proves nothing relevant changed and the cached set can be returned. Pure
|
|
27
|
+
* field edits do not bump any counter, keeping the cache warm on the hot path.
|
|
20
28
|
*/
|
|
21
29
|
export declare class ReachabilityAnalyzer {
|
|
22
30
|
private readonly entitySnapshots;
|
|
@@ -24,10 +32,21 @@ export declare class ReachabilityAnalyzer {
|
|
|
24
32
|
private readonly relations;
|
|
25
33
|
private readonly roots;
|
|
26
34
|
constructor(entitySnapshots: EntitySnapshotStore, meta: EntityMetaStore, relations: RelationStore, roots: RootRegistry);
|
|
35
|
+
private cache;
|
|
27
36
|
/**
|
|
28
37
|
* Returns the set of entity keys ("entityType:id") for created
|
|
29
38
|
* (never-persisted) entities reachable from a root through live relations.
|
|
39
|
+
*
|
|
40
|
+
* The returned set is the cached instance and is owned by the analyzer —
|
|
41
|
+
* callers must treat it as read-only (the current consumers only call
|
|
42
|
+
* `.has(...)` on it).
|
|
30
43
|
*/
|
|
31
44
|
computeReachableCreated(): Set<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Sum of the graph-relevant mutation counters across the sub-stores the walk
|
|
47
|
+
* reads. Monotonic, so an unchanged value means no relevant mutation happened.
|
|
48
|
+
*/
|
|
49
|
+
private graphVersion;
|
|
50
|
+
private walk;
|
|
32
51
|
}
|
|
33
52
|
//# sourceMappingURL=ReachabilityAnalyzer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReachabilityAnalyzer.d.ts","sourceRoot":"","sources":["../../src/store/ReachabilityAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD
|
|
1
|
+
{"version":3,"file":"ReachabilityAnalyzer.d.ts","sourceRoot":"","sources":["../../src/store/ReachabilityAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,oBAAoB;IAE/B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAHL,eAAe,EAAE,mBAAmB,EACpC,IAAI,EAAE,eAAe,EACrB,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,YAAY;IAGrC,OAAO,CAAC,KAAK,CAAwD;IAErE;;;;;;;OAOG;IACH,uBAAuB,IAAI,GAAG,CAAC,MAAM,CAAC;IAYtC;;;OAGG;IACH,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,IAAI;CA0DZ"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes a single temp→persisted rekey in every shape the participating
|
|
3
|
+
* sub-stores need (full key, owner prefix, bare id).
|
|
4
|
+
*/
|
|
5
|
+
export interface RekeyContext {
|
|
6
|
+
/** Old composite key, "entityType:tempId". */
|
|
7
|
+
readonly oldKey: string;
|
|
8
|
+
/** New composite key, "entityType:persistedId". */
|
|
9
|
+
readonly newKey: string;
|
|
10
|
+
/** Old owner prefix, "entityType:tempId:". */
|
|
11
|
+
readonly oldKeyPrefix: string;
|
|
12
|
+
/** New owner prefix, "entityType:persistedId:". */
|
|
13
|
+
readonly newKeyPrefix: string;
|
|
14
|
+
/** The temp id being replaced. */
|
|
15
|
+
readonly oldId: string;
|
|
16
|
+
/** The server-assigned id. */
|
|
17
|
+
readonly newId: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A store that owns per-entity state keyed (directly or by prefix) on an entity
|
|
21
|
+
* id and can migrate it from a temp id to its persisted id.
|
|
22
|
+
*/
|
|
23
|
+
export interface Rekeyable {
|
|
24
|
+
rekey(ctx: RekeyContext): void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Single source of temp→persisted identity, and the one place the rekey fan-out
|
|
28
|
+
* is sequenced.
|
|
29
|
+
*
|
|
30
|
+
* Owns the only id-redirect map: "entityType:tempId" → persistedId. Both key
|
|
31
|
+
* resolution ({@link resolveKey}/{@link resolveId}) and persisted-id queries
|
|
32
|
+
* ({@link getPersistedId}/{@link isNewEntity}) derive from it, so there is no
|
|
33
|
+
* second copy to keep in sync — the former `SnapshotStore.rekeyedEntities` and
|
|
34
|
+
* `EntityMetaStore.tempToPersistedId` are both gone. `SubscriptionManager` keeps
|
|
35
|
+
* its own closure-redirect chain, which tracks relation-key prefixes and stale
|
|
36
|
+
* unsubscribe closures rather than entity identity, so it stays internal there.
|
|
37
|
+
*/
|
|
38
|
+
export declare class RekeyOrchestrator {
|
|
39
|
+
/** "entityType:tempId" → persistedId. The single identity-redirect map. */
|
|
40
|
+
private readonly tempToPersisted;
|
|
41
|
+
private readonly participants;
|
|
42
|
+
private readonly participantSet;
|
|
43
|
+
/**
|
|
44
|
+
* @param participants the sub-stores to migrate, in the exact order
|
|
45
|
+
* {@link rekey} must visit them (the order is load-bearing — see rekey()).
|
|
46
|
+
*/
|
|
47
|
+
constructor(participants: readonly Rekeyable[]);
|
|
48
|
+
/** Adds a participant once, preserving registration order. */
|
|
49
|
+
registerParticipant(participant: Rekeyable): void;
|
|
50
|
+
/** Resolves an entity key, following a temp→persisted redirect if present. */
|
|
51
|
+
resolveKey(entityType: string, id: string): string;
|
|
52
|
+
/** Resolves an entity id to its persisted id if it has been rekeyed. */
|
|
53
|
+
resolveId(entityType: string, id: string): string;
|
|
54
|
+
/** The persisted id for an entity, or null if it has none yet. */
|
|
55
|
+
getPersistedId(entityType: string, id: string): string | null;
|
|
56
|
+
/** Whether an entity has never been persisted (no server-assigned id yet). */
|
|
57
|
+
isNewEntity(entityType: string, id: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Migrates an entity from its temp id to its server-assigned id across every
|
|
60
|
+
* participating store.
|
|
61
|
+
*
|
|
62
|
+
* Ordering contract (do not reorder — each step relies on the previous):
|
|
63
|
+
* 0. register the redirect, so any resolveKey/resolveId during the fan-out
|
|
64
|
+
* already sees the persisted key;
|
|
65
|
+
* 1. roots — keep the root registry aligned;
|
|
66
|
+
* 2. entity snapshot — move data, rewrite the id field and id index;
|
|
67
|
+
* 3. meta — move metadata/load/persisting, then mark exists-on-server;
|
|
68
|
+
* 4. subscriptions — move entity + relation subscribers and parent links;
|
|
69
|
+
* 5. relations — rekey owned relation keys, then replace value references;
|
|
70
|
+
* 6. errors / touched / propagation — move the remaining per-entity state.
|
|
71
|
+
* The caller performs the final notification on the new key.
|
|
72
|
+
*/
|
|
73
|
+
rekey(entityType: string, tempId: string, persistedId: string): void;
|
|
74
|
+
clear(): void;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=RekeyOrchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RekeyOrchestrator.d.ts","sourceRoot":"","sources":["../../src/store/RekeyOrchestrator.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,8BAA8B;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACzB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAAA;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAiB;IAC7B,2EAA2E;IAC3E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA4B;IAC5D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkB;IAC/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAuB;IAEtD;;;OAGG;gBACS,YAAY,EAAE,SAAS,SAAS,EAAE;IAI9C,8DAA8D;IAC9D,mBAAmB,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI;IAMjD,8EAA8E;IAC9E,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM;IAKlD,wEAAwE;IACxE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM;IAIjD,kEAAkE;IAClE,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAM7D,8EAA8E;IAC9E,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAMpD;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAiBpE,KAAK,IAAI,IAAI;CAGb"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bidirectional index of LIVE relation edges between a parent entity
|
|
3
|
+
* ("parentType:parentId") and a child entity id (a bare id).
|
|
4
|
+
*
|
|
5
|
+
* Both directions are updated together by {@link addEdge}/{@link removeEdge}, so
|
|
6
|
+
* the forward query ({@link collectChildren}) and the reverse query
|
|
7
|
+
* ({@link collectParents}) can never disagree — that was the failure mode of the
|
|
8
|
+
* old append-only `childToParents` map, which was updated on connect but not on
|
|
9
|
+
* disconnect. Both queries are O(degree), not O(total edges).
|
|
10
|
+
*
|
|
11
|
+
* Edges are reference-counted: one parent entity may reach the same child through
|
|
12
|
+
* more than one relation field (e.g. `author` and `coauthor`, or two has-many
|
|
13
|
+
* fields), so an edge survives until the last field that contributes it drops it.
|
|
14
|
+
*
|
|
15
|
+
* The index stores only the LIVE edge set; turning relation state (server ids,
|
|
16
|
+
* planned additions/removals, has-one state) into that set is the owning
|
|
17
|
+
* sub-store's job, done once per write by diffing the previous against the next
|
|
18
|
+
* live set (see {@link HasOneStore}/{@link HasManyStore}). The index itself knows
|
|
19
|
+
* nothing about liveness rules.
|
|
20
|
+
*/
|
|
21
|
+
export declare class RelationEdgeIndex {
|
|
22
|
+
/** parentKey ("parentType:parentId") → childId → refcount */
|
|
23
|
+
private readonly forward;
|
|
24
|
+
/** childId → parentKey → refcount */
|
|
25
|
+
private readonly reverse;
|
|
26
|
+
addEdge(parentKey: string, childId: string): void;
|
|
27
|
+
removeEdge(parentKey: string, childId: string): void;
|
|
28
|
+
/** Adds the live child ids of {@link parentKey} to {@link out}. */
|
|
29
|
+
collectChildren(parentKey: string, out: Set<string>): void;
|
|
30
|
+
/** Adds the parent keys that hold a live edge to {@link childId} to {@link out}. */
|
|
31
|
+
collectParents(childId: string, out: Set<string>): void;
|
|
32
|
+
clear(): void;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=RelationEdgeIndex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RelationEdgeIndex.d.ts","sourceRoot":"","sources":["../../src/store/RelationEdgeIndex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,iBAAiB;IAC7B,6DAA6D;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;IACjE,qCAAqC;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;IAEjE,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAKjD,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAKpD,mEAAmE;IACnE,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI;IAO1D,oFAAoF;IACpF,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI;IAOvD,KAAK,IAAI,IAAI;CAIb"}
|