@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
@@ -1,4 +1,4 @@
1
- import type { CollectionImpl } from '../../collection/index.js'
1
+ import type { Collection, CollectionImpl } from '../../collection/index.js'
2
2
  import type { SingleResult, StringCollationConfig } from '../../types.js'
3
3
  import type {
4
4
  Aggregate,
@@ -8,7 +8,9 @@ import type {
8
8
  PropRef,
9
9
  Value,
10
10
  } from '../ir.js'
11
- import type { QueryBuilder } from './index.js'
11
+ import type { InitialQueryBuilder, QueryBuilder } from './index.js'
12
+ import type { VirtualRowProps, WithVirtualProps } from '../../virtual-props.js'
13
+ import type { ConcatToArrayWrapper, ToArrayWrapper } from './functions.js'
12
14
 
13
15
  /**
14
16
  * Context - The central state container for query builder operations
@@ -48,6 +50,8 @@ export interface Context {
48
50
  >
49
51
  // The result type after select (if select has been called)
50
52
  result?: any
53
+ // Whether select/fn.select has been called
54
+ hasResult?: true
51
55
  // Single result only (if findOne has been called)
52
56
  singleResult?: boolean
53
57
  }
@@ -83,7 +87,9 @@ export type Source = {
83
87
  * This can be an explicit type passed by the user or the schema output type.
84
88
  */
85
89
  export type InferCollectionType<T> =
86
- T extends CollectionImpl<infer TOutput, any, any, any, any> ? TOutput : never
90
+ T extends CollectionImpl<infer TOutput, infer TKey, any, any, any>
91
+ ? WithVirtualProps<TOutput, TKey>
92
+ : never
87
93
 
88
94
  /**
89
95
  * SchemaFromSource - Converts a Source definition into a ContextSchema
@@ -174,9 +180,23 @@ type SelectValue =
174
180
  | undefined // Optional values
175
181
  | { [key: string]: SelectValue }
176
182
  | Array<RefLeaf<any>>
183
+ | ToArrayWrapper // toArray() wrapped subquery
184
+ | ConcatToArrayWrapper // concat(toArray(...)) wrapped subquery
185
+ | QueryBuilder<any> // includes subquery (produces a child Collection)
177
186
 
178
187
  // Recursive shape for select objects allowing nested projections
179
188
  type SelectShape = { [key: string]: SelectValue | SelectShape }
189
+ export type ScalarSelectValue =
190
+ | BasicExpression
191
+ | Aggregate
192
+ | Ref
193
+ | RefLeaf<any>
194
+ | string
195
+ | number
196
+ | boolean
197
+ | null
198
+ | undefined
199
+ export type StringifiableScalar = string | number | boolean | null | undefined
180
200
 
181
201
  /**
182
202
  * SelectObject - Wrapper type for select clause objects
@@ -186,6 +206,69 @@ type SelectShape = { [key: string]: SelectValue | SelectShape }
186
206
  * messages when invalid selections are attempted.
187
207
  */
188
208
  export type SelectObject<T extends SelectShape = SelectShape> = T
209
+ type RefBrandKeys = typeof RefBrand | typeof NullableBrand
210
+ type HasNamedSelectKeys<T> =
211
+ Exclude<keyof T, RefBrandKeys> extends never ? false : true
212
+ type IsScalarSelectLike<T> = T extends BasicExpression | Aggregate
213
+ ? true
214
+ : T extends string | number | boolean | null | undefined
215
+ ? true
216
+ : typeof RefBrand extends keyof T
217
+ ? HasNamedSelectKeys<T> extends true
218
+ ? false
219
+ : true
220
+ : false
221
+ export type NonScalarSelectObject<T> = T extends SelectObject
222
+ ? IsScalarSelectLike<T> extends true
223
+ ? never
224
+ : T
225
+ : never
226
+
227
+ export type ResultTypeFromSelectValue<TSelectValue> =
228
+ IsAny<TSelectValue> extends true
229
+ ? any
230
+ : WithoutRefBrand<
231
+ NeedsExtraction<TSelectValue> extends true
232
+ ? ExtractExpressionType<TSelectValue>
233
+ : TSelectValue extends ToArrayWrapper<infer T>
234
+ ? Array<T>
235
+ : TSelectValue extends ConcatToArrayWrapper<any>
236
+ ? string
237
+ : TSelectValue extends QueryBuilder<infer TChildContext>
238
+ ? Collection<GetResult<TChildContext>>
239
+ : TSelectValue extends Ref<infer _T>
240
+ ? ExtractRef<TSelectValue>
241
+ : TSelectValue extends RefLeaf<infer T>
242
+ ? IsNullableRef<TSelectValue> extends true
243
+ ? T | undefined
244
+ : T
245
+ : TSelectValue extends RefLeaf<infer T> | undefined
246
+ ? T | undefined
247
+ : TSelectValue extends RefLeaf<infer T> | null
248
+ ? IsNullableRef<
249
+ Exclude<TSelectValue, null>
250
+ > extends true
251
+ ? T | null | undefined
252
+ : T | null
253
+ : TSelectValue extends Ref<infer _T> | undefined
254
+ ?
255
+ | ExtractRef<Exclude<TSelectValue, undefined>>
256
+ | undefined
257
+ : TSelectValue extends Ref<infer _T> | null
258
+ ? ExtractRef<Exclude<TSelectValue, null>> | null
259
+ : TSelectValue extends Aggregate<infer T>
260
+ ? T
261
+ : TSelectValue extends
262
+ | string
263
+ | number
264
+ | boolean
265
+ | null
266
+ | undefined
267
+ ? TSelectValue
268
+ : TSelectValue extends Record<string, any>
269
+ ? ResultTypeFromSelect<TSelectValue>
270
+ : never
271
+ >
189
272
 
190
273
  /**
191
274
  * ResultTypeFromSelect - Infers the result type from a select object
@@ -223,46 +306,71 @@ export type SelectObject<T extends SelectShape = SelectShape> = T
223
306
  * { id: number, name: string, status: 'active', count: 42, profile: { bio: string } }
224
307
  * ```
225
308
  */
226
- export type ResultTypeFromSelect<TSelectObject> = WithoutRefBrand<
227
- Prettify<{
228
- [K in keyof TSelectObject]: NeedsExtraction<TSelectObject[K]> extends true
229
- ? ExtractExpressionType<TSelectObject[K]>
230
- : // Ref (full object ref or spread with RefBrand) - recursively process properties
231
- TSelectObject[K] extends Ref<infer _T>
232
- ? ExtractRef<TSelectObject[K]>
233
- : // RefLeaf (simple property ref like user.name)
234
- TSelectObject[K] extends RefLeaf<infer T>
235
- ? IsNullableRef<TSelectObject[K]> extends true
236
- ? T | undefined
237
- : T
238
- : // RefLeaf | undefined (schema-optional field)
239
- TSelectObject[K] extends RefLeaf<infer T> | undefined
240
- ? T | undefined
241
- : // RefLeaf | null (schema-nullable field)
242
- TSelectObject[K] extends RefLeaf<infer T> | null
243
- ? IsNullableRef<Exclude<TSelectObject[K], null>> extends true
244
- ? T | null | undefined
245
- : T | null
246
- : // Ref | undefined (optional object-type schema field)
247
- TSelectObject[K] extends Ref<infer _T> | undefined
248
- ? ExtractRef<Exclude<TSelectObject[K], undefined>> | undefined
249
- : // Ref | null (nullable object-type schema field)
250
- TSelectObject[K] extends Ref<infer _T> | null
251
- ? ExtractRef<Exclude<TSelectObject[K], null>> | null
252
- : TSelectObject[K] extends Aggregate<infer T>
253
- ? T
254
- : TSelectObject[K] extends
255
- | string
256
- | number
257
- | boolean
258
- | null
259
- | undefined
260
- ? TSelectObject[K]
261
- : TSelectObject[K] extends Record<string, any>
262
- ? ResultTypeFromSelect<TSelectObject[K]>
263
- : never
264
- }>
265
- >
309
+ export type ResultTypeFromSelect<TSelectObject> =
310
+ IsAny<TSelectObject> extends true
311
+ ? any
312
+ : WithoutRefBrand<
313
+ Prettify<{
314
+ [K in keyof TSelectObject]: NeedsExtraction<
315
+ TSelectObject[K]
316
+ > extends true
317
+ ? ExtractExpressionType<TSelectObject[K]>
318
+ : TSelectObject[K] extends ToArrayWrapper<infer T>
319
+ ? Array<T>
320
+ : TSelectObject[K] extends ConcatToArrayWrapper<any>
321
+ ? string
322
+ : // includes subquery (bare QueryBuilder) — produces a child Collection
323
+ TSelectObject[K] extends QueryBuilder<infer TChildContext>
324
+ ? Collection<GetResult<TChildContext>>
325
+ : // Ref (full object ref or spread with RefBrand) - recursively process properties
326
+ TSelectObject[K] extends Ref<infer _T>
327
+ ? ExtractRef<TSelectObject[K]>
328
+ : // RefLeaf (simple property ref like user.name)
329
+ TSelectObject[K] extends RefLeaf<infer T>
330
+ ? IsNullableRef<TSelectObject[K]> extends true
331
+ ? T | undefined
332
+ : T
333
+ : // RefLeaf | undefined (schema-optional field)
334
+ TSelectObject[K] extends RefLeaf<infer T> | undefined
335
+ ? T | undefined
336
+ : // RefLeaf | null (schema-nullable field)
337
+ TSelectObject[K] extends RefLeaf<infer T> | null
338
+ ? IsNullableRef<
339
+ Exclude<TSelectObject[K], null>
340
+ > extends true
341
+ ? T | null | undefined
342
+ : T | null
343
+ : // Ref | undefined (optional object-type schema field)
344
+ TSelectObject[K] extends Ref<infer _T> | undefined
345
+ ?
346
+ | ExtractRef<
347
+ Exclude<TSelectObject[K], undefined>
348
+ >
349
+ | undefined
350
+ : // Ref | null (nullable object-type schema field)
351
+ TSelectObject[K] extends Ref<infer _T> | null
352
+ ? ExtractRef<
353
+ Exclude<TSelectObject[K], null>
354
+ > | null
355
+ : TSelectObject[K] extends Aggregate<infer T>
356
+ ? T
357
+ : TSelectObject[K] extends
358
+ | string
359
+ | number
360
+ | boolean
361
+ | null
362
+ | undefined
363
+ ? TSelectObject[K]
364
+ : TSelectObject[K] extends Record<string, any>
365
+ ? ResultTypeFromSelect<TSelectObject[K]>
366
+ : never
367
+ }>
368
+ >
369
+
370
+ export type SelectResult<TSelect> =
371
+ IsPlainObject<TSelect> extends true
372
+ ? ResultTypeFromSelect<TSelect>
373
+ : ResultTypeFromSelectValue<TSelect>
266
374
 
267
375
  // Extract Ref or subobject with a spread or a Ref
268
376
  type ExtractRef<T> = Prettify<ResultTypeFromSelect<WithoutRefBrand<T>>>
@@ -373,7 +481,7 @@ export type JoinOnCallback<TContext extends Context> = (
373
481
  * Example (no GROUP BY): `(row) => row.user.salary > 70000 && row.$selected.user_count > 2`
374
482
  */
375
483
  export type FunctionalHavingRow<TContext extends Context> = TContext[`schema`] &
376
- (TContext[`result`] extends object ? { $selected: TContext[`result`] } : {})
484
+ (TContext[`hasResult`] extends true ? { $selected: TContext[`result`] } : {})
377
485
 
378
486
  /**
379
487
  * RefsForContext - Creates ref proxies for all tables/collections in a query context
@@ -409,7 +517,7 @@ export type RefsForContext<TContext extends Context> = {
409
517
  : // T is exactly undefined, exactly null, or neither optional nor nullable
410
518
  // Wrap in Ref as-is (includes exact undefined, exact null, and normal types)
411
519
  Ref<TContext[`schema`][K]>
412
- } & (TContext[`result`] extends object
520
+ } & (TContext[`hasResult`] extends true
413
521
  ? { $selected: Ref<TContext[`result`]> }
414
522
  : {})
415
523
 
@@ -474,6 +582,29 @@ type NonUndefined<T> = T extends undefined ? never : T
474
582
  // Helper type to extract non-null type
475
583
  type NonNull<T> = T extends null ? never : T
476
584
 
585
+ /**
586
+ * Virtual properties available on all Ref types in query builders.
587
+ * These allow querying on sync status, origin, key, and collection ID.
588
+ *
589
+ * @example
590
+ * ```typescript
591
+ * // Filter by sync status
592
+ * .where(({ user }) => eq(user.$synced, true))
593
+ *
594
+ * // Filter by origin
595
+ * .where(({ order }) => eq(order.$origin, 'local'))
596
+ *
597
+ * // Access key in select
598
+ * .select(({ user }) => ({
599
+ * key: user.$key,
600
+ * collectionId: user.$collectionId,
601
+ * }))
602
+ * ```
603
+ */
604
+ type VirtualPropsRef<TKey extends string | number = string | number> = {
605
+ readonly [K in keyof VirtualRowProps<TKey>]: RefLeaf<VirtualRowProps<TKey>[K]>
606
+ }
607
+
477
608
  /**
478
609
  * Ref - The user-facing ref interface for the query builder
479
610
  *
@@ -487,12 +618,18 @@ type NonNull<T> = T extends null ? never : T
487
618
  * through all nested property accesses, ensuring the result type includes
488
619
  * `| undefined` for all fields accessed through this ref.
489
620
  *
621
+ * Includes virtual properties ($synced, $origin, $key, $collectionId) for
622
+ * querying on sync status and row metadata.
623
+ *
490
624
  * Example usage:
491
625
  * ```typescript
492
- * // Non-nullable ref (inner join or from table):
493
- * select(({ user }) => ({ name: user.name })) // result: string
626
+ * // Clean interface - no internal properties visible
627
+ * const users: Ref<{ id: number; profile?: { bio: string } }> = { ... }
628
+ * users.id // Ref<number> - clean display
629
+ * users.profile?.bio // Ref<string> - nested optional access works
630
+ * users.$synced // RefLeaf<boolean> - virtual property access
494
631
  *
495
- * // Nullable ref (left join right side):
632
+ * // Nullable ref (left/right/full join side):
496
633
  * select(({ dept }) => ({ name: dept.name })) // result: string | undefined
497
634
  *
498
635
  * // Spread operations work cleanly:
@@ -519,7 +656,8 @@ export type Ref<T = any, Nullable extends boolean = false> = {
519
656
  IsPlainObject<T[K]> extends true
520
657
  ? Ref<T[K], Nullable>
521
658
  : RefLeaf<T[K], Nullable>
522
- } & RefLeaf<T, Nullable>
659
+ } & RefLeaf<T, Nullable> &
660
+ VirtualPropsRef
523
661
 
524
662
  /**
525
663
  * Ref - The user-facing ref type with clean IDE display
@@ -543,7 +681,7 @@ type IsNullableRef<T> = typeof NullableBrand extends keyof T ? true : false
543
681
 
544
682
  // Helper type to remove RefBrand and NullableBrand from objects
545
683
  type WithoutRefBrand<T> =
546
- T extends Record<string, any>
684
+ IsPlainObject<T> extends true
547
685
  ? Omit<T, typeof RefBrand | typeof NullableBrand>
548
686
  : T
549
687
 
@@ -561,6 +699,10 @@ type PreserveSingleResultFlag<TFlag> = [TFlag] extends [true]
561
699
  ? { singleResult: true }
562
700
  : {}
563
701
 
702
+ type PreserveHasResultFlag<TFlag> = [TFlag] extends [true]
703
+ ? { hasResult: true }
704
+ : {}
705
+
564
706
  /**
565
707
  * MergeContextWithJoinType - Creates a new context after a join operation
566
708
  *
@@ -606,7 +748,8 @@ export type MergeContextWithJoinType<
606
748
  [K in keyof TNewSchema & string]: TJoinType
607
749
  }
608
750
  result: TContext[`result`]
609
- } & PreserveSingleResultFlag<TContext[`singleResult`]>
751
+ } & PreserveSingleResultFlag<TContext[`singleResult`]> &
752
+ PreserveHasResultFlag<TContext[`hasResult`]>
610
753
 
611
754
  /**
612
755
  * ApplyJoinOptionalityToMergedSchema - Applies optionality rules when merging schemas
@@ -664,6 +807,17 @@ export type InferResultType<TContext extends Context> =
664
807
  ? GetResult<TContext> | undefined
665
808
  : Array<GetResult<TContext>>
666
809
 
810
+ type WithVirtualPropsIfObject<TResult> = TResult extends object
811
+ ? WithVirtualProps<TResult, string | number>
812
+ : TResult
813
+
814
+ type PrettifyIfPlainObject<T> = IsPlainObject<T> extends true ? Prettify<T> : T
815
+ type ResultValue<TContext extends Context> = TContext[`hasResult`] extends true
816
+ ? WithVirtualPropsIfObject<TContext[`result`]>
817
+ : TContext[`hasJoins`] extends true
818
+ ? TContext[`schema`]
819
+ : TContext[`schema`][TContext[`fromSourceName`]]
820
+
667
821
  /**
668
822
  * GetResult - Determines the final result type of a query
669
823
  *
@@ -689,16 +843,46 @@ export type InferResultType<TContext extends Context> =
689
843
  * The `Prettify` wrapper ensures clean type display in IDEs by flattening
690
844
  * complex intersection types into readable object types.
691
845
  */
846
+ export type GetRawResult<TContext extends Context> = ResultValue<TContext>
847
+
692
848
  export type GetResult<TContext extends Context> = Prettify<
693
- TContext[`result`] extends object
694
- ? TContext[`result`]
695
- : TContext[`hasJoins`] extends true
696
- ? // Optionality is already applied in the schema, just return it
697
- TContext[`schema`]
698
- : // Single table query - return the specific table
699
- TContext[`schema`][TContext[`fromSourceName`]]
849
+ ResultValue<TContext>
700
850
  >
701
851
 
852
+ type IsExactlyContext<TContext extends Context> = [Context] extends [TContext]
853
+ ? [TContext] extends [Context]
854
+ ? true
855
+ : false
856
+ : false
857
+
858
+ type RootScalarResultError = {
859
+ readonly __tanstackDbRootQueryError__: `Top-level scalar results are not supported by createLiveQueryCollection() or queryOnce(). Return an object, or use the scalar query inside toArray(...) or concat(toArray(...)).`
860
+ }
861
+
862
+ export type RootObjectResultConstraint<TContext extends Context> =
863
+ IsExactlyContext<TContext> extends true
864
+ ? unknown
865
+ : GetResult<TContext> extends object
866
+ ? unknown
867
+ : RootScalarResultError
868
+
869
+ type ContextFromQueryBuilder<TQuery extends QueryBuilder<any>> =
870
+ TQuery extends QueryBuilder<infer TContext> ? TContext : never
871
+
872
+ export type RootQueryBuilder<TQuery extends QueryBuilder<any>> = TQuery &
873
+ RootObjectResultConstraint<ContextFromQueryBuilder<TQuery>>
874
+
875
+ export type RootQueryFn<TQuery extends QueryBuilder<any>> = (
876
+ q: InitialQueryBuilder,
877
+ ) => RootQueryBuilder<TQuery>
878
+
879
+ export type RootQueryResult<TContext extends Context> =
880
+ IsExactlyContext<TContext> extends true
881
+ ? any
882
+ : GetResult<TContext> extends object
883
+ ? GetResult<TContext>
884
+ : never
885
+
702
886
  /**
703
887
  * ApplyJoinOptionalityToSchema - Legacy helper for complex join scenarios
704
888
  *
@@ -831,7 +1015,7 @@ export type MergeContextForJoinCallback<
831
1015
  ? TContext[`joinTypes`]
832
1016
  : {}
833
1017
  result: TContext[`result`]
834
- }
1018
+ } & PreserveHasResultFlag<TContext[`hasResult`]>
835
1019
 
836
1020
  /**
837
1021
  * WithResult - Updates a context with a new result type after select()
@@ -848,8 +1032,9 @@ export type MergeContextForJoinCallback<
848
1032
  * result type display cleanly in IDEs.
849
1033
  */
850
1034
  export type WithResult<TContext extends Context, TResult> = Prettify<
851
- Omit<TContext, `result`> & {
852
- result: Prettify<TResult>
1035
+ Omit<TContext, `result` | `hasResult`> & {
1036
+ result: PrettifyIfPlainObject<TResult>
1037
+ hasResult: true
853
1038
  }
854
1039
  >
855
1040
 
@@ -862,6 +1047,12 @@ export type Prettify<T> = {
862
1047
 
863
1048
  /**
864
1049
  * IsPlainObject - Utility type to check if T is a plain object
1050
+ *
1051
+ * Returns `false` for:
1052
+ * - Arrays (ReadonlyArray)
1053
+ * - JavaScript built-ins (Date, Map, Set, etc.)
1054
+ * - Objects with `Symbol.toStringTag` (class instances like Temporal types,
1055
+ * TypedArrays not already in JsBuiltIns, etc.) — these are not plain data objects
865
1056
  */
866
1057
  type IsPlainObject<T> = T extends unknown
867
1058
  ? T extends object
@@ -869,10 +1060,14 @@ type IsPlainObject<T> = T extends unknown
869
1060
  ? false
870
1061
  : T extends JsBuiltIns
871
1062
  ? false
872
- : true
1063
+ : T extends { readonly [Symbol.toStringTag]: string }
1064
+ ? false
1065
+ : true
873
1066
  : false
874
1067
  : false
875
1068
 
1069
+ type IsAny<T> = 0 extends 1 & T ? true : false
1070
+
876
1071
  /**
877
1072
  * JsBuiltIns - List of JavaScript built-ins
878
1073
  */
@@ -14,6 +14,31 @@ function isUnknown(value: any): boolean {
14
14
  return value === null || value === undefined
15
15
  }
16
16
 
17
+ function toDateValue(value: any): Date | null {
18
+ if (value instanceof Date) {
19
+ return Number.isNaN(value.getTime()) ? null : value
20
+ }
21
+
22
+ if (typeof value === `string` || typeof value === `number`) {
23
+ const parsed = new Date(value)
24
+ return Number.isNaN(parsed.getTime()) ? null : parsed
25
+ }
26
+
27
+ return null
28
+ }
29
+
30
+ function evaluateStrftime(format: string, date: Date): string {
31
+ if (format === `%Y-%m-%d`) {
32
+ return date.toISOString().slice(0, 10)
33
+ }
34
+
35
+ if (format === `%Y-%m-%dT%H:%M:%fZ`) {
36
+ return date.toISOString()
37
+ }
38
+
39
+ return date.toISOString()
40
+ }
41
+
17
42
  /**
18
43
  * Converts a 3-valued logic result to a boolean for use in WHERE/HAVING filters.
19
44
  * In SQL, UNKNOWN (null) values in WHERE clauses exclude rows, matching false behavior.
@@ -471,6 +496,38 @@ function compileFunction(func: Func, isSingleRow: boolean): (data: any) => any {
471
496
  return divisor !== 0 ? (a ?? 0) / divisor : null
472
497
  }
473
498
  }
499
+ case `date`: {
500
+ const arg = compiledArgs[0]!
501
+ return (data) => {
502
+ const value = arg(data)
503
+ const dateValue = toDateValue(value)
504
+ return dateValue ? dateValue.toISOString().slice(0, 10) : null
505
+ }
506
+ }
507
+ case `datetime`: {
508
+ const arg = compiledArgs[0]!
509
+ return (data) => {
510
+ const value = arg(data)
511
+ const dateValue = toDateValue(value)
512
+ return dateValue ? dateValue.toISOString() : null
513
+ }
514
+ }
515
+ case `strftime`: {
516
+ const formatArg = compiledArgs[0]!
517
+ const sourceArg = compiledArgs[1]!
518
+ return (data) => {
519
+ const format = formatArg(data)
520
+ if (typeof format !== `string`) {
521
+ return null
522
+ }
523
+ const sourceValue = sourceArg(data)
524
+ const dateValue = toDateValue(sourceValue)
525
+ if (!dateValue) {
526
+ return null
527
+ }
528
+ return evaluateStrftime(format, dateValue)
529
+ }
530
+ }
474
531
 
475
532
  // Null/undefined checking functions
476
533
  case `isUndefined`: {