@tanstack/db 0.5.11 → 0.5.13

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 (225) hide show
  1. package/dist/cjs/SortedMap.cjs +40 -26
  2. package/dist/cjs/SortedMap.cjs.map +1 -1
  3. package/dist/cjs/SortedMap.d.cts +10 -15
  4. package/dist/cjs/collection/change-events.cjs.map +1 -1
  5. package/dist/cjs/collection/changes.cjs +2 -0
  6. package/dist/cjs/collection/changes.cjs.map +1 -1
  7. package/dist/cjs/collection/events.cjs.map +1 -1
  8. package/dist/cjs/collection/events.d.cts +12 -4
  9. package/dist/cjs/collection/index.cjs +2 -1
  10. package/dist/cjs/collection/index.cjs.map +1 -1
  11. package/dist/cjs/collection/indexes.cjs.map +1 -1
  12. package/dist/cjs/collection/lifecycle.cjs.map +1 -1
  13. package/dist/cjs/collection/mutations.cjs +5 -2
  14. package/dist/cjs/collection/mutations.cjs.map +1 -1
  15. package/dist/cjs/collection/state.cjs +6 -5
  16. package/dist/cjs/collection/state.cjs.map +1 -1
  17. package/dist/cjs/collection/state.d.cts +4 -1
  18. package/dist/cjs/collection/subscription.cjs +91 -57
  19. package/dist/cjs/collection/subscription.cjs.map +1 -1
  20. package/dist/cjs/collection/subscription.d.cts +26 -4
  21. package/dist/cjs/collection/sync.cjs +11 -6
  22. package/dist/cjs/collection/sync.cjs.map +1 -1
  23. package/dist/cjs/errors.cjs +9 -0
  24. package/dist/cjs/errors.cjs.map +1 -1
  25. package/dist/cjs/errors.d.cts +3 -0
  26. package/dist/cjs/event-emitter.cjs.map +1 -1
  27. package/dist/cjs/index.cjs +2 -0
  28. package/dist/cjs/index.cjs.map +1 -1
  29. package/dist/cjs/index.d.cts +1 -1
  30. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  31. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  32. package/dist/cjs/indexes/btree-index.cjs +8 -6
  33. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  34. package/dist/cjs/indexes/lazy-index.cjs.map +1 -1
  35. package/dist/cjs/indexes/reverse-index.cjs.map +1 -1
  36. package/dist/cjs/local-only.cjs.map +1 -1
  37. package/dist/cjs/local-storage.cjs.map +1 -1
  38. package/dist/cjs/optimistic-action.cjs.map +1 -1
  39. package/dist/cjs/paced-mutations.cjs.map +1 -1
  40. package/dist/cjs/proxy.cjs.map +1 -1
  41. package/dist/cjs/query/builder/functions.cjs.map +1 -1
  42. package/dist/cjs/query/builder/index.cjs.map +1 -1
  43. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
  44. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
  45. package/dist/cjs/query/compiler/expressions.cjs.map +1 -1
  46. package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
  47. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  48. package/dist/cjs/query/compiler/joins.cjs.map +1 -1
  49. package/dist/cjs/query/compiler/order-by.cjs +91 -38
  50. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  51. package/dist/cjs/query/compiler/order-by.d.cts +6 -2
  52. package/dist/cjs/query/compiler/select.cjs.map +1 -1
  53. package/dist/cjs/query/expression-helpers.cjs.map +1 -1
  54. package/dist/cjs/query/index.d.cts +1 -1
  55. package/dist/cjs/query/ir.cjs.map +1 -1
  56. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  57. package/dist/cjs/query/live/collection-registry.cjs.map +1 -1
  58. package/dist/cjs/query/live/collection-subscriber.cjs +30 -15
  59. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  60. package/dist/cjs/query/live/internal.cjs.map +1 -1
  61. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  62. package/dist/cjs/query/optimizer.cjs.map +1 -1
  63. package/dist/cjs/query/predicate-utils.cjs +19 -2
  64. package/dist/cjs/query/predicate-utils.cjs.map +1 -1
  65. package/dist/cjs/query/predicate-utils.d.cts +32 -1
  66. package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
  67. package/dist/cjs/scheduler.cjs.map +1 -1
  68. package/dist/cjs/strategies/debounceStrategy.cjs.map +1 -1
  69. package/dist/cjs/strategies/queueStrategy.cjs.map +1 -1
  70. package/dist/cjs/strategies/throttleStrategy.cjs.map +1 -1
  71. package/dist/cjs/transactions.cjs.map +1 -1
  72. package/dist/cjs/types.d.cts +53 -8
  73. package/dist/cjs/utils/browser-polyfills.cjs.map +1 -1
  74. package/dist/cjs/utils/btree.cjs.map +1 -1
  75. package/dist/cjs/utils/comparison.cjs.map +1 -1
  76. package/dist/cjs/utils/cursor.cjs +39 -0
  77. package/dist/cjs/utils/cursor.cjs.map +1 -0
  78. package/dist/cjs/utils/cursor.d.cts +18 -0
  79. package/dist/cjs/utils/index-optimization.cjs.map +1 -1
  80. package/dist/cjs/utils.cjs.map +1 -1
  81. package/dist/esm/SortedMap.d.ts +10 -15
  82. package/dist/esm/SortedMap.js +40 -26
  83. package/dist/esm/SortedMap.js.map +1 -1
  84. package/dist/esm/collection/change-events.js.map +1 -1
  85. package/dist/esm/collection/changes.js +2 -0
  86. package/dist/esm/collection/changes.js.map +1 -1
  87. package/dist/esm/collection/events.d.ts +12 -4
  88. package/dist/esm/collection/events.js.map +1 -1
  89. package/dist/esm/collection/index.js +2 -1
  90. package/dist/esm/collection/index.js.map +1 -1
  91. package/dist/esm/collection/indexes.js.map +1 -1
  92. package/dist/esm/collection/lifecycle.js.map +1 -1
  93. package/dist/esm/collection/mutations.js +6 -3
  94. package/dist/esm/collection/mutations.js.map +1 -1
  95. package/dist/esm/collection/state.d.ts +4 -1
  96. package/dist/esm/collection/state.js +6 -5
  97. package/dist/esm/collection/state.js.map +1 -1
  98. package/dist/esm/collection/subscription.d.ts +26 -4
  99. package/dist/esm/collection/subscription.js +92 -58
  100. package/dist/esm/collection/subscription.js.map +1 -1
  101. package/dist/esm/collection/sync.js +11 -6
  102. package/dist/esm/collection/sync.js.map +1 -1
  103. package/dist/esm/errors.d.ts +3 -0
  104. package/dist/esm/errors.js +9 -0
  105. package/dist/esm/errors.js.map +1 -1
  106. package/dist/esm/event-emitter.js.map +1 -1
  107. package/dist/esm/index.d.ts +1 -1
  108. package/dist/esm/index.js +4 -2
  109. package/dist/esm/indexes/auto-index.js.map +1 -1
  110. package/dist/esm/indexes/base-index.js.map +1 -1
  111. package/dist/esm/indexes/btree-index.js +8 -6
  112. package/dist/esm/indexes/btree-index.js.map +1 -1
  113. package/dist/esm/indexes/lazy-index.js.map +1 -1
  114. package/dist/esm/indexes/reverse-index.js.map +1 -1
  115. package/dist/esm/local-only.js.map +1 -1
  116. package/dist/esm/local-storage.js.map +1 -1
  117. package/dist/esm/optimistic-action.js.map +1 -1
  118. package/dist/esm/paced-mutations.js.map +1 -1
  119. package/dist/esm/proxy.js.map +1 -1
  120. package/dist/esm/query/builder/functions.js.map +1 -1
  121. package/dist/esm/query/builder/index.js.map +1 -1
  122. package/dist/esm/query/builder/ref-proxy.js.map +1 -1
  123. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  124. package/dist/esm/query/compiler/expressions.js.map +1 -1
  125. package/dist/esm/query/compiler/group-by.js.map +1 -1
  126. package/dist/esm/query/compiler/index.js.map +1 -1
  127. package/dist/esm/query/compiler/joins.js.map +1 -1
  128. package/dist/esm/query/compiler/order-by.d.ts +6 -2
  129. package/dist/esm/query/compiler/order-by.js +91 -38
  130. package/dist/esm/query/compiler/order-by.js.map +1 -1
  131. package/dist/esm/query/compiler/select.js.map +1 -1
  132. package/dist/esm/query/expression-helpers.js.map +1 -1
  133. package/dist/esm/query/index.d.ts +1 -1
  134. package/dist/esm/query/ir.js.map +1 -1
  135. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  136. package/dist/esm/query/live/collection-registry.js.map +1 -1
  137. package/dist/esm/query/live/collection-subscriber.js +30 -15
  138. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  139. package/dist/esm/query/live/internal.js.map +1 -1
  140. package/dist/esm/query/live-query-collection.js.map +1 -1
  141. package/dist/esm/query/optimizer.js.map +1 -1
  142. package/dist/esm/query/predicate-utils.d.ts +32 -1
  143. package/dist/esm/query/predicate-utils.js +19 -2
  144. package/dist/esm/query/predicate-utils.js.map +1 -1
  145. package/dist/esm/query/subset-dedupe.js.map +1 -1
  146. package/dist/esm/scheduler.js.map +1 -1
  147. package/dist/esm/strategies/debounceStrategy.js.map +1 -1
  148. package/dist/esm/strategies/queueStrategy.js.map +1 -1
  149. package/dist/esm/strategies/throttleStrategy.js.map +1 -1
  150. package/dist/esm/transactions.js.map +1 -1
  151. package/dist/esm/types.d.ts +53 -8
  152. package/dist/esm/utils/browser-polyfills.js.map +1 -1
  153. package/dist/esm/utils/btree.js.map +1 -1
  154. package/dist/esm/utils/comparison.js.map +1 -1
  155. package/dist/esm/utils/cursor.d.ts +18 -0
  156. package/dist/esm/utils/cursor.js +39 -0
  157. package/dist/esm/utils/cursor.js.map +1 -0
  158. package/dist/esm/utils/index-optimization.js.map +1 -1
  159. package/dist/esm/utils.js.map +1 -1
  160. package/package.json +30 -28
  161. package/src/SortedMap.ts +50 -31
  162. package/src/collection/change-events.ts +20 -20
  163. package/src/collection/changes.ts +16 -12
  164. package/src/collection/events.ts +20 -10
  165. package/src/collection/index.ts +47 -46
  166. package/src/collection/indexes.ts +14 -14
  167. package/src/collection/lifecycle.ts +16 -16
  168. package/src/collection/mutations.ts +25 -20
  169. package/src/collection/state.ts +43 -36
  170. package/src/collection/subscription.ts +171 -90
  171. package/src/collection/sync.ts +34 -22
  172. package/src/duplicate-instance-check.ts +1 -1
  173. package/src/errors.ts +49 -40
  174. package/src/event-emitter.ts +5 -5
  175. package/src/index.ts +21 -21
  176. package/src/indexes/auto-index.ts +11 -11
  177. package/src/indexes/base-index.ts +13 -13
  178. package/src/indexes/btree-index.ts +21 -17
  179. package/src/indexes/index-options.ts +3 -3
  180. package/src/indexes/lazy-index.ts +8 -8
  181. package/src/indexes/reverse-index.ts +5 -5
  182. package/src/local-only.ts +12 -12
  183. package/src/local-storage.ts +17 -17
  184. package/src/optimistic-action.ts +5 -5
  185. package/src/paced-mutations.ts +6 -6
  186. package/src/proxy.ts +43 -43
  187. package/src/query/builder/functions.ts +28 -28
  188. package/src/query/builder/index.ts +22 -22
  189. package/src/query/builder/ref-proxy.ts +4 -4
  190. package/src/query/builder/types.ts +8 -8
  191. package/src/query/compiler/evaluators.ts +9 -9
  192. package/src/query/compiler/expressions.ts +6 -6
  193. package/src/query/compiler/group-by.ts +24 -24
  194. package/src/query/compiler/index.ts +44 -44
  195. package/src/query/compiler/joins.ts +37 -37
  196. package/src/query/compiler/order-by.ts +170 -77
  197. package/src/query/compiler/select.ts +13 -13
  198. package/src/query/compiler/types.ts +2 -2
  199. package/src/query/expression-helpers.ts +16 -16
  200. package/src/query/index.ts +10 -9
  201. package/src/query/ir.ts +13 -13
  202. package/src/query/live/collection-config-builder.ts +53 -53
  203. package/src/query/live/collection-registry.ts +6 -6
  204. package/src/query/live/collection-subscriber.ts +87 -48
  205. package/src/query/live/internal.ts +1 -1
  206. package/src/query/live/types.ts +4 -4
  207. package/src/query/live-query-collection.ts +15 -15
  208. package/src/query/optimizer.ts +29 -29
  209. package/src/query/predicate-utils.ts +105 -50
  210. package/src/query/subset-dedupe.ts +6 -6
  211. package/src/scheduler.ts +3 -3
  212. package/src/strategies/debounceStrategy.ts +6 -6
  213. package/src/strategies/index.ts +4 -4
  214. package/src/strategies/queueStrategy.ts +5 -5
  215. package/src/strategies/throttleStrategy.ts +6 -6
  216. package/src/strategies/types.ts +2 -2
  217. package/src/transactions.ts +9 -9
  218. package/src/types.ts +76 -18
  219. package/src/utils/array-utils.ts +1 -1
  220. package/src/utils/browser-polyfills.ts +2 -2
  221. package/src/utils/btree.ts +22 -22
  222. package/src/utils/comparison.ts +3 -3
  223. package/src/utils/cursor.ts +78 -0
  224. package/src/utils/index-optimization.ts +14 -14
  225. package/src/utils.ts +4 -4
