@tanstack/db 0.5.33 → 0.6.1

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 (282) 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 +22 -4
  40. package/dist/cjs/index.cjs.map +1 -1
  41. package/dist/cjs/index.d.cts +11 -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/index.d.cts +2 -1
  81. package/dist/cjs/query/ir.cjs +18 -1
  82. package/dist/cjs/query/ir.cjs.map +1 -1
  83. package/dist/cjs/query/ir.d.cts +21 -1
  84. package/dist/cjs/query/live/collection-config-builder.cjs +501 -5
  85. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  86. package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
  87. package/dist/cjs/query/live/types.d.cts +3 -3
  88. package/dist/cjs/query/live/utils.cjs +43 -3
  89. package/dist/cjs/query/live/utils.cjs.map +1 -1
  90. package/dist/cjs/query/live/utils.d.cts +1 -0
  91. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  92. package/dist/cjs/query/live-query-collection.d.cts +9 -6
  93. package/dist/cjs/query/query-once.cjs.map +1 -1
  94. package/dist/cjs/query/query-once.d.cts +7 -5
  95. package/dist/cjs/query/subset-dedupe.cjs +9 -3
  96. package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
  97. package/dist/cjs/types.d.cts +42 -8
  98. package/dist/cjs/utils/array-utils.cjs +27 -0
  99. package/dist/cjs/utils/array-utils.cjs.map +1 -0
  100. package/dist/cjs/utils/array-utils.d.cts +16 -0
  101. package/dist/cjs/utils/comparison.cjs +11 -0
  102. package/dist/cjs/utils/comparison.cjs.map +1 -1
  103. package/dist/cjs/utils/index-optimization.cjs +4 -0
  104. package/dist/cjs/utils/index-optimization.cjs.map +1 -1
  105. package/dist/cjs/utils.cjs +7 -9
  106. package/dist/cjs/utils.cjs.map +1 -1
  107. package/dist/cjs/utils.d.cts +6 -1
  108. package/dist/cjs/virtual-props.cjs +33 -0
  109. package/dist/cjs/virtual-props.cjs.map +1 -0
  110. package/dist/cjs/virtual-props.d.cts +196 -0
  111. package/dist/esm/collection/change-events.d.ts +3 -2
  112. package/dist/esm/collection/change-events.js.map +1 -1
  113. package/dist/esm/collection/changes.d.ts +10 -1
  114. package/dist/esm/collection/changes.js +13 -4
  115. package/dist/esm/collection/changes.js.map +1 -1
  116. package/dist/esm/collection/cleanup-queue.d.ts +30 -0
  117. package/dist/esm/collection/cleanup-queue.js +89 -0
  118. package/dist/esm/collection/cleanup-queue.js.map +1 -0
  119. package/dist/esm/collection/events.d.ts +39 -1
  120. package/dist/esm/collection/events.js +14 -0
  121. package/dist/esm/collection/events.js.map +1 -1
  122. package/dist/esm/collection/index.d.ts +49 -36
  123. package/dist/esm/collection/index.js +67 -29
  124. package/dist/esm/collection/index.js.map +1 -1
  125. package/dist/esm/collection/indexes.d.ts +27 -17
  126. package/dist/esm/collection/indexes.js +211 -62
  127. package/dist/esm/collection/indexes.js.map +1 -1
  128. package/dist/esm/collection/lifecycle.d.ts +0 -1
  129. package/dist/esm/collection/lifecycle.js +5 -22
  130. package/dist/esm/collection/lifecycle.js.map +1 -1
  131. package/dist/esm/collection/mutations.d.ts +1 -0
  132. package/dist/esm/collection/mutations.js +18 -0
  133. package/dist/esm/collection/mutations.js.map +1 -1
  134. package/dist/esm/collection/state.d.ts +65 -1
  135. package/dist/esm/collection/state.js +381 -53
  136. package/dist/esm/collection/state.js.map +1 -1
  137. package/dist/esm/collection/subscription.d.ts +4 -0
  138. package/dist/esm/collection/subscription.js +6 -0
  139. package/dist/esm/collection/subscription.js.map +1 -1
  140. package/dist/esm/collection/sync.d.ts +2 -0
  141. package/dist/esm/collection/sync.js +108 -1
  142. package/dist/esm/collection/sync.js.map +1 -1
  143. package/dist/esm/collection/transaction-metadata.d.ts +1 -0
  144. package/dist/esm/collection/transaction-metadata.js +5 -0
  145. package/dist/esm/collection/transaction-metadata.js.map +1 -0
  146. package/dist/esm/errors.d.ts +3 -0
  147. package/dist/esm/errors.js +8 -0
  148. package/dist/esm/errors.js.map +1 -1
  149. package/dist/esm/index.d.ts +11 -3
  150. package/dist/esm/index.js +25 -7
  151. package/dist/esm/index.js.map +1 -1
  152. package/dist/esm/indexes/auto-index.js +13 -6
  153. package/dist/esm/indexes/auto-index.js.map +1 -1
  154. package/dist/esm/indexes/base-index.d.ts +2 -6
  155. package/dist/esm/indexes/base-index.js +1 -4
  156. package/dist/esm/indexes/base-index.js.map +1 -1
  157. package/dist/esm/indexes/basic-index.d.ts +102 -0
  158. package/dist/esm/indexes/basic-index.js +361 -0
  159. package/dist/esm/indexes/basic-index.js.map +1 -0
  160. package/dist/esm/indexes/btree-index.d.ts +1 -1
  161. package/dist/esm/indexes/btree-index.js.map +1 -1
  162. package/dist/esm/indexes/index-options.d.ts +8 -9
  163. package/dist/esm/indexes/index-registry.d.ts +61 -0
  164. package/dist/esm/indexes/index-registry.js +89 -0
  165. package/dist/esm/indexes/index-registry.js.map +1 -0
  166. package/dist/esm/local-only.js +5 -0
  167. package/dist/esm/local-only.js.map +1 -1
  168. package/dist/esm/query/builder/functions.d.ts +25 -3
  169. package/dist/esm/query/builder/functions.js +27 -11
  170. package/dist/esm/query/builder/functions.js.map +1 -1
  171. package/dist/esm/query/builder/index.d.ts +4 -3
  172. package/dist/esm/query/builder/index.js +201 -40
  173. package/dist/esm/query/builder/index.js.map +1 -1
  174. package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
  175. package/dist/esm/query/builder/ref-proxy.js.map +1 -1
  176. package/dist/esm/query/builder/types.d.ts +84 -19
  177. package/dist/esm/query/compiler/evaluators.js +51 -0
  178. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  179. package/dist/esm/query/compiler/group-by.d.ts +4 -2
  180. package/dist/esm/query/compiler/group-by.js +101 -29
  181. package/dist/esm/query/compiler/group-by.js.map +1 -1
  182. package/dist/esm/query/compiler/index.d.ts +30 -2
  183. package/dist/esm/query/compiler/index.js +285 -13
  184. package/dist/esm/query/compiler/index.js.map +1 -1
  185. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  186. package/dist/esm/query/compiler/order-by.js +30 -11
  187. package/dist/esm/query/compiler/order-by.js.map +1 -1
  188. package/dist/esm/query/compiler/select.js +8 -0
  189. package/dist/esm/query/compiler/select.js.map +1 -1
  190. package/dist/esm/query/index.d.ts +2 -1
  191. package/dist/esm/query/ir.d.ts +21 -1
  192. package/dist/esm/query/ir.js +18 -1
  193. package/dist/esm/query/ir.js.map +1 -1
  194. package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
  195. package/dist/esm/query/live/collection-config-builder.js +503 -7
  196. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  197. package/dist/esm/query/live/types.d.ts +3 -3
  198. package/dist/esm/query/live/utils.d.ts +1 -0
  199. package/dist/esm/query/live/utils.js +43 -3
  200. package/dist/esm/query/live/utils.js.map +1 -1
  201. package/dist/esm/query/live-query-collection.d.ts +9 -6
  202. package/dist/esm/query/live-query-collection.js.map +1 -1
  203. package/dist/esm/query/query-once.d.ts +7 -5
  204. package/dist/esm/query/query-once.js.map +1 -1
  205. package/dist/esm/query/subset-dedupe.js +9 -3
  206. package/dist/esm/query/subset-dedupe.js.map +1 -1
  207. package/dist/esm/types.d.ts +42 -8
  208. package/dist/esm/utils/array-utils.d.ts +16 -0
  209. package/dist/esm/utils/array-utils.js +27 -0
  210. package/dist/esm/utils/array-utils.js.map +1 -0
  211. package/dist/esm/utils/comparison.js +11 -0
  212. package/dist/esm/utils/comparison.js.map +1 -1
  213. package/dist/esm/utils/index-optimization.js +4 -0
  214. package/dist/esm/utils/index-optimization.js.map +1 -1
  215. package/dist/esm/utils.d.ts +6 -1
  216. package/dist/esm/utils.js +7 -9
  217. package/dist/esm/utils.js.map +1 -1
  218. package/dist/esm/virtual-props.d.ts +196 -0
  219. package/dist/esm/virtual-props.js +33 -0
  220. package/dist/esm/virtual-props.js.map +1 -0
  221. package/package.json +4 -3
  222. package/skills/db-core/SKILL.md +4 -2
  223. package/skills/db-core/collection-setup/SKILL.md +30 -11
  224. package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
  225. package/skills/db-core/collection-setup/references/powersync-adapter.md +4 -0
  226. package/skills/db-core/collection-setup/references/query-adapter.md +32 -0
  227. package/skills/db-core/custom-adapter/SKILL.md +58 -9
  228. package/skills/db-core/live-queries/SKILL.md +162 -2
  229. package/skills/db-core/mutations-optimistic/SKILL.md +1 -1
  230. package/skills/db-core/persistence/SKILL.md +241 -0
  231. package/skills/meta-framework/SKILL.md +1 -1
  232. package/src/collection/change-events.ts +13 -9
  233. package/src/collection/changes.ts +30 -7
  234. package/src/collection/cleanup-queue.ts +105 -0
  235. package/src/collection/events.ts +65 -0
  236. package/src/collection/index.ts +110 -45
  237. package/src/collection/indexes.ts +283 -76
  238. package/src/collection/lifecycle.ts +5 -26
  239. package/src/collection/mutations.ts +21 -0
  240. package/src/collection/state.ts +545 -71
  241. package/src/collection/subscription.ts +7 -0
  242. package/src/collection/sync.ts +137 -0
  243. package/src/collection/transaction-metadata.ts +1 -0
  244. package/src/errors.ts +9 -0
  245. package/src/index.ts +46 -3
  246. package/src/indexes/auto-index.ts +18 -8
  247. package/src/indexes/base-index.ts +2 -10
  248. package/src/indexes/basic-index.ts +507 -0
  249. package/src/indexes/btree-index.ts +1 -1
  250. package/src/indexes/index-options.ts +17 -37
  251. package/src/indexes/index-registry.ts +174 -0
  252. package/src/local-only.ts +7 -0
  253. package/src/query/builder/functions.ts +84 -7
  254. package/src/query/builder/index.ts +329 -9
  255. package/src/query/builder/ref-proxy.ts +22 -4
  256. package/src/query/builder/types.ts +257 -62
  257. package/src/query/compiler/evaluators.ts +57 -0
  258. package/src/query/compiler/group-by.ts +156 -35
  259. package/src/query/compiler/index.ts +445 -15
  260. package/src/query/compiler/order-by.ts +51 -12
  261. package/src/query/compiler/select.ts +9 -0
  262. package/src/query/index.ts +7 -0
  263. package/src/query/ir.ts +23 -2
  264. package/src/query/live/collection-config-builder.ts +809 -9
  265. package/src/query/live/types.ts +10 -4
  266. package/src/query/live/utils.ts +64 -3
  267. package/src/query/live-query-collection.ts +43 -18
  268. package/src/query/query-once.ts +31 -12
  269. package/src/query/subset-dedupe.ts +11 -7
  270. package/src/types.ts +49 -9
  271. package/src/utils/array-utils.ts +49 -0
  272. package/src/utils/comparison.ts +14 -0
  273. package/src/utils/index-optimization.ts +4 -0
  274. package/src/utils.ts +12 -9
  275. package/src/virtual-props.ts +282 -0
  276. package/dist/cjs/indexes/lazy-index.cjs +0 -190
  277. package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
  278. package/dist/cjs/indexes/lazy-index.d.cts +0 -96
  279. package/dist/esm/indexes/lazy-index.d.ts +0 -96
  280. package/dist/esm/indexes/lazy-index.js +0 -190
  281. package/dist/esm/indexes/lazy-index.js.map +0 -1
  282. package/src/indexes/lazy-index.ts +0 -251
