@tanstack/db 0.5.32 → 0.6.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 (287) hide show
  1. package/dist/cjs/collection/change-events.cjs.map +1 -1
  2. package/dist/cjs/collection/change-events.d.cts +3 -2
  3. package/dist/cjs/collection/changes.cjs +13 -4
  4. package/dist/cjs/collection/changes.cjs.map +1 -1
  5. package/dist/cjs/collection/changes.d.cts +10 -1
  6. package/dist/cjs/collection/cleanup-queue.cjs +89 -0
  7. package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
  8. package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
  9. package/dist/cjs/collection/events.cjs +14 -0
  10. package/dist/cjs/collection/events.cjs.map +1 -1
  11. package/dist/cjs/collection/events.d.cts +39 -1
  12. package/dist/cjs/collection/index.cjs +66 -28
  13. package/dist/cjs/collection/index.cjs.map +1 -1
  14. package/dist/cjs/collection/index.d.cts +49 -36
  15. package/dist/cjs/collection/indexes.cjs +211 -62
  16. package/dist/cjs/collection/indexes.cjs.map +1 -1
  17. package/dist/cjs/collection/indexes.d.cts +27 -17
  18. package/dist/cjs/collection/lifecycle.cjs +5 -22
  19. package/dist/cjs/collection/lifecycle.cjs.map +1 -1
  20. package/dist/cjs/collection/lifecycle.d.cts +0 -1
  21. package/dist/cjs/collection/mutations.cjs +18 -0
  22. package/dist/cjs/collection/mutations.cjs.map +1 -1
  23. package/dist/cjs/collection/mutations.d.cts +1 -0
  24. package/dist/cjs/collection/state.cjs +381 -53
  25. package/dist/cjs/collection/state.cjs.map +1 -1
  26. package/dist/cjs/collection/state.d.cts +65 -1
  27. package/dist/cjs/collection/subscription.cjs +6 -0
  28. package/dist/cjs/collection/subscription.cjs.map +1 -1
  29. package/dist/cjs/collection/subscription.d.cts +4 -0
  30. package/dist/cjs/collection/sync.cjs +108 -1
  31. package/dist/cjs/collection/sync.cjs.map +1 -1
  32. package/dist/cjs/collection/sync.d.cts +2 -0
  33. package/dist/cjs/collection/transaction-metadata.cjs +5 -0
  34. package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
  35. package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
  36. package/dist/cjs/errors.cjs +8 -0
  37. package/dist/cjs/errors.cjs.map +1 -1
  38. package/dist/cjs/errors.d.cts +3 -0
  39. package/dist/cjs/index.cjs +24 -4
  40. package/dist/cjs/index.cjs.map +1 -1
  41. package/dist/cjs/index.d.cts +12 -3
  42. package/dist/cjs/indexes/auto-index.cjs +13 -6
  43. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  44. package/dist/cjs/indexes/base-index.cjs +0 -3
  45. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  46. package/dist/cjs/indexes/base-index.d.cts +2 -6
  47. package/dist/cjs/indexes/basic-index.cjs +361 -0
  48. package/dist/cjs/indexes/basic-index.cjs.map +1 -0
  49. package/dist/cjs/indexes/basic-index.d.cts +102 -0
  50. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  51. package/dist/cjs/indexes/btree-index.d.cts +1 -1
  52. package/dist/cjs/indexes/index-options.d.cts +8 -9
  53. package/dist/cjs/indexes/index-registry.cjs +89 -0
  54. package/dist/cjs/indexes/index-registry.cjs.map +1 -0
  55. package/dist/cjs/indexes/index-registry.d.cts +61 -0
  56. package/dist/cjs/local-only.cjs +5 -0
  57. package/dist/cjs/local-only.cjs.map +1 -1
  58. package/dist/cjs/query/builder/functions.cjs +27 -11
  59. package/dist/cjs/query/builder/functions.cjs.map +1 -1
  60. package/dist/cjs/query/builder/functions.d.cts +25 -3
  61. package/dist/cjs/query/builder/index.cjs +200 -39
  62. package/dist/cjs/query/builder/index.cjs.map +1 -1
  63. package/dist/cjs/query/builder/index.d.cts +4 -3
  64. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
  65. package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
  66. package/dist/cjs/query/builder/types.d.cts +84 -19
  67. package/dist/cjs/query/compiler/evaluators.cjs +51 -0
  68. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
  69. package/dist/cjs/query/compiler/group-by.cjs +100 -28
  70. package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
  71. package/dist/cjs/query/compiler/group-by.d.cts +4 -2
  72. package/dist/cjs/query/compiler/index.cjs +283 -11
  73. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  74. package/dist/cjs/query/compiler/index.d.cts +30 -2
  75. package/dist/cjs/query/compiler/order-by.cjs +29 -10
  76. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  77. package/dist/cjs/query/compiler/order-by.d.cts +1 -1
  78. package/dist/cjs/query/compiler/select.cjs +8 -0
  79. package/dist/cjs/query/compiler/select.cjs.map +1 -1
  80. package/dist/cjs/query/effect.cjs +602 -0
  81. package/dist/cjs/query/effect.cjs.map +1 -0
  82. package/dist/cjs/query/effect.d.cts +94 -0
  83. package/dist/cjs/query/index.d.cts +2 -1
  84. package/dist/cjs/query/ir.cjs +18 -1
  85. package/dist/cjs/query/ir.cjs.map +1 -1
  86. package/dist/cjs/query/ir.d.cts +21 -1
  87. package/dist/cjs/query/live/collection-config-builder.cjs +493 -66
  88. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  89. package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
  90. package/dist/cjs/query/live/collection-subscriber.cjs +33 -100
  91. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  92. package/dist/cjs/query/live/collection-subscriber.d.cts +0 -1
  93. package/dist/cjs/query/live/types.d.cts +3 -3
  94. package/dist/cjs/query/live/utils.cjs +219 -0
  95. package/dist/cjs/query/live/utils.cjs.map +1 -0
  96. package/dist/cjs/query/live/utils.d.cts +110 -0
  97. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  98. package/dist/cjs/query/live-query-collection.d.cts +9 -6
  99. package/dist/cjs/query/query-once.cjs.map +1 -1
  100. package/dist/cjs/query/query-once.d.cts +7 -5
  101. package/dist/cjs/query/subset-dedupe.cjs +9 -3
  102. package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
  103. package/dist/cjs/types.d.cts +42 -8
  104. package/dist/cjs/utils/array-utils.cjs +27 -0
  105. package/dist/cjs/utils/array-utils.cjs.map +1 -0
  106. package/dist/cjs/utils/array-utils.d.cts +16 -0
  107. package/dist/cjs/utils/comparison.cjs +11 -0
  108. package/dist/cjs/utils/comparison.cjs.map +1 -1
  109. package/dist/cjs/utils/index-optimization.cjs +4 -0
  110. package/dist/cjs/utils/index-optimization.cjs.map +1 -1
  111. package/dist/cjs/utils.cjs +7 -9
  112. package/dist/cjs/utils.cjs.map +1 -1
  113. package/dist/cjs/utils.d.cts +6 -1
  114. package/dist/cjs/virtual-props.cjs +33 -0
  115. package/dist/cjs/virtual-props.cjs.map +1 -0
  116. package/dist/cjs/virtual-props.d.cts +196 -0
  117. package/dist/esm/collection/change-events.d.ts +3 -2
  118. package/dist/esm/collection/change-events.js.map +1 -1
  119. package/dist/esm/collection/changes.d.ts +10 -1
  120. package/dist/esm/collection/changes.js +13 -4
  121. package/dist/esm/collection/changes.js.map +1 -1
  122. package/dist/esm/collection/cleanup-queue.d.ts +30 -0
  123. package/dist/esm/collection/cleanup-queue.js +89 -0
  124. package/dist/esm/collection/cleanup-queue.js.map +1 -0
  125. package/dist/esm/collection/events.d.ts +39 -1
  126. package/dist/esm/collection/events.js +14 -0
  127. package/dist/esm/collection/events.js.map +1 -1
  128. package/dist/esm/collection/index.d.ts +49 -36
  129. package/dist/esm/collection/index.js +67 -29
  130. package/dist/esm/collection/index.js.map +1 -1
  131. package/dist/esm/collection/indexes.d.ts +27 -17
  132. package/dist/esm/collection/indexes.js +211 -62
  133. package/dist/esm/collection/indexes.js.map +1 -1
  134. package/dist/esm/collection/lifecycle.d.ts +0 -1
  135. package/dist/esm/collection/lifecycle.js +5 -22
  136. package/dist/esm/collection/lifecycle.js.map +1 -1
  137. package/dist/esm/collection/mutations.d.ts +1 -0
  138. package/dist/esm/collection/mutations.js +18 -0
  139. package/dist/esm/collection/mutations.js.map +1 -1
  140. package/dist/esm/collection/state.d.ts +65 -1
  141. package/dist/esm/collection/state.js +381 -53
  142. package/dist/esm/collection/state.js.map +1 -1
  143. package/dist/esm/collection/subscription.d.ts +4 -0
  144. package/dist/esm/collection/subscription.js +6 -0
  145. package/dist/esm/collection/subscription.js.map +1 -1
  146. package/dist/esm/collection/sync.d.ts +2 -0
  147. package/dist/esm/collection/sync.js +108 -1
  148. package/dist/esm/collection/sync.js.map +1 -1
  149. package/dist/esm/collection/transaction-metadata.d.ts +1 -0
  150. package/dist/esm/collection/transaction-metadata.js +5 -0
  151. package/dist/esm/collection/transaction-metadata.js.map +1 -0
  152. package/dist/esm/errors.d.ts +3 -0
  153. package/dist/esm/errors.js +8 -0
  154. package/dist/esm/errors.js.map +1 -1
  155. package/dist/esm/index.d.ts +12 -3
  156. package/dist/esm/index.js +27 -7
  157. package/dist/esm/index.js.map +1 -1
  158. package/dist/esm/indexes/auto-index.js +13 -6
  159. package/dist/esm/indexes/auto-index.js.map +1 -1
  160. package/dist/esm/indexes/base-index.d.ts +2 -6
  161. package/dist/esm/indexes/base-index.js +1 -4
  162. package/dist/esm/indexes/base-index.js.map +1 -1
  163. package/dist/esm/indexes/basic-index.d.ts +102 -0
  164. package/dist/esm/indexes/basic-index.js +361 -0
  165. package/dist/esm/indexes/basic-index.js.map +1 -0
  166. package/dist/esm/indexes/btree-index.d.ts +1 -1
  167. package/dist/esm/indexes/btree-index.js.map +1 -1
  168. package/dist/esm/indexes/index-options.d.ts +8 -9
  169. package/dist/esm/indexes/index-registry.d.ts +61 -0
  170. package/dist/esm/indexes/index-registry.js +89 -0
  171. package/dist/esm/indexes/index-registry.js.map +1 -0
  172. package/dist/esm/local-only.js +5 -0
  173. package/dist/esm/local-only.js.map +1 -1
  174. package/dist/esm/query/builder/functions.d.ts +25 -3
  175. package/dist/esm/query/builder/functions.js +27 -11
  176. package/dist/esm/query/builder/functions.js.map +1 -1
  177. package/dist/esm/query/builder/index.d.ts +4 -3
  178. package/dist/esm/query/builder/index.js +201 -40
  179. package/dist/esm/query/builder/index.js.map +1 -1
  180. package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
  181. package/dist/esm/query/builder/ref-proxy.js.map +1 -1
  182. package/dist/esm/query/builder/types.d.ts +84 -19
  183. package/dist/esm/query/compiler/evaluators.js +51 -0
  184. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  185. package/dist/esm/query/compiler/group-by.d.ts +4 -2
  186. package/dist/esm/query/compiler/group-by.js +101 -29
  187. package/dist/esm/query/compiler/group-by.js.map +1 -1
  188. package/dist/esm/query/compiler/index.d.ts +30 -2
  189. package/dist/esm/query/compiler/index.js +285 -13
  190. package/dist/esm/query/compiler/index.js.map +1 -1
  191. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  192. package/dist/esm/query/compiler/order-by.js +30 -11
  193. package/dist/esm/query/compiler/order-by.js.map +1 -1
  194. package/dist/esm/query/compiler/select.js +8 -0
  195. package/dist/esm/query/compiler/select.js.map +1 -1
  196. package/dist/esm/query/effect.d.ts +94 -0
  197. package/dist/esm/query/effect.js +602 -0
  198. package/dist/esm/query/effect.js.map +1 -0
  199. package/dist/esm/query/index.d.ts +2 -1
  200. package/dist/esm/query/ir.d.ts +21 -1
  201. package/dist/esm/query/ir.js +18 -1
  202. package/dist/esm/query/ir.js.map +1 -1
  203. package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
  204. package/dist/esm/query/live/collection-config-builder.js +492 -65
  205. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  206. package/dist/esm/query/live/collection-subscriber.d.ts +0 -1
  207. package/dist/esm/query/live/collection-subscriber.js +31 -98
  208. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  209. package/dist/esm/query/live/types.d.ts +3 -3
  210. package/dist/esm/query/live/utils.d.ts +110 -0
  211. package/dist/esm/query/live/utils.js +219 -0
  212. package/dist/esm/query/live/utils.js.map +1 -0
  213. package/dist/esm/query/live-query-collection.d.ts +9 -6
  214. package/dist/esm/query/live-query-collection.js.map +1 -1
  215. package/dist/esm/query/query-once.d.ts +7 -5
  216. package/dist/esm/query/query-once.js.map +1 -1
  217. package/dist/esm/query/subset-dedupe.js +9 -3
  218. package/dist/esm/query/subset-dedupe.js.map +1 -1
  219. package/dist/esm/types.d.ts +42 -8
  220. package/dist/esm/utils/array-utils.d.ts +16 -0
  221. package/dist/esm/utils/array-utils.js +27 -0
  222. package/dist/esm/utils/array-utils.js.map +1 -0
  223. package/dist/esm/utils/comparison.js +11 -0
  224. package/dist/esm/utils/comparison.js.map +1 -1
  225. package/dist/esm/utils/index-optimization.js +4 -0
  226. package/dist/esm/utils/index-optimization.js.map +1 -1
  227. package/dist/esm/utils.d.ts +6 -1
  228. package/dist/esm/utils.js +7 -9
  229. package/dist/esm/utils.js.map +1 -1
  230. package/dist/esm/virtual-props.d.ts +196 -0
  231. package/dist/esm/virtual-props.js +33 -0
  232. package/dist/esm/virtual-props.js.map +1 -0
  233. package/package.json +2 -2
  234. package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
  235. package/src/collection/change-events.ts +13 -9
  236. package/src/collection/changes.ts +30 -7
  237. package/src/collection/cleanup-queue.ts +105 -0
  238. package/src/collection/events.ts +65 -0
  239. package/src/collection/index.ts +110 -45
  240. package/src/collection/indexes.ts +283 -76
  241. package/src/collection/lifecycle.ts +5 -26
  242. package/src/collection/mutations.ts +21 -0
  243. package/src/collection/state.ts +545 -71
  244. package/src/collection/subscription.ts +7 -0
  245. package/src/collection/sync.ts +137 -0
  246. package/src/collection/transaction-metadata.ts +1 -0
  247. package/src/errors.ts +9 -0
  248. package/src/index.ts +57 -3
  249. package/src/indexes/auto-index.ts +18 -8
  250. package/src/indexes/base-index.ts +2 -10
  251. package/src/indexes/basic-index.ts +507 -0
  252. package/src/indexes/btree-index.ts +1 -1
  253. package/src/indexes/index-options.ts +17 -37
  254. package/src/indexes/index-registry.ts +174 -0
  255. package/src/local-only.ts +7 -0
  256. package/src/query/builder/functions.ts +84 -7
  257. package/src/query/builder/index.ts +329 -9
  258. package/src/query/builder/ref-proxy.ts +22 -4
  259. package/src/query/builder/types.ts +257 -62
  260. package/src/query/compiler/evaluators.ts +57 -0
  261. package/src/query/compiler/group-by.ts +156 -35
  262. package/src/query/compiler/index.ts +445 -15
  263. package/src/query/compiler/order-by.ts +51 -12
  264. package/src/query/compiler/select.ts +9 -0
  265. package/src/query/effect.ts +1119 -0
  266. package/src/query/index.ts +7 -0
  267. package/src/query/ir.ts +23 -2
  268. package/src/query/live/collection-config-builder.ts +778 -104
  269. package/src/query/live/collection-subscriber.ts +40 -156
  270. package/src/query/live/types.ts +10 -4
  271. package/src/query/live/utils.ts +417 -0
  272. package/src/query/live-query-collection.ts +43 -18
  273. package/src/query/query-once.ts +31 -12
  274. package/src/query/subset-dedupe.ts +11 -7
  275. package/src/types.ts +49 -9
  276. package/src/utils/array-utils.ts +49 -0
  277. package/src/utils/comparison.ts +14 -0
  278. package/src/utils/index-optimization.ts +4 -0
  279. package/src/utils.ts +12 -9
  280. package/src/virtual-props.ts +282 -0
  281. package/dist/cjs/indexes/lazy-index.cjs +0 -190
  282. package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
  283. package/dist/cjs/indexes/lazy-index.d.cts +0 -96
  284. package/dist/esm/indexes/lazy-index.d.ts +0 -96
  285. package/dist/esm/indexes/lazy-index.js +0 -190
  286. package/dist/esm/indexes/lazy-index.js.map +0 -1
  287. package/src/indexes/lazy-index.ts +0 -251