@@ -1,6 +1,6 @@
1
- import { Func, Value } from "./ir.js"
2
- import type { BasicExpression, OrderBy, PropRef } from "./ir.js"
3
- import type { LoadSubsetOptions } from "../types.js"
1
+ import { Func, Value } from './ir.js'
2
+ import type { BasicExpression, OrderBy, PropRef } from './ir.js'
3
+ import type { LoadSubsetOptions } from '../types.js'
4
4
 
5
5
  /**
6
6
  * Check if one where clause is a logical subset of another.
@@ -20,7 +20,7 @@ import type { LoadSubsetOptions } from "../types.js"
20
20
  */
21
21
  export function isWhereSubset(
22
22
  subset: BasicExpression<boolean> | undefined,
23
- superset: BasicExpression<boolean> | undefined
23
+ superset: BasicExpression<boolean> | undefined,
24
24
  ): boolean {
25
25
  // undefined/missing where clause means "no filter" (all data)
26
26
  // Both undefined means subset relationship holds (all data ⊆ all data)
@@ -44,7 +44,7 @@ export function isWhereSubset(
44
44
  }
45
45
 
46
46
  function makeDisjunction(
47
- preds: Array<BasicExpression<boolean>>
47
+ preds: Array<BasicExpression<boolean>>,
48
48
  ): BasicExpression<boolean> {
49
49
  if (preds.length === 0) {
50
50
  return new Value(false)
@@ -57,14 +57,14 @@ function makeDisjunction(
57
57
 
58
58
  function convertInToOr(inField: InField) {
59
59
  const equalities = inField.values.map(
60
- (value) => new Func(`eq`, [inField.ref, new Value(value)])
60
+ (value) => new Func(`eq`, [inField.ref, new Value(value)]),
61
61
  )
62
62
  return makeDisjunction(equalities)
63
63
  }
64
64
 
65
65
  function isWhereSubsetInternal(
66
66
  subset: BasicExpression<boolean>,
67
- superset: BasicExpression<boolean>
67
+ superset: BasicExpression<boolean>,
68
68
  ): boolean {
69
69
  // If subset is false it is requesting no data,
70
70
  // thus the result set is empty
@@ -83,7 +83,7 @@ function isWhereSubsetInternal(
83
83
  // Example: (age > 20) ⊆ (age > 10 AND status = 'active') is false (doesn't imply status condition)
84
84
  if (superset.type === `func` && superset.name === `and`) {
85
85
  return superset.args.every((arg) =>
86
- isWhereSubsetInternal(subset, arg as BasicExpression<boolean>)
86
+ isWhereSubsetInternal(subset, arg as BasicExpression<boolean>),
87
87
  )
88
88
  }
89
89
 
@@ -91,7 +91,7 @@ function isWhereSubsetInternal(
91
91
  if (subset.type === `func` && subset.name === `and`) {
92
92
  // For (A AND B) ⊆ C, since (A AND B) implies A, we check if any conjunct implies C
93
93
  return subset.args.some((arg) =>
94
- isWhereSubsetInternal(arg as BasicExpression<boolean>, superset)
94
+ isWhereSubsetInternal(arg as BasicExpression<boolean>, superset),
95
95
  )
96
96
  }
97
97
 
@@ -114,7 +114,7 @@ function isWhereSubsetInternal(
114
114
  // Handle OR in subset: (A OR B) is subset of C only if both A and B are subsets of C
115
115
  if (subset.type === `func` && subset.name === `or`) {
116
116
  return subset.args.every((arg) =>
117
- isWhereSubsetInternal(arg as BasicExpression<boolean>, superset)
117
+ isWhereSubsetInternal(arg as BasicExpression<boolean>, superset),
118
118
  )
119
119
  }
120
120
 
@@ -123,7 +123,7 @@ function isWhereSubsetInternal(
123
123
  // If subset is contained in any disjunct, it's contained in the union
124
124
  if (superset.type === `func` && superset.name === `or`) {
125
125
  return superset.args.some((arg) =>
126
- isWhereSubsetInternal(subset, arg as BasicExpression<boolean>)
126
+ isWhereSubsetInternal(subset, arg as BasicExpression<boolean>),
127
127
  )
128
128
  }
129
129
 
@@ -145,7 +145,7 @@ function isWhereSubsetInternal(
145
145
  subsetFunc,
146
146
  subsetField.value,
147
147
  supersetFunc,
148
- supersetField.value
148
+ supersetField.value,
149
149
  )
150
150
  }
151
151
 
@@ -205,8 +205,8 @@ function combineWherePredicates(
205
205
  predicates: Array<BasicExpression<boolean>>,
206
206
  operation: `and` | `or`,
207
207
  simplifyFn: (
208
- preds: Array<BasicExpression<boolean>>
209
- ) => BasicExpression<boolean> | null
208
+ preds: Array<BasicExpression<boolean>>,
209
+ ) => BasicExpression<boolean> | null,
210
210
  ): BasicExpression<boolean> {
211
211
  const emptyValue = operation === `and` ? true : false
212
212
  const identityValue = operation === `and` ? true : false
@@ -293,7 +293,7 @@ function combineWherePredicates(
293
293
  * @returns Combined predicate representing the union
294
294
  */
295
295
  export function unionWherePredicates(
296
- predicates: Array<BasicExpression<boolean>>
296
+ predicates: Array<BasicExpression<boolean>>,
297
297
  ): BasicExpression<boolean> {
298
298
  return combineWherePredicates(predicates, `or`, unionSameFieldPredicates)
299
299
  }
@@ -337,7 +337,7 @@ export function unionWherePredicates(
337
337
  */
338
338
  export function minusWherePredicates(
339
339
  fromPredicate: BasicExpression<boolean> | undefined,
340
- subtractPredicate: BasicExpression<boolean> | undefined
340
+ subtractPredicate: BasicExpression<boolean> | undefined,
341
341
  ): BasicExpression<boolean> | null {
342
342
  // If nothing to subtract, return the original
343
343
  if (subtractPredicate === undefined) {
@@ -367,20 +367,20 @@ export function minusWherePredicates(
367
367
  // Try to detect and handle common conditions
368
368
  const commonConditions = findCommonConditions(
369
369
  fromPredicate,
370
- subtractPredicate
370
+ subtractPredicate,
371
371
  )
372
372
  if (commonConditions.length > 0) {
373
373
  // Extract predicates without common conditions
374
374
  const fromWithoutCommon = removeConditions(fromPredicate, commonConditions)
375
375
  const subtractWithoutCommon = removeConditions(
376
376
  subtractPredicate,
377
- commonConditions
377
+ commonConditions,
378
378
  )
379
379
 
380
380
  // Recursively compute difference on simplified predicates
381
381
  const simplifiedDifference = minusWherePredicates(
382
382
  fromWithoutCommon,
383
- subtractWithoutCommon
383
+ subtractWithoutCommon,
384
384
  )
385
385
 
386
386
  if (simplifiedDifference !== null) {
@@ -406,7 +406,7 @@ export function minusWherePredicates(
406
406
  */
407
407
  function minusSameFieldPredicates(
408
408
  fromPred: Func,
409
- subtractPred: Func
409
+ subtractPred: Func,
410
410
  ): BasicExpression<boolean> | null {
411
411
  // Extract field information
412
412
  const fromField =
@@ -439,8 +439,8 @@ function minusSameFieldPredicates(
439
439
  subtractInField.values,
440
440
  v,
441
441
  subtractInField.primitiveSet ?? null,
442
- subtractInField.areAllPrimitives
443
- )
442
+ subtractInField.areAllPrimitives,
443
+ ),
444
444
  )
445
445
 
446
446
  if (remainingValues.length === 0) {
@@ -468,7 +468,7 @@ function minusSameFieldPredicates(
468
468
  const subtractValue = (subtractField as { ref: PropRef; value: any }).value
469
469
 
470
470
  const remainingValues = fromInField.values.filter(
471
- (v) => !areValuesEqual(v, subtractValue)
471
+ (v) => !areValuesEqual(v, subtractValue),
472
472
  )
473
473
 
474
474
  if (remainingValues.length === 0) {
@@ -517,7 +517,7 @@ function minusSameFieldPredicates(
517
517
  fromPred,
518
518
  fromComp.value,
519
519
  subtractPred,
520
- subtractComp.value
520
+ subtractComp.value,
521
521
  )
522
522
  return result
523
523
  }
@@ -533,7 +533,7 @@ function minusRangePredicates(
533
533
  fromFunc: Func,
534
534
  fromValue: any,
535
535
  subtractFunc: Func,
536
- subtractValue: any
536
+ subtractValue: any,
537
537
  ): BasicExpression<boolean> | null {
538
538
  const fromOp = fromFunc.name as `gt` | `gte` | `lt` | `lte` | `eq`
539
539
  const subtractOp = subtractFunc.name as `gt` | `gte` | `lt` | `lte` | `eq`
@@ -712,7 +712,7 @@ function minusRangePredicates(
712
712
  */
713
713
  export function isOrderBySubset(
714
714
  subset: OrderBy | undefined,
715
- superset: OrderBy | undefined
715
+ superset: OrderBy | undefined,
716
716
  ): boolean {
717
717
  // No ordering requirement is always satisfied
718
718
  if (!subset || subset.length === 0) {
@@ -742,7 +742,7 @@ export function isOrderBySubset(
742
742
  if (
743
743
  !areCompareOptionsEqual(
744
744
  subClause.compareOptions,
745
- superClause.compareOptions
745
+ superClause.compareOptions,
746
746
  )
747
747
  ) {
748
748
  return false
@@ -756,6 +756,9 @@ export function isOrderBySubset(
756
756
  * Check if one limit is a subset of another.
757
757
  * Returns true if the subset limit requirements are satisfied by the superset limit.
758
758
  *
759
+ * Note: This function does NOT consider offset. For offset-aware subset checking,
760
+ * use `isOffsetLimitSubset` instead.
761
+ *
759
762
  * @example
760
763
  * isLimitSubset(10, 20) // true (requesting 10 items when 20 are available)
761
764
  * isLimitSubset(20, 10) // false (requesting 20 items when only 10 are available)
@@ -767,7 +770,7 @@ export function isOrderBySubset(
767
770
  */
768
771
  export function isLimitSubset(
769
772
  subset: number | undefined,
770
- superset: number | undefined
773
+ superset: number | undefined,
771
774
  ): boolean {
772
775
  // Unlimited superset satisfies any limit requirement
773
776
  if (superset === undefined) {
@@ -785,7 +788,57 @@ export function isLimitSubset(
785
788
  }
786
789
 
787
790
  /**
788
- * Check if one predicate (where + orderBy + limit) is a subset of another.
791
+ * Check if one offset+limit range is a subset of another.
792
+ * Returns true if the subset range is fully contained within the superset range.
793
+ *
794
+ * A query with `{limit: 10, offset: 0}` loads rows [0, 10).
795
+ * A query with `{limit: 10, offset: 20}` loads rows [20, 30).
796
+ *
797
+ * For subset to be satisfied by superset:
798
+ * - Superset must start at or before subset (superset.offset <= subset.offset)
799
+ * - Superset must end at or after subset (superset.offset + superset.limit >= subset.offset + subset.limit)
800
+ *
801
+ * @example
802
+ * isOffsetLimitSubset({ offset: 0, limit: 5 }, { offset: 0, limit: 10 }) // true
803
+ * isOffsetLimitSubset({ offset: 5, limit: 5 }, { offset: 0, limit: 10 }) // true (rows 5-9 within 0-9)
804
+ * isOffsetLimitSubset({ offset: 5, limit: 10 }, { offset: 0, limit: 10 }) // false (rows 5-14 exceed 0-9)
805
+ * isOffsetLimitSubset({ offset: 20, limit: 10 }, { offset: 0, limit: 10 }) // false (rows 20-29 outside 0-9)
806
+ *
807
+ * @param subset - The offset+limit requirements to check
808
+ * @param superset - The offset+limit that might satisfy the requirements
809
+ * @returns true if subset range is fully contained within superset range
810
+ */
811
+ export function isOffsetLimitSubset(
812
+ subset: { offset?: number; limit?: number },
813
+ superset: { offset?: number; limit?: number },
814
+ ): boolean {
815
+ const subsetOffset = subset.offset ?? 0
816
+ const supersetOffset = superset.offset ?? 0
817
+
818
+ // Superset must start at or before subset
819
+ if (supersetOffset > subsetOffset) {
820
+ return false
821
+ }
822
+
823
+ // If superset is unlimited, it covers everything from its offset onwards
824
+ if (superset.limit === undefined) {
825
+ return true
826
+ }
827
+
828
+ // If subset is unlimited but superset has a limit, subset can't be satisfied
829
+ if (subset.limit === undefined) {
830
+ return false
831
+ }
832
+
833
+ // Both have limits - check if subset range is within superset range
834
+ const subsetEnd = subsetOffset + subset.limit
835
+ const supersetEnd = supersetOffset + superset.limit
836
+
837
+ return subsetEnd <= supersetEnd
838
+ }
839
+
840
+ /**
841
+ * Check if one predicate (where + orderBy + limit + offset) is a subset of another.
789
842
  * Returns true if all aspects of the subset predicate are satisfied by the superset.
790
843
  *
791
844
  * @example
@@ -800,7 +853,7 @@ export function isLimitSubset(
800
853
  */
801
854
  export function isPredicateSubset(
802
855
  subset: LoadSubsetOptions,
803
- superset: LoadSubsetOptions
856
+ superset: LoadSubsetOptions,
804
857
  ): boolean {
805
858
  // When the superset has a limit, we can only determine subset relationship
806
859
  // if the where clauses are equal (not just subset relationship).
@@ -813,9 +866,9 @@ export function isPredicateSubset(
813
866
  // The top 10 items matching 'search%' might include items outside the overall top 10.
814
867
  //
815
868
  // However, if the where clauses are equal, then the subset relationship can
816
- // be determined by orderBy and limit alone:
817
- // Example: superset = {where: status='active', limit: 10, orderBy: desc}
818
- // subset = {where: status='active', limit: 5, orderBy: desc}
869
+ // be determined by orderBy, limit, and offset:
870
+ // Example: superset = {where: status='active', limit: 10, offset: 0, orderBy: desc}
871
+ // subset = {where: status='active', limit: 5, offset: 0, orderBy: desc}
819
872
  // The top 5 active items ARE contained in the top 10 active items.
820
873
  if (superset.limit !== undefined) {
821
874
  // For limited supersets, where clauses must be equal
@@ -824,15 +877,17 @@ export function isPredicateSubset(
824
877
  }
825
878
  return (
826
879
  isOrderBySubset(subset.orderBy, superset.orderBy) &&
827
- isLimitSubset(subset.limit, superset.limit)
880
+ isOffsetLimitSubset(subset, superset)
828
881
  )
829
882
  }
830
883
 
831
884
  // For unlimited supersets, use the normal subset logic
885
+ // Still need to consider offset - an unlimited query with offset only covers
886
+ // rows from that offset onwards
832
887
  return (
833
888
  isWhereSubset(subset.where, superset.where) &&
834
889
  isOrderBySubset(subset.orderBy, superset.orderBy) &&
835
- isLimitSubset(subset.limit, superset.limit)
890
+ isOffsetLimitSubset(subset, superset)
836
891
  )
837
892
  }
838
893
 
@@ -842,7 +897,7 @@ export function isPredicateSubset(
842
897
  */
843
898
  function areWhereClausesEqual(
844
899
  a: BasicExpression<boolean> | undefined,
845
- b: BasicExpression<boolean> | undefined
900
+ b: BasicExpression<boolean> | undefined,
846
901
  ): boolean {
847
902
  if (a === undefined && b === undefined) {
848
903
  return true
@@ -863,7 +918,7 @@ function areWhereClausesEqual(
863
918
  */
864
919
  function findCommonConditions(
865
920
  predicate1: BasicExpression<boolean>,
866
- predicate2: BasicExpression<boolean>
921
+ predicate2: BasicExpression<boolean>,
867
922
  ): Array<BasicExpression<boolean>> {
868
923
  const conditions1 = extractAllConditions(predicate1)
869
924
  const conditions2 = extractAllConditions(predicate2)
@@ -889,7 +944,7 @@ function findCommonConditions(
889
944
  * Extract all individual conditions from a predicate, flattening AND operations.
890
945
  */
891
946
  function extractAllConditions(
892
- predicate: BasicExpression<boolean>
947
+ predicate: BasicExpression<boolean>,
893
948
  ): Array<BasicExpression<boolean>> {
894
949
  if (predicate.type === `func` && predicate.name === `and`) {
895
950
  const conditions: Array<BasicExpression<boolean>> = []
@@ -908,14 +963,14 @@ function extractAllConditions(
908
963
  */
909
964
  function removeConditions(
910
965
  predicate: BasicExpression<boolean>,
911
- conditionsToRemove: Array<BasicExpression<boolean>>
966
+ conditionsToRemove: Array<BasicExpression<boolean>>,
912
967
  ): BasicExpression<boolean> | undefined {
913
968
  if (predicate.type === `func` && predicate.name === `and`) {
914
969
  const remainingArgs = predicate.args.filter(
915
970
  (arg) =>
916
971
  !conditionsToRemove.some((cond) =>
917
- areExpressionsEqual(arg as BasicExpression<boolean>, cond)
918
- )
972
+ areExpressionsEqual(arg as BasicExpression<boolean>, cond),
973
+ ),
919
974
  )
920
975
 
921
976
  if (remainingArgs.length === 0) {
@@ -940,7 +995,7 @@ function removeConditions(
940
995
  * Flattens nested AND operations to avoid unnecessary nesting.
941
996
  */
942
997
  function combineConditions(
943
- conditions: Array<BasicExpression<boolean>>
998
+ conditions: Array<BasicExpression<boolean>>,
944
999
  ): BasicExpression<boolean> {
945
1000
  if (conditions.length === 0) {
946
1001
  return { type: `val`, value: true } as BasicExpression<boolean>
@@ -977,7 +1032,7 @@ function combineConditions(
977
1032
  function findPredicateWithOperator(
978
1033
  predicates: Array<BasicExpression<boolean>>,
979
1034
  operator: string,
980
- value: any
1035
+ value: any,
981
1036
  ): BasicExpression<boolean> | undefined {
982
1037
  return predicates.find((p) => {
983
1038
  if (p.type === `func`) {
@@ -1012,7 +1067,7 @@ function areExpressionsEqual(a: BasicExpression, b: BasicExpression): boolean {
1012
1067
  return false
1013
1068
  }
1014
1069
  return aFunc.args.every((arg, i) =>
1015
- areExpressionsEqual(arg, bFunc.args[i]!)
1070
+ areExpressionsEqual(arg, bFunc.args[i]!),
1016
1071
  )
1017
1072
  }
1018
1073
 
@@ -1086,7 +1141,7 @@ function arrayIncludesWithSet(
1086
1141
  array: Array<any>,
1087
1142
  value: any,
1088
1143
  primitiveSet: Set<any> | null,
1089
- arrayIsAllPrimitives?: boolean
1144
+ arrayIsAllPrimitives?: boolean,
1090
1145
  ): boolean {
1091
1146
  // Fast path: use pre-built Set for O(1) lookup
1092
1147
  if (primitiveSet) {
@@ -1124,7 +1179,7 @@ function minValue(a: any, b: any): any {
1124
1179
 
1125
1180
  function areCompareOptionsEqual(
1126
1181
  a: { direction?: `asc` | `desc`; [key: string]: any },
1127
- b: { direction?: `asc` | `desc`; [key: string]: any }
1182
+ b: { direction?: `asc` | `desc`; [key: string]: any },
1128
1183
  ): boolean {
1129
1184
  // For now, just compare direction - could be enhanced for other options
1130
1185
  return a.direction === b.direction
@@ -1215,7 +1270,7 @@ function isComparisonSubset(
1215
1270
  subsetFunc: Func,
1216
1271
  subsetValue: any,
1217
1272
  supersetFunc: Func,
1218
- supersetValue: any
1273
+ supersetValue: any,
1219
1274
  ): boolean {
1220
1275
  const subOp = subsetFunc.name
1221
1276
  const superOp = supersetFunc.name
@@ -1283,7 +1338,7 @@ function isComparisonSubset(
1283
1338
  }
1284
1339
 
1285
1340
  function groupPredicatesByField(
1286
- predicates: Array<BasicExpression<boolean>>
1341
+ predicates: Array<BasicExpression<boolean>>,
1287
1342
  ): Map<string | null, Array<BasicExpression<boolean>>> {
1288
1343
  const groups = new Map<string | null, Array<BasicExpression<boolean>>>()
1289
1344
 
@@ -1310,7 +1365,7 @@ function groupPredicatesByField(
1310
1365
  }
1311
1366
 
1312
1367
  function unionSameFieldPredicates(
1313
- predicates: Array<BasicExpression<boolean>>
1368
+ predicates: Array<BasicExpression<boolean>>,
1314
1369
  ): BasicExpression<boolean> | null {
1315
1370
  if (predicates.length === 1) {
1316
1371
  return predicates[0]!
@@ -1436,7 +1491,7 @@ function unionSameFieldPredicates(
1436
1491
  return (p as Func).name === `in`
1437
1492
  }
1438
1493
  return false
1439
- })!
1494
+ })!,
1440
1495
  )
1441
1496
  }
1442
1497
 
@@ -3,9 +3,9 @@ import {
3
3
  isWhereSubset,
4
4
  minusWherePredicates,
5
5
  unionWherePredicates,
6
- } from "./predicate-utils.js"
7
- import type { BasicExpression } from "./ir.js"
8
- import type { LoadSubsetOptions } from "../types.js"
6
+ } from './predicate-utils.js'
7
+ import type { BasicExpression } from './ir.js'
8
+ import type { LoadSubsetOptions } from '../types.js'
9
9
 
10
10
  /**
11
11
  * Deduplicated wrapper for a loadSubset function.
@@ -34,7 +34,7 @@ import type { LoadSubsetOptions } from "../types.js"
34
34
  export class DeduplicatedLoadSubset {
35
35
  // The underlying loadSubset function to wrap
36
36
  private readonly _loadSubset: (
37
- options: LoadSubsetOptions
37
+ options: LoadSubsetOptions,
38
38
  ) => true | Promise<void>
39
39
 
40
40
  // An optional callback function that is invoked when a loadSubset call is deduplicated.
@@ -101,7 +101,7 @@ export class DeduplicatedLoadSubset {
101
101
  // Check against limited calls
102
102
  if (options.limit !== undefined) {
103
103
  const alreadyLoaded = this.limitedCalls.some((loaded) =>
104
- isPredicateSubset(options, loaded)
104
+ isPredicateSubset(options, loaded),
105
105
  )
106
106
 
107
107
  if (alreadyLoaded) {
@@ -113,7 +113,7 @@ export class DeduplicatedLoadSubset {
113
113
  // Check against in-flight calls using the same subset logic as resolved calls
114
114
  // This prevents duplicate requests when concurrent calls have subset relationships
115
115
  const matchingInflight = this.inflightCalls.find((inflight) =>
116
- isPredicateSubset(options, inflight.options)
116
+ isPredicateSubset(options, inflight.options),
117
117
  )
118
118
 
119
119
  if (matchingInflight !== undefined) {
package/src/scheduler.ts CHANGED
@@ -54,7 +54,7 @@ export class Scheduler {
54
54
  * Get or create the state bucket for a context.
55
55
  */
56
56
  private getOrCreateContext(
57
- contextId: SchedulerContextId
57
+ contextId: SchedulerContextId,
58
58
  ): SchedulerContextState {
59
59
  let context = this.contexts.get(contextId)
60
60
  if (!context) {
@@ -166,8 +166,8 @@ export class Scheduler {
166
166
  if (!ranThisPass) {
167
167
  throw new Error(
168
168
  `Scheduler detected unresolved dependencies for context ${String(
169
- contextId
170
- )}.`
169
+ contextId,
170
+ )}.`,
171
171
  )
172
172
  }
173
173
  }
@@ -1,6 +1,6 @@
1
- import { LiteDebouncer } from "@tanstack/pacer-lite/lite-debouncer"
2
- import type { DebounceStrategy, DebounceStrategyOptions } from "./types"
3
- import type { Transaction } from "../transactions"
1
+ import { LiteDebouncer } from '@tanstack/pacer-lite/lite-debouncer'
2
+ import type { DebounceStrategy, DebounceStrategyOptions } from './types'
3
+ import type { Transaction } from '../transactions'
4
4
 
5
5
  /**
6
6
  * Creates a debounce strategy that delays transaction execution until after
@@ -26,18 +26,18 @@ import type { Transaction } from "../transactions"
26
26
  * ```
27
27
  */
28
28
  export function debounceStrategy(
29
- options: DebounceStrategyOptions
29
+ options: DebounceStrategyOptions,
30
30
  ): DebounceStrategy {
31
31
  const debouncer = new LiteDebouncer(
32
32
  (callback: () => Transaction) => callback(),
33
- options
33
+ options,
34
34
  )
35
35
 
36
36
  return {
37
37
  _type: `debounce`,
38
38
  options,
39
39
  execute: <T extends object = Record<string, unknown>>(
40
- fn: () => Transaction<T>
40
+ fn: () => Transaction<T>,
41
41
  ) => {
42
42
  debouncer.maybeExecute(fn as () => Transaction)
43
43
  },
@@ -1,7 +1,7 @@
1
1
  // Export all strategy factories
2
- export { debounceStrategy } from "./debounceStrategy"
3
- export { queueStrategy } from "./queueStrategy"
4
- export { throttleStrategy } from "./throttleStrategy"
2
+ export { debounceStrategy } from './debounceStrategy'
3
+ export { queueStrategy } from './queueStrategy'
4
+ export { throttleStrategy } from './throttleStrategy'
5
5
 
6
6
  // Export strategy types
7
7
  export type {
@@ -14,4 +14,4 @@ export type {
14
14
  ThrottleStrategy,
15
15
  ThrottleStrategyOptions,
16
16
  StrategyOptions,
17
- } from "./types"
17
+ } from './types'
@@ -1,6 +1,6 @@
1
- import { LiteQueuer } from "@tanstack/pacer-lite/lite-queuer"
2
- import type { QueueStrategy, QueueStrategyOptions } from "./types"
3
- import type { Transaction } from "../transactions"
1
+ import { LiteQueuer } from '@tanstack/pacer-lite/lite-queuer'
2
+ import type { QueueStrategy, QueueStrategyOptions } from './types'
3
+ import type { Transaction } from '../transactions'
4
4
 
5
5
  /**
6
6
  * Creates a queue strategy that processes all mutations in order with proper serialization.
@@ -71,14 +71,14 @@ export function queueStrategy(options?: QueueStrategyOptions): QueueStrategy {
71
71
  addItemsTo: options?.addItemsTo ?? `back`, // Default FIFO: add to back
72
72
  getItemsFrom: options?.getItemsFrom ?? `front`, // Default FIFO: get from front
73
73
  started: true, // Start processing immediately
74
- }
74
+ },
75
75
  )
76
76
 
77
77
  return {
78
78
  _type: `queue`,
79
79
  options,
80
80
  execute: <T extends object = Record<string, unknown>>(
81
- fn: () => Transaction<T>
81
+ fn: () => Transaction<T>,
82
82
  ) => {
83
83
  // Add the transaction-creating function to the queue
84
84
  queuer.addItem(fn as () => Transaction)
@@ -1,6 +1,6 @@
1
- import { LiteThrottler } from "@tanstack/pacer-lite/lite-throttler"
2
- import type { ThrottleStrategy, ThrottleStrategyOptions } from "./types"
3
- import type { Transaction } from "../transactions"
1
+ import { LiteThrottler } from '@tanstack/pacer-lite/lite-throttler'
2
+ import type { ThrottleStrategy, ThrottleStrategyOptions } from './types'
3
+ import type { Transaction } from '../transactions'
4
4
 
5
5
  /**
6
6
  * Creates a throttle strategy that ensures transactions are evenly spaced
@@ -46,18 +46,18 @@ import type { Transaction } from "../transactions"
46
46
  * ```
47
47
  */
48
48
  export function throttleStrategy(
49
- options: ThrottleStrategyOptions
49
+ options: ThrottleStrategyOptions,
50
50
  ): ThrottleStrategy {
51
51
  const throttler = new LiteThrottler(
52
52
  (callback: () => Transaction) => callback(),
53
- options
53
+ options,
54
54
  )
55
55
 
56
56
  return {
57
57
  _type: `throttle`,
58
58
  options,
59
59
  execute: <T extends object = Record<string, unknown>>(
60
- fn: () => Transaction<T>
60
+ fn: () => Transaction<T>,
61
61
  ) => {
62
62
  throttler.maybeExecute(fn as () => Transaction)
63
63
  },
@@ -1,4 +1,4 @@
1
- import type { Transaction } from "../transactions"
1
+ import type { Transaction } from '../transactions'
2
2
 
3
3
  /**
4
4
  * Base strategy interface that all strategy implementations must conform to
@@ -13,7 +13,7 @@ export interface BaseStrategy<TName extends string = string> {
13
13
  * @returns The result of the function execution (if applicable)
14
14
  */
15
15
  execute: <T extends object = Record<string, unknown>>(
16
- fn: () => Transaction<T>
16
+ fn: () => Transaction<T>,
17
17
  ) => void | Promise<void>
18
18
 
19
19
  /**
@@ -1,20 +1,20 @@
1
- import { createDeferred } from "./deferred"
2
- import "./duplicate-instance-check"
1
+ import { createDeferred } from './deferred'
2
+ import './duplicate-instance-check'
3
3
  import {
4
4
  MissingMutationFunctionError,
5
5
  TransactionAlreadyCompletedRollbackError,
6
6
  TransactionNotPendingCommitError,
7
7
  TransactionNotPendingMutateError,
8
- } from "./errors"
9
- import { transactionScopedScheduler } from "./scheduler.js"
10
- import type { Deferred } from "./deferred"
8
+ } from './errors'
9
+ import { transactionScopedScheduler } from './scheduler.js'
10
+ import type { Deferred } from './deferred'
11
11
  import type {
12
12
  MutationFn,
13
13
  PendingMutation,
14
14
  TransactionConfig,
15
15
  TransactionState,
16
16
  TransactionWithMutations,
17
- } from "./types"
17
+ } from './types'
18
18
 
19
19
  const transactions: Array<Transaction<any>> = []
20
20
  let transactionStack: Array<Transaction<any>> = []
@@ -41,7 +41,7 @@ let sequenceNumber = 0
41
41
  */
42
42
  function mergePendingMutations<T extends object>(
43
43
  existing: PendingMutation<T>,
44
- incoming: PendingMutation<T>
44
+ incoming: PendingMutation<T>,
45
45
  ): PendingMutation<T> | null {
46
46
  // Truth table implementation
47
47
  switch (`${existing.type}-${incoming.type}` as const) {
@@ -154,7 +154,7 @@ function mergePendingMutations<T extends object>(
154
154
  * await tx.commit()
155
155
  */
156
156
  export function createTransaction<T extends object = Record<string, unknown>>(
157
- config: TransactionConfig<T>
157
+ config: TransactionConfig<T>,
158
158
  ): Transaction<T> {
159
159
  const newTransaction = new Transaction<T>(config)
160
160
  transactions.push(newTransaction)
@@ -327,7 +327,7 @@ class Transaction<T extends object = Record<string, unknown>> {
327
327
  applyMutations(mutations: Array<PendingMutation<any>>): void {
328
328
  for (const newMutation of mutations) {
329
329
  const existingIndex = this.mutations.findIndex(
330
- (m) => m.globalKey === newMutation.globalKey
330
+ (m) => m.globalKey === newMutation.globalKey,
331
331
  )
332
332
 
333
333
  if (existingIndex >= 0) {