@@ -256,6 +256,13 @@ export class CollectionSubscription
256
256
  this.orderByIndex = index
257
257
  }
258
258
 
259
+ /**
260
+ * Check if an orderBy index has been set for this subscription
261
+ */
262
+ hasOrderByIndex(): boolean {
263
+ return this.orderByIndex !== undefined
264
+ }
265
+
259
266
  /**
260
267
  * Set subscription status and emit events if changed
261
268
  */
@@ -18,6 +18,7 @@ import type {
18
18
  LoadSubsetOptions,
19
19
  OptimisticChangeMessage,
20
20
  SyncConfigRes,
21
+ SyncMetadataApi,
21
22
  } from '../types'
22
23
  import type { CollectionImpl } from './index.js'
23
24
  import type { CollectionStateManager } from './state'
@@ -93,6 +94,8 @@ export class CollectionSyncManager<
93
94
  committed: false,
94
95
  operations: [],
95
96
  deletedKeys: new Set(),
97
+ rowMetadataWrites: new Map(),
98
+ collectionMetadataWrites: new Map(),
96
99
  immediate: options?.immediate,
97
100
  })
98
101
  },
@@ -120,6 +123,10 @@ export class CollectionSyncManager<
120
123
  key = this.config.getKey(messageWithOptionalKey.value)
