@tanstack/db 0.3.2 → 0.4.0

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.
Files changed (164) hide show
  1. package/dist/cjs/{change-events.cjs → collection/change-events.cjs} +13 -42
  2. package/dist/cjs/collection/change-events.cjs.map +1 -0
  3. package/dist/{esm/change-events.d.ts → cjs/collection/change-events.d.cts} +6 -6
  4. package/dist/cjs/collection/changes.cjs +108 -0
  5. package/dist/cjs/collection/changes.cjs.map +1 -0
  6. package/dist/cjs/collection/changes.d.cts +53 -0
  7. package/dist/cjs/{collection-events.cjs → collection/events.cjs} +7 -5
  8. package/dist/cjs/collection/events.cjs.map +1 -0
  9. package/dist/cjs/{collection-events.d.cts → collection/events.d.cts} +7 -4
  10. package/dist/cjs/collection/index.cjs +417 -0
  11. package/dist/cjs/collection/index.cjs.map +1 -0
  12. package/dist/{esm/collection.d.ts → cjs/collection/index.d.cts} +46 -184
  13. package/dist/cjs/collection/indexes.cjs +124 -0
  14. package/dist/cjs/collection/indexes.cjs.map +1 -0
  15. package/dist/cjs/collection/indexes.d.cts +47 -0
  16. package/dist/cjs/collection/lifecycle.cjs +150 -0
  17. package/dist/cjs/collection/lifecycle.cjs.map +1 -0
  18. package/dist/cjs/collection/lifecycle.d.cts +70 -0
  19. package/dist/cjs/collection/mutations.cjs +315 -0
  20. package/dist/cjs/collection/mutations.cjs.map +1 -0
  21. package/dist/cjs/collection/mutations.d.cts +33 -0
  22. package/dist/cjs/collection/state.cjs +597 -0
  23. package/dist/cjs/collection/state.cjs.map +1 -0
  24. package/dist/cjs/collection/state.d.cts +122 -0
  25. package/dist/cjs/collection/subscription.cjs +160 -0
  26. package/dist/cjs/collection/subscription.cjs.map +1 -0
  27. package/dist/cjs/collection/subscription.d.cts +57 -0
  28. package/dist/cjs/collection/sync.cjs +154 -0
  29. package/dist/cjs/collection/sync.cjs.map +1 -0
  30. package/dist/cjs/collection/sync.d.cts +34 -0
  31. package/dist/cjs/index.cjs +8 -8
  32. package/dist/cjs/index.d.cts +2 -2
  33. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  34. package/dist/cjs/indexes/auto-index.d.cts +1 -1
  35. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  36. package/dist/cjs/indexes/base-index.d.cts +2 -2
  37. package/dist/cjs/indexes/btree-index.cjs +2 -2
  38. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  39. package/dist/cjs/indexes/btree-index.d.cts +1 -1
  40. package/dist/cjs/query/builder/index.cjs +2 -2
  41. package/dist/cjs/query/builder/index.cjs.map +1 -1
  42. package/dist/cjs/query/builder/types.d.cts +1 -1
  43. package/dist/cjs/query/compiler/index.cjs +5 -2
  44. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  45. package/dist/cjs/query/compiler/index.d.cts +3 -2
  46. package/dist/cjs/query/compiler/joins.cjs +22 -24
  47. package/dist/cjs/query/compiler/joins.cjs.map +1 -1
  48. package/dist/cjs/query/compiler/joins.d.cts +3 -2
  49. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  50. package/dist/cjs/query/compiler/order-by.d.cts +1 -1
  51. package/dist/cjs/query/ir.cjs.map +1 -1
  52. package/dist/cjs/query/ir.d.cts +1 -1
  53. package/dist/cjs/query/live/collection-config-builder.cjs +29 -12
  54. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  55. package/dist/cjs/query/live/collection-config-builder.d.cts +3 -0
  56. package/dist/cjs/query/live/collection-subscriber.cjs +43 -104
  57. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  58. package/dist/cjs/query/live/collection-subscriber.d.cts +4 -7
  59. package/dist/cjs/query/live-query-collection.cjs +2 -2
  60. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  61. package/dist/cjs/query/live-query-collection.d.cts +1 -1
  62. package/dist/cjs/transactions.cjs +3 -3
  63. package/dist/cjs/transactions.cjs.map +1 -1
  64. package/dist/cjs/types.d.cts +12 -10
  65. package/dist/{cjs/change-events.d.cts → esm/collection/change-events.d.ts} +6 -6
  66. package/dist/esm/{change-events.js → collection/change-events.js} +13 -42
  67. package/dist/esm/collection/change-events.js.map +1 -0
  68. package/dist/esm/collection/changes.d.ts +53 -0
  69. package/dist/esm/collection/changes.js +108 -0
  70. package/dist/esm/collection/changes.js.map +1 -0
  71. package/dist/esm/{collection-events.d.ts → collection/events.d.ts} +7 -4
  72. package/dist/esm/{collection-events.js → collection/events.js} +7 -5
  73. package/dist/esm/collection/events.js.map +1 -0
  74. package/dist/{cjs/collection.d.cts → esm/collection/index.d.ts} +46 -184
  75. package/dist/esm/collection/index.js +417 -0
  76. package/dist/esm/collection/index.js.map +1 -0
  77. package/dist/esm/collection/indexes.d.ts +47 -0
  78. package/dist/esm/collection/indexes.js +124 -0
  79. package/dist/esm/collection/indexes.js.map +1 -0
  80. package/dist/esm/collection/lifecycle.d.ts +70 -0
  81. package/dist/esm/collection/lifecycle.js +150 -0
  82. package/dist/esm/collection/lifecycle.js.map +1 -0
  83. package/dist/esm/collection/mutations.d.ts +33 -0
  84. package/dist/esm/collection/mutations.js +315 -0
  85. package/dist/esm/collection/mutations.js.map +1 -0
  86. package/dist/esm/collection/state.d.ts +122 -0
  87. package/dist/esm/collection/state.js +597 -0
  88. package/dist/esm/collection/state.js.map +1 -0
  89. package/dist/esm/collection/subscription.d.ts +57 -0
  90. package/dist/esm/collection/subscription.js +160 -0
  91. package/dist/esm/collection/subscription.js.map +1 -0
  92. package/dist/esm/collection/sync.d.ts +34 -0
  93. package/dist/esm/collection/sync.js +154 -0
  94. package/dist/esm/collection/sync.js.map +1 -0
  95. package/dist/esm/index.d.ts +2 -2
  96. package/dist/esm/index.js +1 -1
  97. package/dist/esm/indexes/auto-index.d.ts +1 -1
  98. package/dist/esm/indexes/auto-index.js.map +1 -1
  99. package/dist/esm/indexes/base-index.d.ts +2 -2
  100. package/dist/esm/indexes/base-index.js.map +1 -1
  101. package/dist/esm/indexes/btree-index.d.ts +1 -1
  102. package/dist/esm/indexes/btree-index.js +2 -2
  103. package/dist/esm/indexes/btree-index.js.map +1 -1
  104. package/dist/esm/proxy.js +1 -1
  105. package/dist/esm/query/builder/index.js +1 -1
  106. package/dist/esm/query/builder/index.js.map +1 -1
  107. package/dist/esm/query/builder/types.d.ts +1 -1
  108. package/dist/esm/query/compiler/index.d.ts +3 -2
  109. package/dist/esm/query/compiler/index.js +5 -2
  110. package/dist/esm/query/compiler/index.js.map +1 -1
  111. package/dist/esm/query/compiler/joins.d.ts +3 -2
  112. package/dist/esm/query/compiler/joins.js +22 -24
  113. package/dist/esm/query/compiler/joins.js.map +1 -1
  114. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  115. package/dist/esm/query/compiler/order-by.js.map +1 -1
  116. package/dist/esm/query/ir.d.ts +1 -1
  117. package/dist/esm/query/ir.js.map +1 -1
  118. package/dist/esm/query/live/collection-config-builder.d.ts +3 -0
  119. package/dist/esm/query/live/collection-config-builder.js +29 -12
  120. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  121. package/dist/esm/query/live/collection-subscriber.d.ts +4 -7
  122. package/dist/esm/query/live/collection-subscriber.js +43 -104
  123. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  124. package/dist/esm/query/live-query-collection.d.ts +1 -1
  125. package/dist/esm/query/live-query-collection.js +1 -1
  126. package/dist/esm/query/live-query-collection.js.map +1 -1
  127. package/dist/esm/transactions.js +3 -3
  128. package/dist/esm/transactions.js.map +1 -1
  129. package/dist/esm/types.d.ts +12 -10
  130. package/package.json +2 -2
  131. package/src/{change-events.ts → collection/change-events.ts} +25 -39
  132. package/src/collection/changes.ts +163 -0
  133. package/src/{collection-events.ts → collection/events.ts} +8 -6
  134. package/src/collection/index.ts +808 -0
  135. package/src/collection/indexes.ts +172 -0
  136. package/src/collection/lifecycle.ts +221 -0
  137. package/src/collection/mutations.ts +535 -0
  138. package/src/collection/state.ts +866 -0
  139. package/src/collection/subscription.ts +239 -0
  140. package/src/collection/sync.ts +235 -0
  141. package/src/index.ts +2 -2
  142. package/src/indexes/auto-index.ts +1 -1
  143. package/src/indexes/base-index.ts +3 -3
  144. package/src/indexes/btree-index.ts +2 -2
  145. package/src/query/builder/index.ts +1 -1
  146. package/src/query/builder/types.ts +1 -1
  147. package/src/query/compiler/index.ts +7 -1
  148. package/src/query/compiler/joins.ts +28 -41
  149. package/src/query/compiler/order-by.ts +1 -1
  150. package/src/query/ir.ts +1 -1
  151. package/src/query/live/collection-config-builder.ts +48 -22
  152. package/src/query/live/collection-subscriber.ts +63 -168
  153. package/src/query/live-query-collection.ts +2 -2
  154. package/src/transactions.ts +3 -3
  155. package/src/types.ts +14 -15
  156. package/dist/cjs/change-events.cjs.map +0 -1
  157. package/dist/cjs/collection-events.cjs.map +0 -1
  158. package/dist/cjs/collection.cjs +0 -1625
  159. package/dist/cjs/collection.cjs.map +0 -1
  160. package/dist/esm/change-events.js.map +0 -1
  161. package/dist/esm/collection-events.js.map +0 -1
  162. package/dist/esm/collection.js +0 -1625
  163. package/dist/esm/collection.js.map +0 -1
  164. package/src/collection.ts +0 -2564
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  createSingleRowRefProxy,
3
3
  toExpression,
