@tanstack/db 0.5.2 → 0.5.4

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 (61) hide show
  1. package/README.md +10 -10
  2. package/dist/cjs/collection/index.d.cts +1 -1
  3. package/dist/cjs/collection/mutations.d.cts +2 -2
  4. package/dist/cjs/collection/sync.cjs +5 -0
  5. package/dist/cjs/collection/sync.cjs.map +1 -1
  6. package/dist/cjs/errors.cjs +8 -0
  7. package/dist/cjs/errors.cjs.map +1 -1
  8. package/dist/cjs/errors.d.cts +3 -0
  9. package/dist/cjs/index.cjs +1 -0
  10. package/dist/cjs/index.cjs.map +1 -1
  11. package/dist/cjs/query/builder/index.cjs +18 -2
  12. package/dist/cjs/query/builder/index.cjs.map +1 -1
  13. package/dist/cjs/query/compiler/expressions.cjs +6 -44
  14. package/dist/cjs/query/compiler/expressions.cjs.map +1 -1
  15. package/dist/cjs/query/compiler/expressions.d.cts +15 -21
  16. package/dist/cjs/query/live/collection-subscriber.cjs +4 -14
  17. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  18. package/dist/cjs/query/optimizer.cjs +6 -9
  19. package/dist/cjs/query/optimizer.cjs.map +1 -1
  20. package/dist/cjs/strategies/debounceStrategy.cjs +4 -4
  21. package/dist/cjs/strategies/debounceStrategy.cjs.map +1 -1
  22. package/dist/cjs/strategies/queueStrategy.cjs +10 -8
  23. package/dist/cjs/strategies/queueStrategy.cjs.map +1 -1
  24. package/dist/cjs/strategies/throttleStrategy.cjs +4 -4
  25. package/dist/cjs/strategies/throttleStrategy.cjs.map +1 -1
  26. package/dist/cjs/types.d.cts +20 -1
  27. package/dist/esm/collection/index.d.ts +1 -1
  28. package/dist/esm/collection/mutations.d.ts +2 -2
  29. package/dist/esm/collection/sync.js +5 -0
  30. package/dist/esm/collection/sync.js.map +1 -1
  31. package/dist/esm/errors.d.ts +3 -0
  32. package/dist/esm/errors.js +8 -0
  33. package/dist/esm/errors.js.map +1 -1
  34. package/dist/esm/index.js +2 -1
  35. package/dist/esm/query/builder/index.js +19 -3
  36. package/dist/esm/query/builder/index.js.map +1 -1
  37. package/dist/esm/query/compiler/expressions.d.ts +15 -21
  38. package/dist/esm/query/compiler/expressions.js +6 -44
  39. package/dist/esm/query/compiler/expressions.js.map +1 -1
  40. package/dist/esm/query/live/collection-subscriber.js +5 -15
  41. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  42. package/dist/esm/query/optimizer.js +1 -4
  43. package/dist/esm/query/optimizer.js.map +1 -1
  44. package/dist/esm/strategies/debounceStrategy.js +2 -2
  45. package/dist/esm/strategies/debounceStrategy.js.map +1 -1
  46. package/dist/esm/strategies/queueStrategy.js +10 -8
  47. package/dist/esm/strategies/queueStrategy.js.map +1 -1
  48. package/dist/esm/strategies/throttleStrategy.js +2 -2
  49. package/dist/esm/strategies/throttleStrategy.js.map +1 -1
  50. package/dist/esm/types.d.ts +20 -1
  51. package/package.json +2 -2
  52. package/src/collection/sync.ts +10 -0
  53. package/src/errors.ts +9 -0
  54. package/src/query/builder/index.ts +28 -2
  55. package/src/query/compiler/expressions.ts +18 -66
  56. package/src/query/live/collection-subscriber.ts +6 -18
  57. package/src/query/optimizer.ts +1 -6
  58. package/src/strategies/debounceStrategy.ts +2 -2
  59. package/src/strategies/queueStrategy.ts +22 -9
  60. package/src/strategies/throttleStrategy.ts +2 -2
  61. package/src/types.ts +20 -1
@@ -1,9 +1,8 @@
1
1
  import { MultiSet } from "@tanstack/db-ivm"
2
2
  import {
3
- convertOrderByToBasicExpression,
4
- convertToBasicExpression,
3
+ normalizeExpressionPaths,
4
+ normalizeOrderByPaths,
5
5
  } from "../compiler/expressions.js"