121
124
  }
122
125
 
126
+ if (this.state.pendingLocalChanges.has(key)) {
127
+ this.state.pendingLocalOrigins.add(key)
128
+ }
129
+
123
130
  let messageType = messageWithOptionalKey.type
124
131
 
125
132
  // Check if an item with this key already exists when inserting
@@ -165,6 +172,23 @@ export class CollectionSyncManager<
165
172
 
166
173
  if (messageType === `delete`) {
167
174
  pendingTransaction.deletedKeys.add(key)
175
+ pendingTransaction.rowMetadataWrites.set(key, { type: `delete` })
176
+ } else if (messageType === `insert`) {
177
+ if (message.metadata !== undefined) {
178
+ pendingTransaction.rowMetadataWrites.set(key, {
179
+ type: `set`,
180
+ value: message.metadata,
181
+ })
182
+ } else {
183
+ pendingTransaction.rowMetadataWrites.set(key, {
184
+ type: `delete`,
185
+ })
186
+ }
187
+ } else if (message.metadata !== undefined) {
188
+ pendingTransaction.rowMetadataWrites.set(key, {
189
+ type: `set`,
190
+ value: message.metadata,
191
+ })
168
192
  }
169
193
  },
170
194
  commit: () => {
@@ -201,6 +225,11 @@ export class CollectionSyncManager<
201
225
  // Clear all operations from the current transaction
202
226
  pendingTransaction.operations = []
203
227
  pendingTransaction.deletedKeys.clear()
228
+ pendingTransaction.rowMetadataWrites.clear()
229
+ // Intentionally preserve collectionMetadataWrites across truncate.
230
+ // Collection-scoped metadata (for example persisted resume/reset
231
+ // state) can be staged before truncate and should commit atomically
232
+ // with the truncate transaction.
204
233
 
205
234
  // Mark the transaction as a truncate operation. During commit, this triggers:
206
235
  // - Delete events for all previously synced keys (excluding optimistic-deleted keys)
@@ -216,6 +245,7 @@ export class CollectionSyncManager<
216
245
  deletes: new Set(this.state.optimisticDeletes),
217
246
  }