4
- } from "./query/builder/ref-proxy"
5
- import { compileSingleRowExpression } from "./query/compiler/evaluators.js"
6
- import { optimizeExpressionWithIndexes } from "./utils/index-optimization.js"
4
+ } from "../query/builder/ref-proxy"
5
+ import { compileSingleRowExpression } from "../query/compiler/evaluators.js"
6
+ import { optimizeExpressionWithIndexes } from "../utils/index-optimization.js"
7
7
  import type {
8
8
  ChangeMessage,
9
9
  CurrentStateAsChangesOptions,
10
10
  SubscribeChangesOptions,
11
- } from "./types"
12
- import type { Collection } from "./collection"
13
- import type { SingleRowRefProxy } from "./query/builder/ref-proxy"
14
- import type { BasicExpression } from "./query/ir.js"
11
+ } from "../types"
12
+ import type { Collection } from "./index.js"
13
+ import type { SingleRowRefProxy } from "../query/builder/ref-proxy"
14
+ import type { BasicExpression } from "../query/ir.js"
15
15
 
16
16
  /**
17
17
  * Interface for a collection-like object that provides the necessary methods
@@ -46,8 +46,8 @@ export function currentStateAsChanges<
46
46
  TKey extends string | number,
47
47
  >(
48
48
  collection: CollectionLike<T, TKey>,
49
- options: CurrentStateAsChangesOptions<T> = {}
50
- ): Array<ChangeMessage<T>> {
49
+ options: CurrentStateAsChangesOptions = {}
50
+ ): Array<ChangeMessage<T>> | void {
51
51
  // Helper function to collect filtered results
52
52
  const collectFilteredResults = (
53
53
  filterFn?: (value: T) => boolean
@@ -66,31 +66,17 @@ export function currentStateAsChanges<
66
66
  return result
67
67
  }
68
68
 
69
- if (!options.where && !options.whereExpression) {
69
+ // TODO: handle orderBy and limit options
70
+ // by calling optimizeOrderedLimit
71
+
72
+ if (!options.where) {
70
73
  // No filtering, return all items
71
74
  return collectFilteredResults()
72
75
  }
73
76
 
74
77
  // There's a where clause, let's see if we can use an index
75
78
  try {
76
- let expression: BasicExpression<boolean>
77
-
78
- if (options.whereExpression) {
79
- // Use the pre-compiled expression directly
80
- expression = options.whereExpression
81
- } else if (options.where) {
82
- // Create the single-row refProxy for the callback
83
- const singleRowRefProxy = createSingleRowRefProxy<T>()
84
-
85
- // Execute the callback to get the expression
86
- const whereExpression = options.where(singleRowRefProxy)
87
-
88
- // Convert the result to a BasicExpression
89
- expression = toExpression(whereExpression)
90
- } else {
91
- // This should never happen due to the check above, but TypeScript needs it
92
- return []
93
- }
79
+ const expression: BasicExpression<boolean> = options.where
94
80
 
95
81
  // Try to optimize the query using indexes
96
82
  const optimizationResult = optimizeExpressionWithIndexes(
@@ -113,11 +99,11 @@ export function currentStateAsChanges<
113
99
  }
114
100
  return result
115
101
  } else {
116
- // No index found or complex expression, fall back to full scan with filter
117
- const filterFn = options.where
118
- ? createFilterFunction(options.where)
119
- : createFilterFunctionFromExpression(expression)
102
+ if (options.optimizedOnly) {
103
+ return
104
+ }
120
105
 
106
+ const filterFn = createFilterFunctionFromExpression(expression)
121
107
  return collectFilteredResults(filterFn)
122
108
  }
123
109
  } catch (error) {
@@ -127,9 +113,11 @@ export function currentStateAsChanges<
127
113
  error
128
114
  )
129
115
 
130
- const filterFn = options.where
131
- ? createFilterFunction(options.where)
132
- : createFilterFunctionFromExpression(options.whereExpression!)
116
+ const filterFn = createFilterFunctionFromExpression(options.where)
117
+
118
+ if (options.optimizedOnly) {
119
+ return
120
+ }
133
121
 
134
122
  return collectFilteredResults(filterFn)
135
123
  }
@@ -201,11 +189,9 @@ export function createFilterFunctionFromExpression<T extends object>(
201
189
  */