6
- import { WhereClauseConversionError } from "../../errors.js"
7
6
  import type { MultiSetArray, RootStreamBuilder } from "@tanstack/db-ivm"
8
7
  import type { Collection } from "../../collection/index.js"
9
8
  import type { ChangeMessage } from "../../types.js"
@@ -41,13 +40,8 @@ export class CollectionSubscriber<
41
40
  const whereClause = this.getWhereClauseForAlias()
42
41
 
43
42
  if (whereClause) {
44
- const whereExpression = convertToBasicExpression(whereClause, this.alias)
45
-
46
- if (whereExpression) {
47
- return this.subscribeToChanges(whereExpression)
48
- }
49
-
50
- throw new WhereClauseConversionError(this.collectionId, this.alias)
43
+ const whereExpression = normalizeExpressionPaths(whereClause, this.alias)
44
+ return this.subscribeToChanges(whereExpression)
51
45
  }
52
46
 
53
47
  return this.subscribeToChanges()
@@ -199,10 +193,7 @@ export class CollectionSubscriber<
199
193
  subscription.setOrderByIndex(index)
200
194
 
201
195
  // Normalize the orderBy clauses such that the references are relative to the collection
202
- const normalizedOrderBy = convertOrderByToBasicExpression(
203
- orderBy,
204
- this.alias
205
- )
196
+ const normalizedOrderBy = normalizeOrderByPaths(orderBy, this.alias)
206
197
 
207
198
  // Load the first `offset + limit` values from the index
208
199
  // i.e. the K items from the collection that fall into the requested range: [offset, offset + limit[
@@ -289,10 +280,7 @@ export class CollectionSubscriber<
289
280
  : biggestSentRow
290
281
 
291
282
  // Normalize the orderBy clauses such that the references are relative to the collection
292
- const normalizedOrderBy = convertOrderByToBasicExpression(
293
- orderBy,
294
- this.alias
295
- )
283
+ const normalizedOrderBy = normalizeOrderByPaths(orderBy, this.alias)
296
284
 
297
285
  // Take the `n` items after the biggest sent value