218
247
  },
248
+ metadata: this.createSyncMetadataApi(),
219
249
  }),
220
250
  )
221
251
 
@@ -241,6 +271,113 @@ export class CollectionSyncManager<
241
271
  }
242
272
  }
243
273
 
274
+ private getActivePendingSyncTransaction() {
275
+ const pendingTransaction =
276
+ this.state.pendingSyncedTransactions[
277
+ this.state.pendingSyncedTransactions.length - 1
278
+ ]
279
+
280
+ if (!pendingTransaction) {
281
+ throw new NoPendingSyncTransactionWriteError()
282
+ }
283
+ if (pendingTransaction.committed) {
284
+ throw new SyncTransactionAlreadyCommittedWriteError()
285
+ }
286
+
287
+ return pendingTransaction
288
+ }
289
+
290
+ private createSyncMetadataApi(): SyncMetadataApi<TKey> {
291
+ return {
292
+ row: {
293
+ get: (key) => {
294
+ const pendingTransaction =
295
+ this.state.pendingSyncedTransactions[
296
+ this.state.pendingSyncedTransactions.length - 1
297
+ ]
298
+ const pendingWrite = pendingTransaction?.rowMetadataWrites.get(key)
299
+ if (pendingWrite) {
300
+ return pendingWrite.type === `delete`
301
+ ? undefined
302
+ : pendingWrite.value
303
+ }
304
+ if (pendingTransaction?.truncate) {
305
+ return undefined
306
+ }
307
+ return this.state.syncedMetadata.get(key)
308
+ },
309
+ set: (key, metadata) => {
310
+ const pendingTransaction = this.getActivePendingSyncTransaction()
311
+ pendingTransaction.rowMetadataWrites.set(key, {
312
+ type: `set`,
313
+ value: metadata,
314
+ })
315
+ },
316
+ delete: (key) => {
317
+ const pendingTransaction = this.getActivePendingSyncTransaction()
318
+ pendingTransaction.rowMetadataWrites.set(key, {
319
+ type: `delete`,
320
+ })
321
+ },
322
+ },
323
+ collection: {
324
+ get: (key) => {
325
+ const pendingTransaction =
326
+ this.state.pendingSyncedTransactions[
327
+ this.state.pendingSyncedTransactions.length - 1
328
+ ]
329
+ const pendingWrite =
330
+ pendingTransaction?.collectionMetadataWrites.get(key)
331
+ if (pendingWrite) {
332
+ return pendingWrite.type === `delete`
333
+ ? undefined
334
+ : pendingWrite.value
335
+ }
336
+ return this.state.syncedCollectionMetadata.get(key)
337
+ },
338
+ set: (key, value) => {
339
+ const pendingTransaction = this.getActivePendingSyncTransaction()
340
+ pendingTransaction.collectionMetadataWrites.set(key, {
341
+ type: `set`,
342
+ value,
343
+ })
344
+ },
345
+ delete: (key) => {
346
+ const pendingTransaction = this.getActivePendingSyncTransaction()
347
+ pendingTransaction.collectionMetadataWrites.set(key, {
348
+ type: `delete`,
349
+ })
350
+ },
351
+ list: (prefix) => {
352
+ const merged = new Map(this.state.syncedCollectionMetadata)
353
+ const pendingTransaction =
354
+ this.state.pendingSyncedTransactions[
355
+ this.state.pendingSyncedTransactions.length - 1
356
+ ]
357
+ if (pendingTransaction) {
358
+ for (const [
359
+ key,
360
+ pendingWrite,
361
+ ] of pendingTransaction.collectionMetadataWrites) {
362
+ if (pendingWrite.type === `delete`) {
363
+ merged.delete(key)
364
+ } else {
365
+ merged.set(key, pendingWrite.value)
366
+ }
367
+ }
368
+ }
369
+
370
+ return Array.from(merged.entries())
371
+ .filter(([key]) => (prefix ? key.startsWith(prefix) : true))
372
+ .map(([key, value]) => ({
373
+ key,
374
+ value,
375
+ }))
376
+ },
377
+ },
378
+ }
379
+ }
380
+
244
381
  /**
245
382
  * Preload the collection data by starting sync if not already started
246
383
  * Multiple concurrent calls will share the same promise
@@ -0,0 +1 @@
1
+ export const DIRECT_TRANSACTION_METADATA_KEY = `__tanstack_db_direct`
package/src/errors.ts CHANGED
@@ -444,6 +444,15 @@ export class FnSelectWithGroupByError extends QueryCompilationError {
444
444
  }
445
445
  }
446
446
 
447
+ export class UnsupportedRootScalarSelectError extends QueryCompilationError {
448
+ constructor() {
449
+ super(
450
+ `Top-level scalar select() is not supported by createLiveQueryCollection() or queryOnce(). ` +
451
+ `Return an object from .select(), or use the scalar query inside toArray(...) or concat(toArray(...)).`,
452
+ )
453
+ }
454
+ }
455
+
447
456
  export class HavingRequiresGroupByError extends QueryCompilationError {
448
457
  constructor() {
449
458
  super(`HAVING clause requires GROUP BY clause`)
package/src/index.ts CHANGED
@@ -17,12 +17,55 @@ export { deepEquals } from './utils'
17
17
  export * from './paced-mutations'
18
18
  export * from './strategies/index.js'
19
19
 
20
+ // Virtual properties exports
21
+ export {
22
+ type VirtualRowProps,
23
+ type VirtualOrigin,
24
+ type WithVirtualProps,
25
+ type WithoutVirtualProps,
26
+ hasVirtualProps,
27
+ } from './virtual-props.js'
28
+
20
29
  // Index system exports
21
- export * from './indexes/base-index.js'
22
- export * from './indexes/btree-index.js'
23
- export * from './indexes/lazy-index.js'
30
+ export { BaseIndex } from './indexes/base-index.js'
31
+ export type {
32
+ IndexInterface,
33
+ IndexConstructor,
34
+ IndexStats,
35
+ IndexOperation,
36
+ } from './indexes/base-index.js'
24
37
  export { type IndexOptions } from './indexes/index-options.js'
25
38
 
39
+ // Index implementations
40
+ export { BasicIndex } from './indexes/basic-index.js'
41
+ export type {
42
+ BasicIndexOptions,
43
+ RangeQueryOptions,
44
+ } from './indexes/basic-index.js'
45
+ export { BTreeIndex } from './indexes/btree-index.js'
46
+ export type { RangeQueryOptions as BTreeRangeQueryOptions } from './indexes/btree-index.js'
47
+ export { ReverseIndex } from './indexes/reverse-index.js'
48
+
49
+ // Index optimization utilities
50
+ export {
51
+ optimizeExpressionWithIndexes,
52
+ findIndexForField,
53
+ } from './utils/index-optimization.js'
54
+
55
+ // Dev mode utilities
56
+ export {
57
+ configureIndexDevMode,
58
+ isDevModeEnabled,
59
+ getIndexDevModeConfig,
60
+ trackQuery,
61
+ clearQueryPatterns,
62
+ getQueryPatterns,
63
+ } from './indexes/index-registry.js'
64
+ export type {
65
+ IndexDevModeConfig,
66
+ IndexSuggestion,
67
+ } from './indexes/index-registry.js'
68
+
26
69
  // Expression helpers
27
70
  export * from './query/expression-helpers.js'
28
71
 
@@ -1,5 +1,6 @@
1
1
  import { DEFAULT_COMPARE_OPTIONS } from '../utils'
2
- import { BTreeIndex } from './btree-index'
2
+ import { hasVirtualPropPath } from '../virtual-props'
3
+ import { checkCollectionSizeForIndex, isDevModeEnabled } from './index-registry'
3
4
  import type { CompareOptions } from '../query/builder/types'
4
5
  import type { BasicExpression } from '../query/ir'
5
6
  import type { CollectionImpl } from '../collection/index.js'
@@ -10,11 +11,9 @@ export interface AutoIndexConfig {
10
11
 
11
12
  function shouldAutoIndex(collection: CollectionImpl<any, any, any, any, any>) {
12
13
  // Only proceed if auto-indexing is enabled
13
- if (collection.config.autoIndex !== `eager`) {
14
- return false
15
- }
16
-
17
- return true
14
+ // Note: autoIndex: 'eager' without defaultIndexType is caught at construction time
15
+ // in CollectionImpl, so we don't need to check for it here.
16
+ return collection.config.autoIndex === `eager`
18
17
  }
19
18
 
20
19
  export function ensureIndexForField<
@@ -27,6 +26,9 @@ export function ensureIndexForField<
27
26
  compareOptions?: CompareOptions,
28
27
  compareFn?: (a: any, b: any) => number,
29
28
  ) {
29
+ if (hasVirtualPropPath(fieldPath)) {
30
+ return
31
+ }
30
32
  if (!shouldAutoIndex(collection)) {
31
33
  return
32
34
  }
@@ -46,9 +48,18 @@ export function ensureIndexForField<
46
48
  return // Index already exists
47
49
  }
48
50
 
51
+ // Dev mode: check if collection size warrants an index suggestion
52
+ if (isDevModeEnabled()) {
53
+ checkCollectionSizeForIndex(
54
+ collection.id || `unknown`,
55
+ collection.size,
56
+ fieldPath,
57
+ )
58
+ }
59
+
49
60
  // Create a new index for this field using the collection's createIndex method
61
+ // The collection will use its defaultIndexType
50
62
  try {
51
- // Use the proxy-based approach to create the proper accessor for nested paths
52
63
  collection.createIndex(
53
64
  (row) => {
54
65
  // Navigate through the field path
@@ -60,7 +71,6 @@ export function ensureIndexForField<
60
71
  },
61
72
  {
62
73
  name: `auto:${fieldPath.join(`.`)}`,
63
- indexType: BTreeIndex,
64
74
  options: compareFn ? { compareFn, compareOptions: compareOpts } : {},
65
75
  },
66
76
  )
@@ -26,7 +26,7 @@ export interface IndexStats {
26
26
  }
27
27
 
28
28
  export interface IndexInterface<
29
- TKey extends string | number | undefined = string | number | undefined,
29
+ TKey extends string | number = string | number,
30
30
  > {
31
31
  add: (key: TKey, item: any) => void
32
32
  remove: (key: TKey, item: any) => void
@@ -79,7 +79,7 @@ export interface IndexInterface<
79
79
  * Base abstract class that all index types extend
80
80
  */
81
81
  export abstract class BaseIndex<
82
- TKey extends string | number | undefined = string | number | undefined,
82
+ TKey extends string | number = string | number,
83
83
  > implements IndexInterface<TKey> {
84
84
  public readonly id: number
85
85
  public readonly name?: string
@@ -189,7 +189,6 @@ export abstract class BaseIndex<
189
189
  }
190
190
  }
191
191
 
192
- // Protected methods for subclasses
193
192
  protected abstract initialize(options?: any): void
194
193
 
195
194
  protected evaluateIndexExpression(item: any): any {
@@ -218,10 +217,3 @@ export type IndexConstructor<TKey extends string | number = string | number> =
218
217
  name?: string,
219
218
  options?: any,
220
219
  ) => BaseIndex<TKey>
221
-
222
- /**
223
- * Index resolver can be either a class constructor or async loader
224
- */
225
- export type IndexResolver<TKey extends string | number = string | number> =
226
- | IndexConstructor<TKey>
227
- | (() => Promise<IndexConstructor<TKey>>)