202
190
  export function createFilteredCallback<T extends object>(
203
191
  originalCallback: (changes: Array<ChangeMessage<T>>) => void,
204
- options: SubscribeChangesOptions<T>
192
+ options: SubscribeChangesOptions
205
193
  ): (changes: Array<ChangeMessage<T>>) => void {
206
- const filterFn = options.whereExpression
207
- ? createFilterFunctionFromExpression(options.whereExpression)
208
- : createFilterFunction(options.where!)
194
+ const filterFn = createFilterFunctionFromExpression(options.whereExpression!)
209
195
 
210
196
  return (changes: Array<ChangeMessage<T>>) => {
211
197
  const filteredChanges: Array<ChangeMessage<T>> = []
@@ -0,0 +1,163 @@
1
+ import { NegativeActiveSubscribersError } from "../errors"
2
+ import { CollectionSubscription } from "./subscription.js"
3
+ import type { StandardSchemaV1 } from "@standard-schema/spec"
4
+ import type { ChangeMessage, SubscribeChangesOptions } from "../types"
5
+ import type { CollectionLifecycleManager } from "./lifecycle.js"
6
+ import type { CollectionSyncManager } from "./sync.js"
7
+ import type { CollectionEventsManager } from "./events.js"
8
+ import type { CollectionImpl } from "./index.js"
9
+
10
+ export class CollectionChangesManager<
11
+ TOutput extends object = Record<string, unknown>,
12
+ TKey extends string | number = string | number,
13
+ TSchema extends StandardSchemaV1 = StandardSchemaV1,
14
+ TInput extends object = TOutput,
15
+ > {
16
+ private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>
17
+ private sync!: CollectionSyncManager<TOutput, TKey, TSchema, TInput>
18
+ private events!: CollectionEventsManager
19
+ private collection!: CollectionImpl<TOutput, TKey, any, TSchema, TInput>
20
+
21
+ public activeSubscribersCount = 0
22
+ public changeSubscriptions = new Set<CollectionSubscription>()
23
+ public batchedEvents: Array<ChangeMessage<TOutput, TKey>> = []
24
+ public shouldBatchEvents = false
25
+
26
+ /**
27
+ * Creates a new CollectionChangesManager instance
28
+ */
29
+ constructor() {}
30
+
31
+ public setDeps(deps: {
32
+ lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>
33
+ sync: CollectionSyncManager<TOutput, TKey, TSchema, TInput>
34
+ events: CollectionEventsManager
35
+ collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>
36
+ }) {
37
+ this.lifecycle = deps.lifecycle
38
+ this.sync = deps.sync
39
+ this.events = deps.events
40
+ this.collection = deps.collection
41
+ }
42
+
43
+ /**
44
+ * Emit an empty ready event to notify subscribers that the collection is ready
45
+ * This bypasses the normal empty array check in emitEvents
46
+ */
47
+ public emitEmptyReadyEvent(): void {
48
+ // Emit empty array directly to all subscribers
49
+ for (const subscription of this.changeSubscriptions) {
50
+ subscription.emitEvents([])
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Emit events either immediately or batch them for later emission
56
+ */
57
+ public emitEvents(
58
+ changes: Array<ChangeMessage<TOutput, TKey>>,
59
+ forceEmit = false
60
+ ): void {
61
+ // Skip batching for user actions (forceEmit=true) to keep UI responsive
62
+ if (this.shouldBatchEvents && !forceEmit) {
63
+ // Add events to the batch
64
+ this.batchedEvents.push(...changes)
65
+ return
66
+ }
67
+
68
+ // Either we're not batching, or we're forcing emission (user action or ending batch cycle)
69
+ let eventsToEmit = changes
70
+
71
+ // If we have batched events and this is a forced emit, combine them
72
+ if (this.batchedEvents.length > 0 && forceEmit) {
73
+ eventsToEmit = [...this.batchedEvents, ...changes]
74
+ this.batchedEvents = []
75
+ this.shouldBatchEvents = false
76
+ }
77
+
78
+ if (eventsToEmit.length === 0) return
79
+
80
+ // Emit to all listeners
81
+ for (const subscription of this.changeSubscriptions) {
82
+ subscription.emitEvents(eventsToEmit)
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Subscribe to changes in the collection
88
+ */
89
+ public subscribeChanges(
90
+ callback: (changes: Array<ChangeMessage<TOutput>>) => void,
91
+ options: SubscribeChangesOptions = {}
92
+ ): CollectionSubscription {
93
+ // Start sync and track subscriber
94
+ this.addSubscriber()
95
+
96
+ const subscription = new CollectionSubscription(this.collection, callback, {
97
+ ...options,
98
+ onUnsubscribe: () => {
99
+ this.removeSubscriber()
100
+ this.changeSubscriptions.delete(subscription)
101
+ },
102
+ })
103
+
104
+ if (options.includeInitialState) {
105
+ subscription.requestSnapshot()
106
+ }
107
+
108
+ // Add to batched listeners
109
+ this.changeSubscriptions.add(subscription)
110
+
111
+ return subscription
112
+ }
113
+
114
+ /**
115
+ * Increment the active subscribers count and start sync if needed
116
+ */
117
+ private addSubscriber(): void {
118
+ const previousSubscriberCount = this.activeSubscribersCount
119
+ this.activeSubscribersCount++
120
+ this.lifecycle.cancelGCTimer()
121
+
122
+ // Start sync if collection was cleaned up
123
+ if (
124
+ this.lifecycle.status === `cleaned-up` ||
125
+ this.lifecycle.status === `idle`
126
+ ) {
127
+ this.sync.startSync()
128
+ }
129
+
130
+ this.events.emitSubscribersChange(
131
+ this.activeSubscribersCount,
132
+ previousSubscriberCount
133
+ )
134
+ }
135
+
136
+ /**
137
+ * Decrement the active subscribers count and start GC timer if needed
138
+ */
139
+ private removeSubscriber(): void {
140
+ const previousSubscriberCount = this.activeSubscribersCount
141
+ this.activeSubscribersCount--
142
+
143
+ if (this.activeSubscribersCount === 0) {
144
+ this.lifecycle.startGCTimer()
145
+ } else if (this.activeSubscribersCount < 0) {
146
+ throw new NegativeActiveSubscribersError()
147
+ }
148
+
149
+ this.events.emitSubscribersChange(
150
+ this.activeSubscribersCount,
151
+ previousSubscriberCount
152
+ )
153
+ }
154
+
155
+ /**
156
+ * Clean up the collection by stopping sync and clearing data
157
+ * This can be called manually or automatically by garbage collection
158
+ */
159
+ public cleanup(): void {
160
+ this.batchedEvents = []
161
+ this.shouldBatchEvents = false
162
+ }
163
+ }
@@ -1,5 +1,5 @@
1
- import type { Collection } from "./collection"
2
- import type { CollectionStatus } from "./types"
1
+ import type { Collection } from "./index.js"
2
+ import type { CollectionStatus } from "../types.js"
3
3
 
4
4
  /**
5
5
  * Event emitted when the collection status changes
@@ -47,15 +47,17 @@ export type CollectionEventHandler<T extends keyof AllCollectionEvents> = (
47
47
  event: AllCollectionEvents[T]
48
48
  ) => void
49
49
 
50
- export class CollectionEvents {
51
- private collection: Collection<any, any, any, any, any>
50
+ export class CollectionEventsManager {
51
+ private collection!: Collection<any, any, any, any, any>
52
52
  private listeners = new Map<
53
53
  keyof AllCollectionEvents,
54
54
  Set<CollectionEventHandler<any>>
55
55
  >()
56
56
 
57
- constructor(collection: Collection<any, any, any, any, any>) {
58
- this.collection = collection
57
+ constructor() {}
58
+
59
+ setDeps(deps: { collection: Collection<any, any, any, any, any> }) {
60
+ this.collection = deps.collection
59
61
  }
60
62
 
61
63
  on<T extends keyof AllCollectionEvents>(