298
286
  subscription.requestLimitedSnapshot({
@@ -131,7 +131,6 @@ import {
131
131
  getWhereExpression,
132
132
  isResidualWhere,
133
133
  } from "./ir.js"
134
- import { isConvertibleToCollectionFilter } from "./compiler/expressions.js"
135
134
  import type { BasicExpression, From, QueryIR, Select, Where } from "./ir.js"
136
135
 
137
136
  /**
@@ -248,14 +247,10 @@ function extractSourceWhereClauses(
248
247
  const groupedClauses = groupWhereClauses(analyzedClauses)
249
248
 
250
249
  // Only include single-source clauses that reference collections directly
251
- // and can be converted to BasicExpression format for collection indexes
252
250
  for (const [sourceAlias, whereClause] of groupedClauses.singleSource) {
253
251
  // Check if this source alias corresponds to a collection reference
254
252
  if (isCollectionReference(query, sourceAlias)) {
255
- // Check if the WHERE clause can be converted to collection-compatible format
256
- if (isConvertibleToCollectionFilter(whereClause)) {
257
- sourceWhereClauses.set(sourceAlias, whereClause)
258
- }
253
+ sourceWhereClauses.set(sourceAlias, whereClause)
259
254
  }
260
255
  }
261
256
 
@@ -1,4 +1,4 @@
1
- import { Debouncer } from "@tanstack/pacer/debouncer"
1
+ import { LiteDebouncer } from "@tanstack/pacer-lite/lite-debouncer"
2
2
  import type { DebounceStrategy, DebounceStrategyOptions } from "./types"
3
3
  import type { Transaction } from "../transactions"
4
4
 
@@ -28,7 +28,7 @@ import type { Transaction } from "../transactions"
28
28
  export function debounceStrategy(
29
29
  options: DebounceStrategyOptions
30
30
  ): DebounceStrategy {
31
- const debouncer = new Debouncer(
31
+ const debouncer = new LiteDebouncer(
32
32
  (callback: () => Transaction) => callback(),
33
33
  options
34
34
  )
@@ -1,4 +1,4 @@
1
- import { AsyncQueuer } from "@tanstack/pacer/async-queuer"
1
+ import { LiteQueuer } from "@tanstack/pacer-lite/lite-queuer"
2
2
  import type { QueueStrategy, QueueStrategyOptions } from "./types"
3
3
  import type { Transaction } from "../transactions"
4
4
 
@@ -44,16 +44,29 @@ import type { Transaction } from "../transactions"
44
44
  * ```
45
45
  */
46
46
  export function queueStrategy(options?: QueueStrategyOptions): QueueStrategy {
47
- const queuer = new AsyncQueuer<() => Transaction>(
48
- async (fn) => {
49
- const transaction = fn()
50
- // Wait for the transaction to be persisted before processing next item
51
- // Note: fn() already calls commit(), we just wait for it to complete
52
- await transaction.isPersisted.promise
47
+ // Manual promise chaining to ensure async serialization
48
+ // LiteQueuer (unlike AsyncQueuer from @tanstack/pacer) lacks built-in async queue
49
+ // primitives and concurrency control. We compensate by manually chaining promises
50
+ // to ensure each transaction completes before the next one starts.
51
+ let processingChain = Promise.resolve()
52
+
53
+ const queuer = new LiteQueuer<() => Transaction>(
54
+ (fn) => {
55
+ // Chain each transaction to the previous one's completion
56
+ processingChain = processingChain
57
+ .then(async () => {
58
+ const transaction = fn()
59
+ // Wait for the transaction to be persisted before processing next item
60
+ await transaction.isPersisted.promise
61
+ })
62
+ .catch(() => {
63
+ // Errors are handled via transaction.isPersisted.promise and surfaced there.
64
+ // This catch prevents unhandled promise rejections from breaking the chain,
65
+ // ensuring subsequent transactions can still execute even if one fails.
66
+ })
53
67
  },
54
68
  {
55
- concurrency: 1, // Process one at a time to ensure serialization
56
- wait: options?.wait,
69
+ wait: options?.wait ?? 0,
57
70
  maxSize: options?.maxSize,
58
71
  addItemsTo: options?.addItemsTo ?? `back`, // Default FIFO: add to back
59
72
  getItemsFrom: options?.getItemsFrom ?? `front`, // Default FIFO: get from front
@@ -1,4 +1,4 @@
1
- import { Throttler } from "@tanstack/pacer/throttler"
1
+ import { LiteThrottler } from "@tanstack/pacer-lite/lite-throttler"
2
2
  import type { ThrottleStrategy, ThrottleStrategyOptions } from "./types"
3
3
  import type { Transaction } from "../transactions"
4
4
 
@@ -48,7 +48,7 @@ import type { Transaction } from "../transactions"
48
48
  export function throttleStrategy(
49
49
  options: ThrottleStrategyOptions
50
50
  ): ThrottleStrategy {
51
- const throttler = new Throttler(
51
+ const throttler = new LiteThrottler(
52
52
  (callback: () => Transaction) => callback(),
53
53
  options
54
54
  )
package/src/types.ts CHANGED
@@ -139,7 +139,26 @@ export type NonEmptyArray<T> = [T, ...Array<T>]
139
139
  export type TransactionWithMutations<
140
140
  T extends object = Record<string, unknown>,
141
141
  TOperation extends OperationType = OperationType,
142
- > = Transaction<T> & {
142
+ > = Omit<Transaction<T>, `mutations`> & {
143
+ /**
144
+ * We must omit the `mutations` property from `Transaction<T>` before intersecting
145
+ * because TypeScript intersects property types when the same property appears on
146
+ * both sides of an intersection.
147
+ *
148
+ * Without `Omit`:
149
+ * - `Transaction<T>` has `mutations: Array<PendingMutation<T>>`
150
+ * - The intersection would create: `Array<PendingMutation<T>> & NonEmptyArray<PendingMutation<T, TOperation>>`
151
+ * - When mapping over this array, TypeScript widens `TOperation` from the specific literal
152
+ * (e.g., `"delete"`) to the union `OperationType` (`"insert" | "update" | "delete"`)
153
+ * - This causes `PendingMutation<T, OperationType>` to evaluate the conditional type
154
+ * `original: TOperation extends 'insert' ? {} : T` as `{} | T` instead of just `T`
155
+ *
156
+ * With `Omit`:
157
+ * - We remove `mutations` from `Transaction<T>` first
158
+ * - Then add back `mutations: NonEmptyArray<PendingMutation<T, TOperation>>`
159
+ * - TypeScript can properly narrow `TOperation` to the specific literal type
160
+ * - This ensures `mutation.original` is correctly typed as `T` (not `{} | T`) when mapping
161
+ */
143
162
  mutations: NonEmptyArray<PendingMutation<T, TOperation>>
144
163
  }
145
164