@@ -6,7 +6,11 @@ import {
6
6
  } from './live/collection-registry.js'
7
7
  import type { LiveQueryCollectionUtils } from './live/collection-config-builder.js'
8
8
  import type { LiveQueryCollectionConfig } from './live/types.js'
9
- import type { InitialQueryBuilder, QueryBuilder } from './builder/index.js'
9
+ import type {
10
+ ExtractContext,
11
+ InitialQueryBuilder,
12
+ QueryBuilder,
13
+ } from './builder/index.js'
10
14
  import type { Collection } from '../collection/index.js'
11
15
  import type {
12
16
  CollectionConfig,
@@ -15,7 +19,13 @@ import type {
15
19
  SingleResult,
16
20
  UtilsRecord,
17
21
  } from '../types.js'
18
- import type { Context, GetResult } from './builder/types.js'
22
+ import type {
23
+ Context,
24
+ RootObjectResultConstraint,
25
+ RootQueryBuilder,
26
+ RootQueryFn,
27
+ RootQueryResult,
28
+ } from './builder/types.js'
19
29
 
20
30
  type CollectionConfigForContext<
21
31
  TContext extends Context,
@@ -60,10 +70,13 @@ type CollectionForContext<
60
70
  * @returns Collection options that can be passed to createCollection
61
71
  */
62
72
  export function liveQueryCollectionOptions<
63
- TContext extends Context,
64
- TResult extends object = GetResult<TContext>,
73
+ TQuery extends QueryBuilder<any>,
74
+ TContext extends Context = ExtractContext<TQuery>,
75
+ TResult extends object = RootQueryResult<TContext>,
65
76
  >(
66
- config: LiveQueryCollectionConfig<TContext, TResult>,
77
+ config: LiveQueryCollectionConfig<TContext, TResult> & {
78
+ query: RootQueryFn<TQuery> | RootQueryBuilder<TQuery>
79
+ },
67
80
  ): CollectionConfigForContext<TContext, TResult> & {
68
81
  utils: LiveQueryCollectionUtils
69
82
  } {
@@ -113,34 +126,42 @@ export function liveQueryCollectionOptions<
113
126
 
114
127
  // Overload 1: Accept just the query function
115
128
  export function createLiveQueryCollection<
116
- TContext extends Context,
117
- TResult extends object = GetResult<TContext>,
129
+ TQueryFn extends (q: InitialQueryBuilder) => QueryBuilder<any>,
130
+ TQuery extends QueryBuilder<any> = ReturnType<TQueryFn>,
118
131
  >(
119
- query: (q: InitialQueryBuilder) => QueryBuilder<TContext>,
120
- ): CollectionForContext<TContext, TResult> & {
132
+ query: TQueryFn & RootQueryFn<TQuery>,
133
+ ): CollectionForContext<
134
+ ExtractContext<TQuery>,
135
+ RootQueryResult<ExtractContext<TQuery>>
136
+ > & {
121
137
  utils: LiveQueryCollectionUtils
122
138
  }
123
139
 
124
140
  // Overload 2: Accept full config object with optional utilities
125
141
  export function createLiveQueryCollection<
126
- TContext extends Context,
127
- TResult extends object = GetResult<TContext>,
142
+ TQuery extends QueryBuilder<any>,
143
+ TContext extends Context = ExtractContext<TQuery>,
128
144
  TUtils extends UtilsRecord = {},
129
145
  >(
130
- config: LiveQueryCollectionConfig<TContext, TResult> & { utils?: TUtils },
131
- ): CollectionForContext<TContext, TResult> & {
146
+ config: LiveQueryCollectionConfig<TContext, RootQueryResult<TContext>> & {
147
+ query: RootQueryFn<TQuery> | RootQueryBuilder<TQuery>
148
+ utils?: TUtils
149
+ },
150
+ ): CollectionForContext<TContext, RootQueryResult<TContext>> & {
132
151
  utils: LiveQueryCollectionUtils & TUtils
133
152
  }
134
153
 
135
154
  // Implementation
136
155
  export function createLiveQueryCollection<
137
156
  TContext extends Context,
138
- TResult extends object = GetResult<TContext>,
157
+ TResult extends object = RootQueryResult<TContext>,
139
158
  TUtils extends UtilsRecord = {},
140
159
  >(
141
160
  configOrQuery:
142
161
  | (LiveQueryCollectionConfig<TContext, TResult> & { utils?: TUtils })
143
- | ((q: InitialQueryBuilder) => QueryBuilder<TContext>),
162
+ | ((
163
+ q: InitialQueryBuilder,
164
+ ) => QueryBuilder<TContext> & RootObjectResultConstraint<TContext>),
144
165
  ): CollectionForContext<TContext, TResult> & {
145
166
  utils: LiveQueryCollectionUtils & TUtils
146
167
  } {
@@ -150,9 +171,11 @@ export function createLiveQueryCollection<
150
171
  const config: LiveQueryCollectionConfig<TContext, TResult> = {
151
172
  query: configOrQuery as (
152
173
  q: InitialQueryBuilder,
153
- ) => QueryBuilder<TContext>,
174
+ ) => QueryBuilder<TContext> & RootObjectResultConstraint<TContext>,
154
175
  }
155
- const options = liveQueryCollectionOptions<TContext, TResult>(config)
176
+ // The implementation accepts both overload shapes, but TypeScript cannot
177
+ // preserve the overload-specific query-builder inference through this branch.
178
+ const options = liveQueryCollectionOptions(config as any)
156
179
  return bridgeToCreateCollection(options) as CollectionForContext<
157
180
  TContext,
158
181
  TResult
@@ -163,7 +186,9 @@ export function createLiveQueryCollection<
163
186
  TContext,
164
187
  TResult
165
188
  > & { utils?: TUtils }
166
- const options = liveQueryCollectionOptions<TContext, TResult>(config)
189
+ // Same overload implementation limitation as above: the config has already
190
+ // been validated by the public signatures, but the branch loses that precision.
191
+ const options = liveQueryCollectionOptions(config as any)
167
192
 
168
193
  // Merge custom utils if provided, preserving the getBuilder() method for dependency tracking
169
194
  if (config.utils) {
@@ -1,6 +1,16 @@
1
1
  import { createLiveQueryCollection } from './live-query-collection.js'
2
- import type { InitialQueryBuilder, QueryBuilder } from './builder/index.js'
3
- import type { Context, InferResultType } from './builder/types.js'
2
+ import type {
3
+ ExtractContext,
4
+ InitialQueryBuilder,
5
+ QueryBuilder,
6
+ } from './builder/index.js'
7
+ import type {
8
+ Context,
9
+ InferResultType,
10
+ RootObjectResultConstraint,
11
+ RootQueryBuilder,
12
+ RootQueryFn,
13
+ } from './builder/types.js'
4
14
 
5
15
  /**
6
16
  * Configuration options for queryOnce
@@ -10,8 +20,10 @@ export interface QueryOnceConfig<TContext extends Context> {
10
20
  * Query builder function that defines the query
11
21
  */
12
22
  query:
13
- | ((q: InitialQueryBuilder) => QueryBuilder<TContext>)
14
- | QueryBuilder<TContext>
23
+ | ((
24
+ q: InitialQueryBuilder,
25
+ ) => QueryBuilder<TContext> & RootObjectResultConstraint<TContext>)
26
+ | (QueryBuilder<TContext> & RootObjectResultConstraint<TContext>)
15
27
  // Future: timeout, signal, etc.
16
28
  }
17
29
 
@@ -44,9 +56,12 @@ export interface QueryOnceConfig<TContext extends Context> {
44
56
  * )
45
57
  * ```
46
58
  */
47
- export function queryOnce<TContext extends Context>(
48
- queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,
49
- ): Promise<InferResultType<TContext>>
59
+ export function queryOnce<
60
+ TQueryFn extends (q: InitialQueryBuilder) => QueryBuilder<any>,
61
+ TQuery extends QueryBuilder<any> = ReturnType<TQueryFn>,
62
+ >(
63
+ queryFn: TQueryFn & RootQueryFn<TQuery>,
64
+ ): Promise<InferResultType<ExtractContext<TQuery>>>
50
65
 
51
66
  // Overload 2: Config object form returning array (non-single result)
52
67
  /**
@@ -65,15 +80,19 @@ export function queryOnce<TContext extends Context>(
65
80
  * })
66
81
  * ```
67
82
  */
68
- export function queryOnce<TContext extends Context>(
69
- config: QueryOnceConfig<TContext>,
70
- ): Promise<InferResultType<TContext>>
83
+ export function queryOnce<TQuery extends QueryBuilder<any>>(
84
+ config: QueryOnceConfig<ExtractContext<TQuery>> & {
85
+ query: RootQueryFn<TQuery> | RootQueryBuilder<TQuery>
86
+ },
87
+ ): Promise<InferResultType<ExtractContext<TQuery>>>
71
88
 
72
89
  // Implementation
73
90
  export async function queryOnce<TContext extends Context>(
74
91
  configOrQuery:
75
92
  | QueryOnceConfig<TContext>
76
- | ((q: InitialQueryBuilder) => QueryBuilder<TContext>),
93
+ | ((
94
+ q: InitialQueryBuilder,
95
+ ) => QueryBuilder<TContext> & RootObjectResultConstraint<TContext>),
77
96
  ): Promise<InferResultType<TContext>> {
78
97
  // Normalize input
79
98
  const config: QueryOnceConfig<TContext> =
@@ -107,7 +126,7 @@ export async function queryOnce<TContext extends Context>(
107
126
  | undefined
108
127
  return first as InferResultType<TContext>
109
128
  }
110
- return collection.toArray as InferResultType<TContext>
129
+ return collection.toArray as unknown as InferResultType<TContext>
111
130
  } finally {
112
131
  // Always cleanup, even on error
113
132
  await collection.cleanup()
@@ -126,10 +126,8 @@ export class DeduplicatedLoadSubset {
126
126
  return prom
127
127
  }
128
128
 
129
- // Not fully covered by existing data load the missing subset.
130
- // We need two clones: trackingOptions preserves the original predicate for
131
- // accurate tracking (e.g., where=undefined means "all data"), while loadOptions
132
- // may be narrowed with a difference expression for the actual backend request.
129
+ // Preserve the original request for tracking and in-flight dedupe, but allow
130
+ // the backend request to be narrowed to only the missing subset.
133
131
  const trackingOptions = cloneOptions(options)
134
132
  const loadOptions = cloneOptions(options)
135
133
  if (this.unlimitedWhere !== undefined && options.limit === undefined) {
@@ -147,7 +145,6 @@ export class DeduplicatedLoadSubset {
147
145
 
148
146
  // Handle both sync (true) and async (Promise<void>) return values
149
147
  if (resultPromise === true) {
150
- // Sync return - update tracking with the original predicate
151
148
  this.updateTracking(trackingOptions)
152
149
  return true
153
150
  } else {
@@ -159,7 +156,7 @@ export class DeduplicatedLoadSubset {
159
156
 
160
157
  // We need to create a reference to the in-flight entry so we can remove it later
161
158
  const inflightEntry = {
162
- options: loadOptions, // Store load options for subset matching of in-flight requests
159
+ options: trackingOptions,
163
160
  promise: resultPromise
164
161
  .then((result) => {
165
162
  // Only update tracking if this request is still from the current generation
@@ -238,5 +235,12 @@ export class DeduplicatedLoadSubset {
238
235
  * would reflect the mutated values rather than what was actually loaded.
239
236
  */
240
237
  export function cloneOptions(options: LoadSubsetOptions): LoadSubsetOptions {
241
- return { ...options }
238
+ return {
239
+ ...options,
240
+ orderBy: options.orderBy?.map((clause) => ({
241
+ ...clause,
242
+ compareOptions: { ...clause.compareOptions },
243
+ })),
244
+ cursor: options.cursor ? { ...options.cursor } : undefined,
245
+ }
242
246
  }
package/src/types.ts CHANGED
@@ -4,7 +4,9 @@ import type { StandardSchemaV1 } from '@standard-schema/spec'
4
4
  import type { Transaction } from './transactions'
5
5
  import type { BasicExpression, OrderBy } from './query/ir.js'
6
6
  import type { EventEmitter } from './event-emitter.js'
7
+ import type { IndexConstructor } from './indexes/base-index.js'
7
8
  import type { SingleRowRefProxy } from './query/builder/ref-proxy.js'
9
+ import type { WithVirtualProps } from './virtual-props.js'
8
10
 
9
11
  /**
10
12
  * Interface for a collection-like object that provides the necessary methods
@@ -338,6 +340,7 @@ export interface SyncConfig<
338
340
  commit: () => void
339
341
  markReady: () => void
340
342
  truncate: () => void
343
+ metadata?: SyncMetadataApi<TKey>
341
344
  }) => void | CleanupFn | SyncConfigRes
342
345
 
343
346
  /**
@@ -356,6 +359,25 @@ export interface SyncConfig<
356
359
  rowUpdateMode?: `partial` | `full`
357
360
  }
358
361
 
362
+ export interface SyncMetadataApi<
363
+ TKey extends string | number = string | number,
364
+ > {
365
+ row: {
366
+ get: (key: TKey) => unknown | undefined
367
+ set: (key: TKey, metadata: unknown) => void
368
+ delete: (key: TKey) => void
369
+ }
370
+ collection: {
371
+ get: (key: string) => unknown | undefined
372
+ set: (key: string, value: unknown) => void
373
+ delete: (key: string) => void
374
+ list: (prefix?: string) => ReadonlyArray<{
375
+ key: string
376
+ value: unknown
377
+ }>
378
+ }
379
+ }
380
+
359
381
  export interface ChangeMessage<
360
382
  T extends object = Record<string, unknown>,
361
383
  TKey extends string | number = string | number,
@@ -540,12 +562,27 @@ export interface BaseCollectionConfig<
540
562
  /**
541
563
  * Auto-indexing mode for the collection.
542
564
  * When enabled, indexes will be automatically created for simple where expressions.
543
- * @default "eager"
565
+ * @default "off"
544
566
  * @description
545
- * - "off": No automatic indexing
546
- * - "eager": Automatically create indexes for simple where expressions in subscribeChanges (default)
567
+ * - "off": No automatic indexing (default). Use explicit indexes for better bundle size.
568
+ * - "eager": Automatically create indexes for simple where expressions in subscribeChanges.
569
+ * Requires setting defaultIndexType.
547
570
  */
548
571
  autoIndex?: `off` | `eager`
572
+ /**
573
+ * Default index type to use when creating indexes without an explicit type.
574
+ * Required for auto-indexing. Import from '@tanstack/db'.
575
+ * @example
576
+ * ```ts
577
+ * import { BasicIndex } from '@tanstack/db'
578
+ * const collection = createCollection({
579
+ * defaultIndexType: BasicIndex,
580
+ * autoIndex: 'eager',
581
+ * // ...
582
+ * })
583
+ * ```
584
+ */
585
+ defaultIndexType?: IndexConstructor<TKey>
549
586
  /**
550
587
  * Optional function to compare two items.
551
588
  * This is used to order the items in the collection.
@@ -739,9 +776,10 @@ export type CollectionConfigSingleRowOption<
739
776
  TUtils extends UtilsRecord = {},
740
777
  > = CollectionConfig<T, TKey, TSchema, TUtils> & MaybeSingleResult
741
778
 
742
- export type ChangesPayload<T extends object = Record<string, unknown>> = Array<
743
- ChangeMessage<T>
744
- >
779
+ export type ChangesPayload<
780
+ T extends object = Record<string, unknown>,
781
+ TKey extends string | number = string | number,
782
+ > = Array<ChangeMessage<WithVirtualProps<T, TKey>, TKey>>
745
783
 
746
784
  /**
747
785
  * An input row from a collection
@@ -783,6 +821,7 @@ export type NamespacedAndKeyedStream = IStreamBuilder<KeyedNamespacedRow>
783
821
  */
784
822
  export interface SubscribeChangesOptions<
785
823
  T extends object = Record<string, unknown>,
824
+ TKey extends string | number = string | number,
786
825
  > {
787
826
  /** Whether to include the current state as initial changes */
788
827
  includeInitialState?: boolean
@@ -800,7 +839,7 @@ export interface SubscribeChangesOptions<
800
839
  * })
801
840
  * ```
802
841
  */
803
- where?: (row: SingleRowRefProxy<T>) => any
842
+ where?: (row: SingleRowRefProxy<WithVirtualProps<T, TKey>>) => any
804
843
  /** Pre-compiled expression for filtering changes */
805
844
  whereExpression?: BasicExpression<boolean>
806
845
  /**
@@ -829,7 +868,8 @@ export interface SubscribeChangesOptions<
829
868
 
830
869
  export interface SubscribeChangesSnapshotOptions<
831
870
  T extends object = Record<string, unknown>,
832
- > extends Omit<SubscribeChangesOptions<T>, `includeInitialState`> {
871
+ TKey extends string | number = string | number,
872
+ > extends Omit<SubscribeChangesOptions<T, TKey>, `includeInitialState`> {
833
873
  orderBy?: OrderBy
834
874
  limit?: number
835
875
  }
@@ -879,7 +919,7 @@ export interface CurrentStateAsChangesOptions {
879
919
  export type ChangeListener<
880
920
  T extends object = Record<string, unknown>,
881
921
  TKey extends string | number = string | number,
882
- > = (changes: Array<ChangeMessage<T, TKey>>) => void
922
+ > = (changes: Array<ChangeMessage<WithVirtualProps<T, TKey>, TKey>>) => void
883
923
 
884
924
  // Adapted from https://github.com/sindresorhus/type-fest
885
925
  // MIT License Copyright (c) Sindre Sorhus
@@ -5,6 +5,35 @@
5
5
  * @param compareFn Comparison function to use for ordering
6
6
  * @returns The index where the value should be inserted to maintain order
7
7
  */
8
+ export function findInsertPositionInArray<T>(
9
+ sortedArray: Array<T>,
10
+ value: T,
11
+ compareFn: (a: T, b: T) => number,
12
+ ): number {
13
+ let left = 0
14
+ let right = sortedArray.length
15
+
16
+ while (left < right) {
17
+ const mid = Math.floor((left + right) / 2)
18
+ const comparison = compareFn(sortedArray[mid]!, value)
19
+
20
+ if (comparison < 0) {
21
+ left = mid + 1
22
+ } else {
23
+ right = mid
24
+ }
25
+ }
26
+
27
+ return left
28
+ }
29
+
30
+ /**
31
+ * Finds the correct insert position for a value in a sorted tuple array using binary search
32
+ * @param sortedArray The sorted tuple array to search in
33
+ * @param value The value to find the position for
34
+ * @param compareFn Comparison function to use for ordering
35
+ * @returns The index where the value should be inserted to maintain order
36
+ */
8
37
  export function findInsertPosition<T>(
9
38
  sortedArray: Array<[T, any]>,
10
39
  value: T,
@@ -26,3 +55,23 @@ export function findInsertPosition<T>(
26
55
 
27
56
  return left
28
57
  }
58
+
59
+ /**
60
+ * Deletes a value from a sorted array while maintaining sort order
61
+ * @param sortedArray The sorted array to delete from
62
+ * @param value The value to delete
63
+ * @param compareFn Comparison function to use for ordering
64
+ * @returns True if the value was found and deleted, false otherwise
65
+ */
66
+ export function deleteInSortedArray<T>(
67
+ sortedArray: Array<T>,
68
+ value: T,
69
+ compareFn: (a: T, b: T) => number,
70
+ ): boolean {
71
+ const idx = findInsertPositionInArray(sortedArray, value, compareFn)
72
+ if (idx < sortedArray.length && compareFn(sortedArray[idx]!, value) === 0) {
73
+ sortedArray.splice(idx, 1)
74
+ return true
75
+ }
76
+ return false
77
+ }
@@ -1,3 +1,4 @@
1
+ import { isTemporal } from '../utils'
1
2
  import type { CompareOptions } from '../query/builder/types'
2
3
 
3
4
  // WeakMap to store stable IDs for objects
@@ -54,6 +55,15 @@ export const ascComparator = (a: any, b: any, opts: CompareOptions): number => {
54
55
  return a.getTime() - b.getTime()
55
56
  }
56
57
 
58
+ // If both are Temporal objects of the same type, compare by string representation
59
+ if (isTemporal(a) && isTemporal(b)) {
60
+ const aStr = a.toString()
61
+ const bStr = b.toString()
62
+ if (aStr < bStr) return -1
63
+ if (aStr > bStr) return 1
64
+ return 0
65
+ }
66
+
57
67
  // If at least one of the values is an object, use stable IDs for comparison
58
68
  const aIsObject = typeof a === `object`
59
69
  const bIsObject = typeof b === `object`
@@ -154,6 +164,10 @@ export function normalizeValue(value: any): any {
154
164
  return value.getTime()
155
165
  }
156
166
 
167
+ if (isTemporal(value)) {
168
+ return `__temporal__${value[Symbol.toStringTag]}__${value.toString()}`
169
+ }
170
+
157
171
  // Normalize Uint8Arrays/Buffers to a string representation for Map key usage
158
172
  // This enables content-based equality for binary data like ULIDs
159
173
  const isUint8Array =
@@ -17,6 +17,7 @@
17
17
 
18
18
  import { DEFAULT_COMPARE_OPTIONS } from '../utils.js'
19
19
  import { ReverseIndex } from '../indexes/reverse-index.js'
20
+ import { hasVirtualPropPath } from '../virtual-props.js'
20
21
  import type { CompareOptions } from '../query/builder/types.js'
21
22
  import type { IndexInterface, IndexOperation } from '../indexes/base-index.js'
22
23
  import type { BasicExpression } from '../query/ir.js'
@@ -38,6 +39,9 @@ export function findIndexForField<TKey extends string | number>(
38
39
  fieldPath: Array<string>,
39
40
  compareOptions?: CompareOptions,
40
41
  ): IndexInterface<TKey> | undefined {
42
+ if (hasVirtualPropPath(fieldPath)) {
43
+ return undefined
44
+ }
41
45
  const compareOpts = compareOptions ?? {
42
46
  ...DEFAULT_COMPARE_OPTIONS,
43
47
  ...collection.compareOptions,
package/src/utils.ts CHANGED
@@ -144,8 +144,8 @@ function deepEqualsInternal(
144
144
  // Handle Temporal objects
145
145
  // Check if both are Temporal objects of the same type
146
146
  if (isTemporal(a) && isTemporal(b)) {
147
- const aTag = getStringTag(a)
148
- const bTag = getStringTag(b)
147
+ const aTag = a[Symbol.toStringTag]
148
+ const bTag = b[Symbol.toStringTag]
149
149
 
150
150
  // If they're different Temporal types, they're not equal
151
151
  if (aTag !== bTag) return false
@@ -211,7 +211,7 @@ function deepEqualsInternal(
211
211
  return false
212
212
  }
213
213
 
214
- const temporalTypes = [
214
+ const temporalTypes = new Set([
215
215
  `Temporal.Duration`,
216
216
  `Temporal.Instant`,
217
217
  `Temporal.PlainDate`,
@@ -220,16 +220,19 @@ const temporalTypes = [
220
220
  `Temporal.PlainTime`,
221
221
  `Temporal.PlainYearMonth`,
222
222
  `Temporal.ZonedDateTime`,
223
- ]
223
+ ])
224
224
 
225
- function getStringTag(a: any): any {
226
- return a[Symbol.toStringTag]
225
+ export interface TemporalLike {
226
+ [Symbol.toStringTag]: string
227
+ toString: () => string
228
+ equals?: (other: unknown) => boolean
227
229
  }
228
230
 
229
231
  /** Checks if the value is a Temporal object by checking for the Temporal brand */
230
- export function isTemporal(a: any): boolean {
231
- const tag = getStringTag(a)
232
- return typeof tag === `string` && temporalTypes.includes(tag)
232
+ export function isTemporal(a: unknown): a is TemporalLike {
233
+ if (a == null || typeof a !== `object`) return false
234
+ const tag = (a as Record<symbol, unknown>)[Symbol.toStringTag]
235
+ return typeof tag === `string` && temporalTypes.has(tag)
233
236
  }
234
237
 
235
238
  export const DEFAULT_COMPARE_OPTIONS: CompareOptions = {