@contember/bindx 0.1.29 → 0.1.31
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/ContemberAdapter.d.ts.map +1 -1
- package/dist/adapter/types.d.ts +4 -0
- package/dist/adapter/types.d.ts.map +1 -1
- package/dist/core/ActionDispatcher.d.ts.map +1 -1
- package/dist/core/actions.d.ts +4 -2
- package/dist/core/actions.d.ts.map +1 -1
- package/dist/handles/HasManyListHandle.d.ts.map +1 -1
- package/dist/handles/HasOneHandle.d.ts +10 -0
- package/dist/handles/HasOneHandle.d.ts.map +1 -1
- package/dist/handles/types.d.ts +2 -0
- package/dist/handles/types.d.ts.map +1 -1
- package/dist/persistence/BatchPersister.d.ts +30 -0
- package/dist/persistence/BatchPersister.d.ts.map +1 -1
- package/dist/persistence/MutationCollector.d.ts +28 -0
- package/dist/persistence/MutationCollector.d.ts.map +1 -1
- package/dist/persistence/types.d.ts +6 -0
- package/dist/persistence/types.d.ts.map +1 -1
- package/dist/store/EntityMetaStore.d.ts +4 -0
- package/dist/store/EntityMetaStore.d.ts.map +1 -1
- package/dist/store/EntitySnapshotStore.d.ts +4 -0
- package/dist/store/EntitySnapshotStore.d.ts.map +1 -1
- package/dist/store/ErrorStore.d.ts +6 -0
- package/dist/store/ErrorStore.d.ts.map +1 -1
- package/dist/store/RelationStore.d.ts +15 -0
- package/dist/store/RelationStore.d.ts.map +1 -1
- package/dist/store/SnapshotStore.d.ts +7 -0
- package/dist/store/SnapshotStore.d.ts.map +1 -1
- package/dist/store/SubscriptionManager.d.ts +15 -0
- package/dist/store/SubscriptionManager.d.ts.map +1 -1
- package/dist/store/TouchedStore.d.ts +4 -0
- package/dist/store/TouchedStore.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/adapter/ContemberAdapter.ts +11 -5
- package/src/adapter/types.ts +4 -0
- package/src/core/ActionDispatcher.ts +3 -0
- package/src/core/actions.ts +6 -2
- package/src/handles/HasManyListHandle.ts +1 -6
- package/src/handles/HasOneHandle.ts +22 -1
- package/src/handles/types.ts +2 -0
- package/src/persistence/BatchPersister.ts +237 -0
- package/src/persistence/MutationCollector.ts +167 -19
- package/src/persistence/types.ts +6 -0
- package/src/store/EntityMetaStore.ts +29 -0
- package/src/store/EntitySnapshotStore.ts +23 -0
- package/src/store/ErrorStore.ts +30 -0
- package/src/store/RelationStore.ts +137 -0
- package/src/store/SnapshotStore.ts +62 -5
- package/src/store/SubscriptionManager.ts +82 -2
- package/src/store/TouchedStore.ts +16 -0
|
@@ -31,9 +31,28 @@ export class SubscriptionManager {
|
|
|
31
31
|
/** Parent-child relationships: childKey -> Set of parentKeys */
|
|
32
32
|
private readonly childToParents = new Map<string, Set<string>>()
|
|
33
33
|
|
|
34
|
+
/** Maps old keys → new keys after rekey, so unsubscribe closures can find migrated callbacks */
|
|
35
|
+
private readonly rekeyedKeys = new Map<string, string>()
|
|
36
|
+
|
|
34
37
|
/** Global version number for change detection */
|
|
35
38
|
private globalVersion = 0
|
|
36
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Resolves a key through the rekey redirect chain.
|
|
42
|
+
* The rekey() method collapses chains (A→C instead of A→B→C), so this should
|
|
43
|
+
* always resolve in one hop. The loop with depth limit is a safety net in case
|
|
44
|
+
* chain collapsing has a bug — prevents infinite loops instead of silently hanging.
|
|
45
|
+
*/
|
|
46
|
+
private resolveKey(key: string): string {
|
|
47
|
+
let resolved = key
|
|
48
|
+
for (let depth = 0; depth < 10; depth++) {
|
|
49
|
+
const next = this.rekeyedKeys.get(resolved)
|
|
50
|
+
if (!next || next === resolved) return resolved
|
|
51
|
+
resolved = next
|
|
52
|
+
}
|
|
53
|
+
return resolved
|
|
54
|
+
}
|
|
55
|
+
|
|
37
56
|
/**
|
|
38
57
|
* Subscribe to changes on a specific entity.
|
|
39
58
|
* Returns unsubscribe function.
|
|
@@ -46,7 +65,8 @@ export class SubscriptionManager {
|
|
|
46
65
|
this.entitySubscribers.get(key)!.add(callback)
|
|
47
66
|
|
|
48
67
|
return () => {
|
|
49
|
-
this.
|
|
68
|
+
const resolvedKey = this.resolveKey(key)
|
|
69
|
+
this.entitySubscribers.get(resolvedKey)?.delete(callback)
|
|
50
70
|
}
|
|
51
71
|
}
|
|
52
72
|
|
|
@@ -62,7 +82,8 @@ export class SubscriptionManager {
|
|
|
62
82
|
this.relationSubscribers.get(key)!.add(callback)
|
|
63
83
|
|
|
64
84
|
return () => {
|
|
65
|
-
this.
|
|
85
|
+
const resolvedKey = this.resolveKey(key)
|
|
86
|
+
this.relationSubscribers.get(resolvedKey)?.delete(callback)
|
|
66
87
|
}
|
|
67
88
|
}
|
|
68
89
|
|
|
@@ -238,4 +259,63 @@ export class SubscriptionManager {
|
|
|
238
259
|
sub()
|
|
239
260
|
}
|
|
240
261
|
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Moves subscriptions and parent-child relationships from oldKey to newKey.
|
|
265
|
+
* Also rekeys relation subscribers under oldKeyPrefix to newKeyPrefix.
|
|
266
|
+
* Registers redirects so unsubscribe closures can find migrated callbacks.
|
|
267
|
+
*/
|
|
268
|
+
rekey(oldKey: string, newKey: string, oldKeyPrefix: string, newKeyPrefix: string): void {
|
|
269
|
+
// Register redirect for entity key (update existing chains first)
|
|
270
|
+
for (const [fromKey, toKey] of this.rekeyedKeys) {
|
|
271
|
+
if (toKey === oldKey) {
|
|
272
|
+
this.rekeyedKeys.set(fromKey, newKey)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
this.rekeyedKeys.set(oldKey, newKey)
|
|
276
|
+
|
|
277
|
+
// Move entity subscribers
|
|
278
|
+
const entitySubs = this.entitySubscribers.get(oldKey)
|
|
279
|
+
if (entitySubs) {
|
|
280
|
+
this.entitySubscribers.delete(oldKey)
|
|
281
|
+
this.entitySubscribers.set(newKey, entitySubs)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Move relation subscribers by prefix (e.g. "Entity:tempId:" → "Entity:persistedId:")
|
|
285
|
+
const toMoveRelations: [string, Set<Subscriber>][] = []
|
|
286
|
+
for (const [key, subs] of this.relationSubscribers) {
|
|
287
|
+
if (key.startsWith(oldKeyPrefix)) {
|
|
288
|
+
toMoveRelations.push([key, subs])
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
for (const [oldRelKey, subs] of toMoveRelations) {
|
|
292
|
+
const newRelKey = newKeyPrefix + oldRelKey.slice(oldKeyPrefix.length)
|
|
293
|
+
|
|
294
|
+
// Register redirect for relation key (update existing chains first)
|
|
295
|
+
for (const [fromKey, toKey] of this.rekeyedKeys) {
|
|
296
|
+
if (toKey === oldRelKey) {
|
|
297
|
+
this.rekeyedKeys.set(fromKey, newRelKey)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
this.rekeyedKeys.set(oldRelKey, newRelKey)
|
|
301
|
+
|
|
302
|
+
this.relationSubscribers.delete(oldRelKey)
|
|
303
|
+
this.relationSubscribers.set(newRelKey, subs)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Move parent-child: update child→parent mappings
|
|
307
|
+
const parents = this.childToParents.get(oldKey)
|
|
308
|
+
if (parents) {
|
|
309
|
+
this.childToParents.delete(oldKey)
|
|
310
|
+
this.childToParents.set(newKey, parents)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Update parent-child: replace oldKey in any parent sets that reference it
|
|
314
|
+
for (const parentSet of this.childToParents.values()) {
|
|
315
|
+
if (parentSet.has(oldKey)) {
|
|
316
|
+
parentSet.delete(oldKey)
|
|
317
|
+
parentSet.add(newKey)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
241
321
|
}
|
|
@@ -36,6 +36,22 @@ export class TouchedStore {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Rekeys all touched fields from oldKeyPrefix to newKeyPrefix.
|
|
41
|
+
*/
|
|
42
|
+
rekey(oldKeyPrefix: string, newKeyPrefix: string): void {
|
|
43
|
+
const toMove: [string, boolean][] = []
|
|
44
|
+
for (const [key, value] of this.touchedFields) {
|
|
45
|
+
if (key.startsWith(oldKeyPrefix)) {
|
|
46
|
+
toMove.push([key, value])
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const [oldKey, value] of toMove) {
|
|
50
|
+
this.touchedFields.delete(oldKey)
|
|
51
|
+
this.touchedFields.set(newKeyPrefix + oldKey.slice(oldKeyPrefix.length), value)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
39
55
|
clear(): void {
|
|
40
56
|
this.touchedFields.clear()
|
|
41
57
|
}
|