@tanstack/db 0.0.30 → 0.0.32
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/change-events.cjs +1 -1
- package/dist/cjs/change-events.cjs.map +1 -1
- package/dist/cjs/collection.cjs +36 -23
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +5 -1
- package/dist/cjs/index.cjs +0 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/change-events.js +1 -1
- package/dist/esm/change-events.js.map +1 -1
- package/dist/esm/collection.d.ts +5 -1
- package/dist/esm/collection.js +36 -23
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/index.js +1 -2
- package/package.json +1 -1
- package/src/change-events.ts +3 -1
- package/src/collection.ts +53 -28
package/src/collection.ts
CHANGED
|
@@ -62,9 +62,6 @@ import type {
|
|
|
62
62
|
import type { IndexOptions } from "./indexes/index-options.js"
|
|
63
63
|
import type { BaseIndex, IndexResolver } from "./indexes/base-index.js"
|
|
64
64
|
|
|
65
|
-
// Store collections in memory
|
|
66
|
-
export const collectionsStore = new Map<string, CollectionImpl<any, any, any>>()
|
|
67
|
-
|
|
68
65
|
interface PendingSyncedTransaction<T extends object = Record<string, unknown>> {
|
|
69
66
|
committed: boolean
|
|
70
67
|
operations: Array<OptimisticChangeMessage<T>>
|
|
@@ -321,6 +318,12 @@ export class CollectionImpl<
|
|
|
321
318
|
const callbacks = [...this.onFirstReadyCallbacks]
|
|
322
319
|
this.onFirstReadyCallbacks = []
|
|
323
320
|
callbacks.forEach((callback) => callback())
|
|
321
|
+
|
|
322
|
+
// If the collection is empty when it becomes ready, emit an empty change event
|
|
323
|
+
// to notify subscribers (like LiveQueryCollection) that the collection is ready
|
|
324
|
+
if (this.size === 0 && this.changeListeners.size > 0) {
|
|
325
|
+
this.emitEmptyReadyEvent()
|
|
326
|
+
}
|
|
324
327
|
}
|
|
325
328
|
}
|
|
326
329
|
}
|
|
@@ -427,9 +430,6 @@ export class CollectionImpl<
|
|
|
427
430
|
autoIndex: config.autoIndex ?? `eager`,
|
|
428
431
|
}
|
|
429
432
|
|
|
430
|
-
// Store in global collections store
|
|
431
|
-
collectionsStore.set(this.id, this)
|
|
432
|
-
|
|
433
433
|
// Set up data storage with optional comparison function
|
|
434
434
|
if (this.config.compare) {
|
|
435
435
|
this.syncedData = new SortedMap<TKey, T>(this.config.compare)
|
|
@@ -687,7 +687,9 @@ export class CollectionImpl<
|
|
|
687
687
|
/**
|
|
688
688
|
* Recompute optimistic state from active transactions
|
|
689
689
|
*/
|
|
690
|
-
private recomputeOptimisticState(
|
|
690
|
+
private recomputeOptimisticState(
|
|
691
|
+
triggeredByUserAction: boolean = false
|
|
692
|
+
): void {
|
|
691
693
|
// Skip redundant recalculations when we're in the middle of committing sync transactions
|
|
692
694
|
if (this.isCommittingSyncTransactions) {
|
|
693
695
|
return
|
|
@@ -738,13 +740,26 @@ export class CollectionImpl<
|
|
|
738
740
|
this.collectOptimisticChanges(previousState, previousDeletes, events)
|
|
739
741
|
|
|
740
742
|
// Filter out events for recently synced keys to prevent duplicates
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
)
|
|
743
|
+
// BUT: Only filter out events that are actually from sync operations
|
|
744
|
+
// New user transactions should NOT be filtered even if the key was recently synced
|
|
745
|
+
const filteredEventsBySyncStatus = events.filter((event) => {
|
|
746
|
+
if (!this.recentlySyncedKeys.has(event.key)) {
|
|
747
|
+
return true // Key not recently synced, allow event through
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Key was recently synced - allow if this is a user-triggered action
|
|
751
|
+
if (triggeredByUserAction) {
|
|
752
|
+
return true
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Otherwise filter out duplicate sync events
|
|
756
|
+
return false
|
|
757
|
+
})
|
|
744
758
|
|
|
745
759
|
// Filter out redundant delete events if there are pending sync transactions
|
|
746
760
|
// that will immediately restore the same data, but only for completed transactions
|
|
747
|
-
|
|
761
|
+
// IMPORTANT: Skip complex filtering for user-triggered actions to prevent UI blocking
|
|
762
|
+
if (this.pendingSyncedTransactions.length > 0 && !triggeredByUserAction) {
|
|
748
763
|
const pendingSyncKeys = new Set<TKey>()
|
|
749
764
|
const completedTransactionMutations = new Set<string>()
|
|
750
765
|
|
|
@@ -788,14 +803,14 @@ export class CollectionImpl<
|
|
|
788
803
|
if (filteredEvents.length > 0) {
|
|
789
804
|
this.updateIndexes(filteredEvents)
|
|
790
805
|
}
|
|
791
|
-
this.emitEvents(filteredEvents)
|
|
806
|
+
this.emitEvents(filteredEvents, triggeredByUserAction)
|
|
792
807
|
} else {
|
|
793
808
|
// Update indexes for all events
|
|
794
809
|
if (filteredEventsBySyncStatus.length > 0) {
|
|
795
810
|
this.updateIndexes(filteredEventsBySyncStatus)
|
|
796
811
|
}
|
|
797
812
|
// Emit all events if no pending sync transactions
|
|
798
|
-
this.emitEvents(filteredEventsBySyncStatus)
|
|
813
|
+
this.emitEvents(filteredEventsBySyncStatus, triggeredByUserAction)
|
|
799
814
|
}
|
|
800
815
|
}
|
|
801
816
|
|
|
@@ -873,27 +888,37 @@ export class CollectionImpl<
|
|
|
873
888
|
return this.syncedData.get(key)
|
|
874
889
|
}
|
|
875
890
|
|
|
891
|
+
/**
|
|
892
|
+
* Emit an empty ready event to notify subscribers that the collection is ready
|
|
893
|
+
* This bypasses the normal empty array check in emitEvents
|
|
894
|
+
*/
|
|
895
|
+
private emitEmptyReadyEvent(): void {
|
|
896
|
+
// Emit empty array directly to all listeners
|
|
897
|
+
for (const listener of this.changeListeners) {
|
|
898
|
+
listener([])
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
876
902
|
/**
|
|
877
903
|
* Emit events either immediately or batch them for later emission
|
|
878
904
|
*/
|
|
879
905
|
private emitEvents(
|
|
880
906
|
changes: Array<ChangeMessage<T, TKey>>,
|
|
881
|
-
|
|
907
|
+
forceEmit = false
|
|
882
908
|
): void {
|
|
883
|
-
|
|
909
|
+
// Skip batching for user actions (forceEmit=true) to keep UI responsive
|
|
910
|
+
if (this.shouldBatchEvents && !forceEmit) {
|
|
884
911
|
// Add events to the batch
|
|
885
912
|
this.batchedEvents.push(...changes)
|
|
886
913
|
return
|
|
887
914
|
}
|
|
888
915
|
|
|
889
|
-
// Either we're not batching, or we're ending
|
|
916
|
+
// Either we're not batching, or we're forcing emission (user action or ending batch cycle)
|
|
890
917
|
let eventsToEmit = changes
|
|
891
918
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
eventsToEmit = [...this.batchedEvents, ...changes]
|
|
896
|
-
}
|
|
919
|
+
// If we have batched events and this is a forced emit, combine them
|
|
920
|
+
if (this.batchedEvents.length > 0 && forceEmit) {
|
|
921
|
+
eventsToEmit = [...this.batchedEvents, ...changes]
|
|
897
922
|
this.batchedEvents = []
|
|
898
923
|
this.shouldBatchEvents = false
|
|
899
924
|
}
|
|
@@ -1625,7 +1650,7 @@ export class CollectionImpl<
|
|
|
1625
1650
|
ambientTransaction.applyMutations(mutations)
|
|
1626
1651
|
|
|
1627
1652
|
this.transactions.set(ambientTransaction.id, ambientTransaction)
|
|
1628
|
-
this.recomputeOptimisticState()
|
|
1653
|
+
this.recomputeOptimisticState(true)
|
|
1629
1654
|
|
|
1630
1655
|
return ambientTransaction
|
|
1631
1656
|
} else {
|
|
@@ -1650,7 +1675,7 @@ export class CollectionImpl<
|
|
|
1650
1675
|
|
|
1651
1676
|
// Add the transaction to the collection's transactions store
|
|
1652
1677
|
this.transactions.set(directOpTransaction.id, directOpTransaction)
|
|
1653
|
-
this.recomputeOptimisticState()
|
|
1678
|
+
this.recomputeOptimisticState(true)
|
|
1654
1679
|
|
|
1655
1680
|
return directOpTransaction
|
|
1656
1681
|
}
|
|
@@ -1847,7 +1872,7 @@ export class CollectionImpl<
|
|
|
1847
1872
|
ambientTransaction.applyMutations(mutations)
|
|
1848
1873
|
|
|
1849
1874
|
this.transactions.set(ambientTransaction.id, ambientTransaction)
|
|
1850
|
-
this.recomputeOptimisticState()
|
|
1875
|
+
this.recomputeOptimisticState(true)
|
|
1851
1876
|
|
|
1852
1877
|
return ambientTransaction
|
|
1853
1878
|
}
|
|
@@ -1876,7 +1901,7 @@ export class CollectionImpl<
|
|
|
1876
1901
|
// Add the transaction to the collection's transactions store
|
|
1877
1902
|
|
|
1878
1903
|
this.transactions.set(directOpTransaction.id, directOpTransaction)
|
|
1879
|
-
this.recomputeOptimisticState()
|
|
1904
|
+
this.recomputeOptimisticState(true)
|
|
1880
1905
|
|
|
1881
1906
|
return directOpTransaction
|
|
1882
1907
|
}
|
|
@@ -1963,7 +1988,7 @@ export class CollectionImpl<
|
|
|
1963
1988
|
ambientTransaction.applyMutations(mutations)
|
|
1964
1989
|
|
|
1965
1990
|
this.transactions.set(ambientTransaction.id, ambientTransaction)
|
|
1966
|
-
this.recomputeOptimisticState()
|
|
1991
|
+
this.recomputeOptimisticState(true)
|
|
1967
1992
|
|
|
1968
1993
|
return ambientTransaction
|
|
1969
1994
|
}
|
|
@@ -1989,7 +2014,7 @@ export class CollectionImpl<
|
|
|
1989
2014
|
directOpTransaction.commit()
|
|
1990
2015
|
|
|
1991
2016
|
this.transactions.set(directOpTransaction.id, directOpTransaction)
|
|
1992
|
-
this.recomputeOptimisticState()
|
|
2017
|
+
this.recomputeOptimisticState(true)
|
|
1993
2018
|
|
|
1994
2019
|
return directOpTransaction
|
|
1995
2020
|
}
|
|
@@ -2251,6 +2276,6 @@ export class CollectionImpl<
|
|
|
2251
2276
|
// CRITICAL: Capture visible state BEFORE clearing optimistic state
|
|
2252
2277
|
this.capturePreSyncVisibleState()
|
|
2253
2278
|
|
|
2254
|
-
this.recomputeOptimisticState()
|
|
2279
|
+
this.recomputeOptimisticState(false)
|
|
2255
2280
|
}
|
|
2256
2281
|
}
|