@tanstack/db 0.5.33 → 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 (273) 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 +2 -2
  222. package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
  223. package/src/collection/change-events.ts +13 -9
  224. package/src/collection/changes.ts +30 -7
  225. package/src/collection/cleanup-queue.ts +105 -0
  226. package/src/collection/events.ts +65 -0
  227. package/src/collection/index.ts +110 -45
  228. package/src/collection/indexes.ts +283 -76
  229. package/src/collection/lifecycle.ts +5 -26
  230. package/src/collection/mutations.ts +21 -0
  231. package/src/collection/state.ts +545 -71
  232. package/src/collection/subscription.ts +7 -0
  233. package/src/collection/sync.ts +137 -0
  234. package/src/collection/transaction-metadata.ts +1 -0
  235. package/src/errors.ts +9 -0
  236. package/src/index.ts +46 -3
  237. package/src/indexes/auto-index.ts +18 -8
  238. package/src/indexes/base-index.ts +2 -10
  239. package/src/indexes/basic-index.ts +507 -0
  240. package/src/indexes/btree-index.ts +1 -1
  241. package/src/indexes/index-options.ts +17 -37
  242. package/src/indexes/index-registry.ts +174 -0
  243. package/src/local-only.ts +7 -0
  244. package/src/query/builder/functions.ts +84 -7
  245. package/src/query/builder/index.ts +329 -9
  246. package/src/query/builder/ref-proxy.ts +22 -4
  247. package/src/query/builder/types.ts +257 -62
  248. package/src/query/compiler/evaluators.ts +57 -0
  249. package/src/query/compiler/group-by.ts +156 -35
  250. package/src/query/compiler/index.ts +445 -15
  251. package/src/query/compiler/order-by.ts +51 -12
  252. package/src/query/compiler/select.ts +9 -0
  253. package/src/query/index.ts +7 -0
  254. package/src/query/ir.ts +23 -2
  255. package/src/query/live/collection-config-builder.ts +809 -9
  256. package/src/query/live/types.ts +10 -4
  257. package/src/query/live/utils.ts +64 -3
  258. package/src/query/live-query-collection.ts +43 -18
  259. package/src/query/query-once.ts +31 -12
  260. package/src/query/subset-dedupe.ts +11 -7
  261. package/src/types.ts +49 -9
  262. package/src/utils/array-utils.ts +49 -0
  263. package/src/utils/comparison.ts +14 -0
  264. package/src/utils/index-optimization.ts +4 -0
  265. package/src/utils.ts +12 -9
  266. package/src/virtual-props.ts +282 -0
  267. package/dist/cjs/indexes/lazy-index.cjs +0 -190
  268. package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
  269. package/dist/cjs/indexes/lazy-index.d.cts +0 -96
  270. package/dist/esm/indexes/lazy-index.d.ts +0 -96
  271. package/dist/esm/indexes/lazy-index.js +0 -190
  272. package/dist/esm/indexes/lazy-index.js.map +0 -1
  273. package/src/indexes/lazy-index.ts +0 -251
@@ -1,7 +1,9 @@
1
- import { CollectionImpl } from '../../collection/index.js';
1
+ import { Collection, CollectionImpl } from '../../collection/index.js';
2
2
  import { SingleResult, StringCollationConfig } from '../../types.js';
3
3
  import { Aggregate, BasicExpression, Func, OrderByDirection, PropRef, Value } from '../ir.js';
4
- import { QueryBuilder } from './index.js';
4
+ import { InitialQueryBuilder, QueryBuilder } from './index.js';
5
+ import { VirtualRowProps, WithVirtualProps } from '../../virtual-props.js';
6
+ import { ConcatToArrayWrapper, ToArrayWrapper } from './functions.js';
5
7
  /**
6
8
  * Context - The central state container for query builder operations
7
9
  *
@@ -31,6 +33,7 @@ export interface Context {
31
33
  hasJoins?: boolean;
32
34
  joinTypes?: Record<string, `inner` | `left` | `right` | `full` | `outer` | `cross`>;
33
35
  result?: any;
36
+ hasResult?: true;
34
37
  singleResult?: boolean;
35
38
  }
36
39
  /**
@@ -61,7 +64,7 @@ export type Source = {
61
64
  * This helper ensures we get the same type that was used when creating the collection itself.
62
65
  * This can be an explicit type passed by the user or the schema output type.
63
66
  */
64
- export type InferCollectionType<T> = T extends CollectionImpl<infer TOutput, any, any, any, any> ? TOutput : never;
67
+ export type InferCollectionType<T> = T extends CollectionImpl<infer TOutput, infer TKey, any, any, any> ? WithVirtualProps<TOutput, TKey> : never;
65
68
  /**
66
69
  * SchemaFromSource - Converts a Source definition into a ContextSchema
67
70
  *
@@ -132,10 +135,12 @@ export type WhereCallback<TContext extends Context> = (refs: RefsForContext<TCon
132
135
  */
133
136
  type SelectValue = BasicExpression | Aggregate | Ref | RefLeaf<any> | string | number | boolean | null | undefined | {
134
137
  [key: string]: SelectValue;
135
- } | Array<RefLeaf<any>>;
138
+ } | Array<RefLeaf<any>> | ToArrayWrapper | ConcatToArrayWrapper | QueryBuilder<any>;
136
139
  type SelectShape = {
137
140
  [key: string]: SelectValue | SelectShape;
138
141
  };
142
+ export type ScalarSelectValue = BasicExpression | Aggregate | Ref | RefLeaf<any> | string | number | boolean | null | undefined;
143
+ export type StringifiableScalar = string | number | boolean | null | undefined;
139
144
  /**
140
145
  * SelectObject - Wrapper type for select clause objects
141
146
  *
@@ -144,6 +149,11 @@ type SelectShape = {
144
149
  * messages when invalid selections are attempted.
145
150
  */
146
151
  export type SelectObject<T extends SelectShape = SelectShape> = T;
152
+ type RefBrandKeys = typeof RefBrand | typeof NullableBrand;
153
+ type HasNamedSelectKeys<T> = Exclude<keyof T, RefBrandKeys> extends never ? false : true;
154
+ type IsScalarSelectLike<T> = T extends BasicExpression | Aggregate ? true : T extends string | number | boolean | null | undefined ? true : typeof RefBrand extends keyof T ? HasNamedSelectKeys<T> extends true ? false : true : false;
155
+ export type NonScalarSelectObject<T> = T extends SelectObject ? IsScalarSelectLike<T> extends true ? never : T : never;
156
+ export type ResultTypeFromSelectValue<TSelectValue> = IsAny<TSelectValue> extends true ? any : WithoutRefBrand<NeedsExtraction<TSelectValue> extends true ? ExtractExpressionType<TSelectValue> : TSelectValue extends ToArrayWrapper<infer T> ? Array<T> : TSelectValue extends ConcatToArrayWrapper<any> ? string : TSelectValue extends QueryBuilder<infer TChildContext> ? Collection<GetResult<TChildContext>> : TSelectValue extends Ref<infer _T> ? ExtractRef<TSelectValue> : TSelectValue extends RefLeaf<infer T> ? IsNullableRef<TSelectValue> extends true ? T | undefined : T : TSelectValue extends RefLeaf<infer T> | undefined ? T | undefined : TSelectValue extends RefLeaf<infer T> | null ? IsNullableRef<Exclude<TSelectValue, null>> extends true ? T | null | undefined : T | null : TSelectValue extends Ref<infer _T> | undefined ? ExtractRef<Exclude<TSelectValue, undefined>> | undefined : TSelectValue extends Ref<infer _T> | null ? ExtractRef<Exclude<TSelectValue, null>> | null : TSelectValue extends Aggregate<infer T> ? T : TSelectValue extends string | number | boolean | null | undefined ? TSelectValue : TSelectValue extends Record<string, any> ? ResultTypeFromSelect<TSelectValue> : never>;
147
157
  /**
148
158
  * ResultTypeFromSelect - Infers the result type from a select object
149
159
  *
@@ -180,9 +190,10 @@ export type SelectObject<T extends SelectShape = SelectShape> = T;
180
190
  * { id: number, name: string, status: 'active', count: 42, profile: { bio: string } }
181
191
  * ```
182
192
  */
183
- export type ResultTypeFromSelect<TSelectObject> = WithoutRefBrand<Prettify<{
184
- [K in keyof TSelectObject]: NeedsExtraction<TSelectObject[K]> extends true ? ExtractExpressionType<TSelectObject[K]> : TSelectObject[K] extends Ref<infer _T> ? ExtractRef<TSelectObject[K]> : TSelectObject[K] extends RefLeaf<infer T> ? IsNullableRef<TSelectObject[K]> extends true ? T | undefined : T : TSelectObject[K] extends RefLeaf<infer T> | undefined ? T | undefined : TSelectObject[K] extends RefLeaf<infer T> | null ? IsNullableRef<Exclude<TSelectObject[K], null>> extends true ? T | null | undefined : T | null : TSelectObject[K] extends Ref<infer _T> | undefined ? ExtractRef<Exclude<TSelectObject[K], undefined>> | undefined : TSelectObject[K] extends Ref<infer _T> | null ? ExtractRef<Exclude<TSelectObject[K], null>> | null : TSelectObject[K] extends Aggregate<infer T> ? T : TSelectObject[K] extends string | number | boolean | null | undefined ? TSelectObject[K] : TSelectObject[K] extends Record<string, any> ? ResultTypeFromSelect<TSelectObject[K]> : never;
193
+ export type ResultTypeFromSelect<TSelectObject> = IsAny<TSelectObject> extends true ? any : WithoutRefBrand<Prettify<{
194
+ [K in keyof TSelectObject]: NeedsExtraction<TSelectObject[K]> extends true ? ExtractExpressionType<TSelectObject[K]> : TSelectObject[K] extends ToArrayWrapper<infer T> ? Array<T> : TSelectObject[K] extends ConcatToArrayWrapper<any> ? string : TSelectObject[K] extends QueryBuilder<infer TChildContext> ? Collection<GetResult<TChildContext>> : TSelectObject[K] extends Ref<infer _T> ? ExtractRef<TSelectObject[K]> : TSelectObject[K] extends RefLeaf<infer T> ? IsNullableRef<TSelectObject[K]> extends true ? T | undefined : T : TSelectObject[K] extends RefLeaf<infer T> | undefined ? T | undefined : TSelectObject[K] extends RefLeaf<infer T> | null ? IsNullableRef<Exclude<TSelectObject[K], null>> extends true ? T | null | undefined : T | null : TSelectObject[K] extends Ref<infer _T> | undefined ? ExtractRef<Exclude<TSelectObject[K], undefined>> | undefined : TSelectObject[K] extends Ref<infer _T> | null ? ExtractRef<Exclude<TSelectObject[K], null>> | null : TSelectObject[K] extends Aggregate<infer T> ? T : TSelectObject[K] extends string | number | boolean | null | undefined ? TSelectObject[K] : TSelectObject[K] extends Record<string, any> ? ResultTypeFromSelect<TSelectObject[K]> : never;
185
195
  }>>;
196
+ export type SelectResult<TSelect> = IsPlainObject<TSelect> extends true ? ResultTypeFromSelect<TSelect> : ResultTypeFromSelectValue<TSelect>;
186
197
  type ExtractRef<T> = Prettify<ResultTypeFromSelect<WithoutRefBrand<T>>>;
187
198
  type ExtractExpressionType<T> = T extends PropRef<infer U> ? U : T extends Value<infer U> ? U : T extends Func<infer U> ? U : T extends Aggregate<infer U> ? U : T extends BasicExpression<infer U> ? U : T;
188
199
  type NeedsExtraction<T> = T extends PropRef<any> | Value<any> | Func<any> | Aggregate<any> | BasicExpression<any> ? true : false;
@@ -256,7 +267,7 @@ export type JoinOnCallback<TContext extends Context> = (refs: RefsForContext<TCo
256
267
  * Example: `({ $selected }) => $selected.sessionCount > 2`
257
268
  * Example (no GROUP BY): `(row) => row.user.salary > 70000 && row.$selected.user_count > 2`
258
269
  */
259
- export type FunctionalHavingRow<TContext extends Context> = TContext[`schema`] & (TContext[`result`] extends object ? {
270
+ export type FunctionalHavingRow<TContext extends Context> = TContext[`schema`] & (TContext[`hasResult`] extends true ? {
260
271
  $selected: TContext[`result`];
261
272
  } : {});
262
273
  /**
@@ -277,7 +288,7 @@ export type FunctionalHavingRow<TContext extends Context> = TContext[`schema`] &
277
288
  */
278
289
  export type RefsForContext<TContext extends Context> = {
279
290
  [K in keyof TContext[`schema`]]: IsNonExactOptional<TContext[`schema`][K]> extends true ? IsNonExactNullable<TContext[`schema`][K]> extends true ? Ref<NonNullable<TContext[`schema`][K]>, true> : Ref<NonUndefined<TContext[`schema`][K]>, true> : IsNonExactNullable<TContext[`schema`][K]> extends true ? Ref<NonNull<TContext[`schema`][K]>, true> : Ref<TContext[`schema`][K]>;
280
- } & (TContext[`result`] extends object ? {
291
+ } & (TContext[`hasResult`] extends true ? {
281
292
  $selected: Ref<TContext[`result`]>;
282
293
  } : {});
283
294
  /**
@@ -313,6 +324,28 @@ type IsNonExactNullable<T> = IsNullable<T> extends true ? IsExactlyNull<T> exten
313
324
  */
314
325
  type NonUndefined<T> = T extends undefined ? never : T;
315
326
  type NonNull<T> = T extends null ? never : T;
327
+ /**
328
+ * Virtual properties available on all Ref types in query builders.
329
+ * These allow querying on sync status, origin, key, and collection ID.
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * // Filter by sync status
334
+ * .where(({ user }) => eq(user.$synced, true))
335
+ *
336
+ * // Filter by origin
337
+ * .where(({ order }) => eq(order.$origin, 'local'))
338
+ *
339
+ * // Access key in select
340
+ * .select(({ user }) => ({
341
+ * key: user.$key,
342
+ * collectionId: user.$collectionId,
343
+ * }))
344
+ * ```
345
+ */
346
+ type VirtualPropsRef<TKey extends string | number = string | number> = {
347
+ readonly [K in keyof VirtualRowProps<TKey>]: RefLeaf<VirtualRowProps<TKey>[K]>;
348
+ };
316
349
  /**
317
350
  * Ref - The user-facing ref interface for the query builder
318
351
  *
@@ -326,12 +359,18 @@ type NonNull<T> = T extends null ? never : T;
326
359
  * through all nested property accesses, ensuring the result type includes
327
360
  * `| undefined` for all fields accessed through this ref.
328
361
  *
362
+ * Includes virtual properties ($synced, $origin, $key, $collectionId) for
363
+ * querying on sync status and row metadata.
364
+ *
329
365
  * Example usage:
330
366
  * ```typescript
331
- * // Non-nullable ref (inner join or from table):
332
- * select(({ user }) => ({ name: user.name })) // result: string
367
+ * // Clean interface - no internal properties visible
368
+ * const users: Ref<{ id: number; profile?: { bio: string } }> = { ... }
369
+ * users.id // Ref<number> - clean display
370
+ * users.profile?.bio // Ref<string> - nested optional access works
371
+ * users.$synced // RefLeaf<boolean> - virtual property access
333
372
  *
334
- * // Nullable ref (left join right side):
373
+ * // Nullable ref (left/right/full join side):
335
374
  * select(({ dept }) => ({ name: dept.name })) // result: string | undefined
336
375
  *
337
376
  * // Spread operations work cleanly:
@@ -340,7 +379,7 @@ type NonNull<T> = T extends null ? never : T;
340
379
  */
341
380
  export type Ref<T = any, Nullable extends boolean = false> = {
342
381
  [K in keyof T]: IsNonExactOptional<T[K]> extends true ? IsNonExactNullable<T[K]> extends true ? IsPlainObject<NonNullable<T[K]>> extends true ? Ref<NonNullable<T[K]>, Nullable> | undefined : RefLeaf<NonNullable<T[K]>, Nullable> | undefined : IsPlainObject<NonUndefined<T[K]>> extends true ? Ref<NonUndefined<T[K]>, Nullable> | undefined : RefLeaf<NonUndefined<T[K]>, Nullable> | undefined : IsNonExactNullable<T[K]> extends true ? IsPlainObject<NonNull<T[K]>> extends true ? Ref<NonNull<T[K]>, Nullable> | null : RefLeaf<NonNull<T[K]>, Nullable> | null : IsPlainObject<T[K]> extends true ? Ref<T[K], Nullable> : RefLeaf<T[K], Nullable>;
343
- } & RefLeaf<T, Nullable>;
382
+ } & RefLeaf<T, Nullable> & VirtualPropsRef;
344
383
  /**
345
384
  * Ref - The user-facing ref type with clean IDE display
346
385
  *
@@ -360,7 +399,7 @@ export type RefLeaf<T = any, Nullable extends boolean = false> = {
360
399
  readonly [NullableBrand]?: true;
361
400
  } : {});
362
401
  type IsNullableRef<T> = typeof NullableBrand extends keyof T ? true : false;
363
- type WithoutRefBrand<T> = T extends Record<string, any> ? Omit<T, typeof RefBrand | typeof NullableBrand> : T;
402
+ type WithoutRefBrand<T> = IsPlainObject<T> extends true ? Omit<T, typeof RefBrand | typeof NullableBrand> : T;
364
403
  /**
365
404
  * PreserveSingleResultFlag - Conditionally includes the singleResult flag
366
405
  *
@@ -374,6 +413,9 @@ type WithoutRefBrand<T> = T extends Record<string, any> ? Omit<T, typeof RefBran
374
413
  type PreserveSingleResultFlag<TFlag> = [TFlag] extends [true] ? {
375
414
  singleResult: true;
376
415
  } : {};
416
+ type PreserveHasResultFlag<TFlag> = [TFlag] extends [true] ? {
417
+ hasResult: true;
418
+ } : {};
377
419
  /**
378
420
  * MergeContextWithJoinType - Creates a new context after a join operation
379
421
  *
@@ -406,7 +448,7 @@ export type MergeContextWithJoinType<TContext extends Context, TNewSchema extend
406
448
  [K in keyof TNewSchema & string]: TJoinType;
407
449
  };
408
450
  result: TContext[`result`];
409
- } & PreserveSingleResultFlag<TContext[`singleResult`]>;
451
+ } & PreserveSingleResultFlag<TContext[`singleResult`]> & PreserveHasResultFlag<TContext[`hasResult`]>;
410
452
  /**
411
453
  * ApplyJoinOptionalityToMergedSchema - Applies optionality rules when merging schemas
412
454
  *
@@ -442,6 +484,9 @@ export type ApplyJoinOptionalityToMergedSchema<TExistingSchema extends ContextSc
442
484
  * Utility type to infer the query result size (single row or an array)
443
485
  */
444
486
  export type InferResultType<TContext extends Context> = TContext extends SingleResult ? GetResult<TContext> | undefined : Array<GetResult<TContext>>;
487
+ type WithVirtualPropsIfObject<TResult> = TResult extends object ? WithVirtualProps<TResult, string | number> : TResult;
488
+ type PrettifyIfPlainObject<T> = IsPlainObject<T> extends true ? Prettify<T> : T;
489
+ type ResultValue<TContext extends Context> = TContext[`hasResult`] extends true ? WithVirtualPropsIfObject<TContext[`result`]> : TContext[`hasJoins`] extends true ? TContext[`schema`] : TContext[`schema`][TContext[`fromSourceName`]];
445
490
  /**
446
491
  * GetResult - Determines the final result type of a query
447
492
  *
@@ -467,7 +512,17 @@ export type InferResultType<TContext extends Context> = TContext extends SingleR
467
512
  * The `Prettify` wrapper ensures clean type display in IDEs by flattening
468
513
  * complex intersection types into readable object types.
469
514
  */
470
- export type GetResult<TContext extends Context> = Prettify<TContext[`result`] extends object ? TContext[`result`] : TContext[`hasJoins`] extends true ? TContext[`schema`] : TContext[`schema`][TContext[`fromSourceName`]]>;
515
+ export type GetRawResult<TContext extends Context> = ResultValue<TContext>;
516
+ export type GetResult<TContext extends Context> = Prettify<ResultValue<TContext>>;
517
+ type IsExactlyContext<TContext extends Context> = [Context] extends [TContext] ? [TContext] extends [Context] ? true : false : false;
518
+ type RootScalarResultError = {
519
+ 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(...)).`;
520
+ };
521
+ export type RootObjectResultConstraint<TContext extends Context> = IsExactlyContext<TContext> extends true ? unknown : GetResult<TContext> extends object ? unknown : RootScalarResultError;
522
+ type ContextFromQueryBuilder<TQuery extends QueryBuilder<any>> = TQuery extends QueryBuilder<infer TContext> ? TContext : never;
523
+ export type RootQueryBuilder<TQuery extends QueryBuilder<any>> = TQuery & RootObjectResultConstraint<ContextFromQueryBuilder<TQuery>>;
524
+ export type RootQueryFn<TQuery extends QueryBuilder<any>> = (q: InitialQueryBuilder) => RootQueryBuilder<TQuery>;
525
+ export type RootQueryResult<TContext extends Context> = IsExactlyContext<TContext> extends true ? any : GetResult<TContext> extends object ? GetResult<TContext> : never;
471
526
  /**
472
527
  * ApplyJoinOptionalityToSchema - Legacy helper for complex join scenarios
473
528
  *
@@ -556,7 +611,7 @@ export type MergeContextForJoinCallback<TContext extends Context, TNewSchema ext
556
611
  hasJoins: true;
557
612
  joinTypes: TContext[`joinTypes`] extends Record<string, any> ? TContext[`joinTypes`] : {};
558
613
  result: TContext[`result`];
559
- };
614
+ } & PreserveHasResultFlag<TContext[`hasResult`]>;
560
615
  /**
561
616
  * WithResult - Updates a context with a new result type after select()
562
617
  *
@@ -571,8 +626,9 @@ export type MergeContextForJoinCallback<TContext extends Context, TNewSchema ext
571
626
  * The double `Prettify` ensures both the overall context and the nested
572
627
  * result type display cleanly in IDEs.
573
628
  */
574
- export type WithResult<TContext extends Context, TResult> = Prettify<Omit<TContext, `result`> & {
575
- result: Prettify<TResult>;
629
+ export type WithResult<TContext extends Context, TResult> = Prettify<Omit<TContext, `result` | `hasResult`> & {
630
+ result: PrettifyIfPlainObject<TResult>;
631
+ hasResult: true;
576
632
  }>;
577
633
  /**
578
634
  * Prettify - Utility type for clean IDE display
@@ -582,8 +638,17 @@ export type Prettify<T> = {
582
638
  } & {};
583
639
  /**
584
640
  * IsPlainObject - Utility type to check if T is a plain object
641
+ *
642
+ * Returns `false` for:
643
+ * - Arrays (ReadonlyArray)
644
+ * - JavaScript built-ins (Date, Map, Set, etc.)
645
+ * - Objects with `Symbol.toStringTag` (class instances like Temporal types,
646
+ * TypedArrays not already in JsBuiltIns, etc.) — these are not plain data objects
585
647
  */
586
- type IsPlainObject<T> = T extends unknown ? T extends object ? T extends ReadonlyArray<any> ? false : T extends JsBuiltIns ? false : true : false : false;
648
+ type IsPlainObject<T> = T extends unknown ? T extends object ? T extends ReadonlyArray<any> ? false : T extends JsBuiltIns ? false : T extends {
649
+ readonly [Symbol.toStringTag]: string;
650
+ } ? false : true : false : false;
651
+ type IsAny<T> = 0 extends 1 & T ? true : false;
587
652
  /**
588
653
  * JsBuiltIns - List of JavaScript built-ins
589
654
  */
@@ -3,6 +3,25 @@ import { normalizeValue, areValuesEqual } from "../../utils/comparison.js";
3
3
  function isUnknown(value) {
4
4
  return value === null || value === void 0;
5
5
  }
6
+ function toDateValue(value) {
7
+ if (value instanceof Date) {
8
+ return Number.isNaN(value.getTime()) ? null : value;
9
+ }
10
+ if (typeof value === `string` || typeof value === `number`) {
11
+ const parsed = new Date(value);
12
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
13
+ }
14
+ return null;
15
+ }
16
+ function evaluateStrftime(format, date) {
17
+ if (format === `%Y-%m-%d`) {
18
+ return date.toISOString().slice(0, 10);
19
+ }
20
+ if (format === `%Y-%m-%dT%H:%M:%fZ`) {
21
+ return date.toISOString();
22
+ }
23
+ return date.toISOString();
24
+ }
6
25
  function toBooleanPredicate(result) {
7
26
  return result === true;
8
27
  }
@@ -343,6 +362,38 @@ function compileFunction(func, isSingleRow) {
343
362
  return divisor !== 0 ? (a ?? 0) / divisor : null;
344
363
  };
345
364
  }
365
+ case `date`: {
366
+ const arg = compiledArgs[0];
367
+ return (data) => {
368
+ const value = arg(data);
369
+ const dateValue = toDateValue(value);
370
+ return dateValue ? dateValue.toISOString().slice(0, 10) : null;
371
+ };
372
+ }
373
+ case `datetime`: {
374
+ const arg = compiledArgs[0];
375
+ return (data) => {
376
+ const value = arg(data);
377
+ const dateValue = toDateValue(value);
378
+ return dateValue ? dateValue.toISOString() : null;
379
+ };
380
+ }
381
+ case `strftime`: {
382
+ const formatArg = compiledArgs[0];
383
+ const sourceArg = compiledArgs[1];
384
+ return (data) => {
385
+ const format = formatArg(data);
386
+ if (typeof format !== `string`) {
387
+ return null;
388
+ }
389
+ const sourceValue = sourceArg(data);
390
+ const dateValue = toDateValue(sourceValue);
391
+ if (!dateValue) {
392
+ return null;
393
+ }
394
+ return evaluateStrftime(format, dateValue);
395
+ };
396
+ }
346
397
  // Null/undefined checking functions
347
398
  case `isUndefined`: {
348
399
  const arg = compiledArgs[0];
@@ -1 +1 @@
1
- {"version":3,"file":"evaluators.js","sources":["../../../../src/query/compiler/evaluators.ts"],"sourcesContent":["import {\n EmptyReferencePathError,\n UnknownExpressionTypeError,\n UnknownFunctionError,\n} from '../../errors.js'\nimport { areValuesEqual, normalizeValue } from '../../utils/comparison.js'\nimport type { BasicExpression, Func, PropRef } from '../ir.js'\nimport type { NamespacedRow } from '../../types.js'\n\n/**\n * Helper function to check if a value is null or undefined (represents UNKNOWN in 3-valued logic)\n */\nfunction isUnknown(value: any): boolean {\n return value === null || value === undefined\n}\n\n/**\n * Converts a 3-valued logic result to a boolean for use in WHERE/HAVING filters.\n * In SQL, UNKNOWN (null) values in WHERE clauses exclude rows, matching false behavior.\n *\n * @param result - The 3-valued logic result: true, false, or null (UNKNOWN)\n * @returns true only if result is explicitly true, false otherwise\n *\n * Truth table:\n * - true → true (include row)\n * - false → false (exclude row)\n * - null (UNKNOWN) → false (exclude row, matching SQL behavior)\n */\nexport function toBooleanPredicate(result: boolean | null): boolean {\n return result === true\n}\n\n/**\n * Compiled expression evaluator function type\n */\nexport type CompiledExpression = (namespacedRow: NamespacedRow) => any\n\n/**\n * Compiled single-row expression evaluator function type\n */\nexport type CompiledSingleRowExpression = (item: Record<string, unknown>) => any\n\n/**\n * Compiles an expression into an optimized evaluator function.\n * This eliminates branching during evaluation by pre-compiling the expression structure.\n */\nexport function compileExpression(\n expr: BasicExpression,\n isSingleRow: boolean = false,\n): CompiledExpression | CompiledSingleRowExpression {\n const compiledFn = compileExpressionInternal(expr, isSingleRow)\n return compiledFn\n}\n\n/**\n * Compiles a single-row expression into an optimized evaluator function.\n */\nexport function compileSingleRowExpression(\n expr: BasicExpression,\n): CompiledSingleRowExpression {\n const compiledFn = compileExpressionInternal(expr, true)\n return compiledFn as CompiledSingleRowExpression\n}\n\n/**\n * Internal unified expression compiler that handles both namespaced and single-row evaluation\n */\nfunction compileExpressionInternal(\n expr: BasicExpression,\n isSingleRow: boolean,\n): (data: any) => any {\n switch (expr.type) {\n case `val`: {\n // For constant values, return a function that just returns the value\n const value = expr.value\n return () => value\n }\n\n case `ref`: {\n // For references, compile based on evaluation mode\n return isSingleRow ? compileSingleRowRef(expr) : compileRef(expr)\n }\n\n case `func`: {\n // For functions, use the unified compiler\n return compileFunction(expr, isSingleRow)\n }\n\n default:\n throw new UnknownExpressionTypeError((expr as any).type)\n }\n}\n\n/**\n * Compiles a reference expression into an optimized evaluator\n */\nfunction compileRef(ref: PropRef): CompiledExpression {\n const [namespace, ...propertyPath] = ref.path\n\n if (!namespace) {\n throw new EmptyReferencePathError()\n }\n\n // Handle $selected namespace - references SELECT result fields\n if (namespace === `$selected`) {\n // Access $selected directly\n if (propertyPath.length === 0) {\n // Just $selected - return entire $selected object\n return (namespacedRow) => (namespacedRow as any).$selected\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const selectResults = (namespacedRow as any).$selected\n return selectResults?.[prop]\n }\n } else {\n // Multiple property navigation (nested SELECT fields)\n return (namespacedRow) => {\n const selectResults = (namespacedRow as any).$selected\n if (selectResults === undefined) {\n return undefined\n }\n\n let value: any = selectResults\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n }\n\n // Handle table alias namespace (existing logic)\n const tableAlias = namespace\n\n // Pre-compile the property path navigation\n if (propertyPath.length === 0) {\n // Simple table reference\n return (namespacedRow) => namespacedRow[tableAlias]\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n return tableData?.[prop]\n }\n } else {\n // Multiple property navigation\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n if (tableData === undefined) {\n return undefined\n }\n\n let value: any = tableData\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n}\n\n/**\n * Compiles a reference expression for single-row evaluation\n */\nfunction compileSingleRowRef(ref: PropRef): CompiledSingleRowExpression {\n const propertyPath = ref.path\n\n // This function works for all path lengths including empty path\n return (item) => {\n let value: any = item\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n}\n\n/**\n * Compiles a function expression for both namespaced and single-row evaluation\n */\nfunction compileFunction(func: Func, isSingleRow: boolean): (data: any) => any {\n // Pre-compile all arguments using the appropriate compiler\n const compiledArgs = func.args.map((arg) =>\n compileExpressionInternal(arg, isSingleRow),\n )\n\n switch (func.name) {\n // Comparison operators\n case `eq`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = normalizeValue(argA(data))\n const b = normalizeValue(argB(data))\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n // Use areValuesEqual for proper Uint8Array/Buffer comparison\n return areValuesEqual(a, b)\n }\n }\n case `gt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a > b\n }\n }\n case `gte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a >= b\n }\n }\n case `lt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a < b\n }\n }\n case `lte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a <= b\n }\n }\n\n // Boolean operators\n case `and`:\n return (data) => {\n // 3-valued logic for AND:\n // - false AND anything = false (short-circuit)\n // - null AND false = false\n // - null AND anything (except false) = null\n // - anything (except false) AND null = null\n // - true AND true = true\n let hasUnknown = false\n for (const compiledArg of compiledArgs) {\n const result = compiledArg(data)\n if (result === false) {\n return false\n }\n if (isUnknown(result)) {\n hasUnknown = true\n }\n }\n // If we got here, no operand was false\n // If any operand was null, return null (UNKNOWN)\n if (hasUnknown) {\n return null\n }\n\n return true\n }\n case `or`:\n return (data) => {\n // 3-valued logic for OR:\n // - true OR anything = true (short-circuit)\n // - null OR anything (except true) = null\n // - false OR false = false\n let hasUnknown = false\n for (const compiledArg of compiledArgs) {\n const result = compiledArg(data)\n if (result === true) {\n return true\n }\n if (isUnknown(result)) {\n hasUnknown = true\n }\n }\n // If we got here, no operand was true\n // If any operand was null, return null (UNKNOWN)\n if (hasUnknown) {\n return null\n }\n\n return false\n }\n case `not`: {\n const arg = compiledArgs[0]!\n return (data) => {\n // 3-valued logic for NOT:\n // - NOT null = null\n // - NOT true = false\n // - NOT false = true\n const result = arg(data)\n if (isUnknown(result)) {\n return null\n }\n return !result\n }\n }\n\n // Array operators\n case `in`: {\n const valueEvaluator = compiledArgs[0]!\n const arrayEvaluator = compiledArgs[1]!\n return (data) => {\n const value = normalizeValue(valueEvaluator(data))\n const array = arrayEvaluator(data)\n // In 3-valued logic, if the value is null/undefined, return UNKNOWN\n if (isUnknown(value)) {\n return null\n }\n if (!Array.isArray(array)) {\n return false\n }\n return array.some((item) => normalizeValue(item) === value)\n }\n }\n\n // String operators\n case `like`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n // In 3-valued logic, if value or pattern is null/undefined, return UNKNOWN\n if (isUnknown(value) || isUnknown(pattern)) {\n return null\n }\n return evaluateLike(value, pattern, false)\n }\n }\n case `ilike`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n // In 3-valued logic, if value or pattern is null/undefined, return UNKNOWN\n if (isUnknown(value) || isUnknown(pattern)) {\n return null\n }\n return evaluateLike(value, pattern, true)\n }\n }\n\n // String functions\n case `upper`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toUpperCase() : value\n }\n }\n case `lower`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toLowerCase() : value\n }\n }\n case `length`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n if (typeof value === `string`) {\n return value.length\n }\n if (Array.isArray(value)) {\n return value.length\n }\n return 0\n }\n }\n case `concat`:\n return (data) => {\n return compiledArgs\n .map((evaluator) => {\n const arg = evaluator(data)\n try {\n return String(arg ?? ``)\n } catch {\n try {\n return JSON.stringify(arg) || ``\n } catch {\n return `[object]`\n }\n }\n })\n .join(``)\n }\n case `coalesce`:\n return (data) => {\n for (const evaluator of compiledArgs) {\n const value = evaluator(data)\n if (value !== null && value !== undefined) {\n return value\n }\n }\n return null\n }\n\n // Math functions\n case `add`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) + (b ?? 0)\n }\n }\n case `subtract`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) - (b ?? 0)\n }\n }\n case `multiply`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) * (b ?? 0)\n }\n }\n case `divide`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n const divisor = b ?? 0\n return divisor !== 0 ? (a ?? 0) / divisor : null\n }\n }\n\n // Null/undefined checking functions\n case `isUndefined`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return value === undefined\n }\n }\n case `isNull`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return value === null\n }\n }\n\n default:\n throw new UnknownFunctionError(func.name)\n }\n}\n\n/**\n * Evaluates LIKE/ILIKE patterns\n */\nfunction evaluateLike(\n value: any,\n pattern: any,\n caseInsensitive: boolean,\n): boolean {\n if (typeof value !== `string` || typeof pattern !== `string`) {\n return false\n }\n\n const searchValue = caseInsensitive ? value.toLowerCase() : value\n const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern\n\n // Convert SQL LIKE pattern to regex\n // First escape all regex special chars except % and _\n let regexPattern = searchPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n\n // Then convert SQL wildcards to regex\n regexPattern = regexPattern.replace(/%/g, `.*`) // % matches any sequence\n regexPattern = regexPattern.replace(/_/g, `.`) // _ matches any single char\n\n // 's' (dotAll flag) makes '.' match all characters including line terminations\n const regex = new RegExp(`^${regexPattern}$`, 's')\n return regex.test(searchValue)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evaluators.js","sources":["../../../../src/query/compiler/evaluators.ts"],"sourcesContent":["import {\n EmptyReferencePathError,\n UnknownExpressionTypeError,\n UnknownFunctionError,\n} from '../../errors.js'\nimport { areValuesEqual, normalizeValue } from '../../utils/comparison.js'\nimport type { BasicExpression, Func, PropRef } from '../ir.js'\nimport type { NamespacedRow } from '../../types.js'\n\n/**\n * Helper function to check if a value is null or undefined (represents UNKNOWN in 3-valued logic)\n */\nfunction isUnknown(value: any): boolean {\n return value === null || value === undefined\n}\n\nfunction toDateValue(value: any): Date | null {\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? null : value\n }\n\n if (typeof value === `string` || typeof value === `number`) {\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? null : parsed\n }\n\n return null\n}\n\nfunction evaluateStrftime(format: string, date: Date): string {\n if (format === `%Y-%m-%d`) {\n return date.toISOString().slice(0, 10)\n }\n\n if (format === `%Y-%m-%dT%H:%M:%fZ`) {\n return date.toISOString()\n }\n\n return date.toISOString()\n}\n\n/**\n * Converts a 3-valued logic result to a boolean for use in WHERE/HAVING filters.\n * In SQL, UNKNOWN (null) values in WHERE clauses exclude rows, matching false behavior.\n *\n * @param result - The 3-valued logic result: true, false, or null (UNKNOWN)\n * @returns true only if result is explicitly true, false otherwise\n *\n * Truth table:\n * - true → true (include row)\n * - false → false (exclude row)\n * - null (UNKNOWN) → false (exclude row, matching SQL behavior)\n */\nexport function toBooleanPredicate(result: boolean | null): boolean {\n return result === true\n}\n\n/**\n * Compiled expression evaluator function type\n */\nexport type CompiledExpression = (namespacedRow: NamespacedRow) => any\n\n/**\n * Compiled single-row expression evaluator function type\n */\nexport type CompiledSingleRowExpression = (item: Record<string, unknown>) => any\n\n/**\n * Compiles an expression into an optimized evaluator function.\n * This eliminates branching during evaluation by pre-compiling the expression structure.\n */\nexport function compileExpression(\n expr: BasicExpression,\n isSingleRow: boolean = false,\n): CompiledExpression | CompiledSingleRowExpression {\n const compiledFn = compileExpressionInternal(expr, isSingleRow)\n return compiledFn\n}\n\n/**\n * Compiles a single-row expression into an optimized evaluator function.\n */\nexport function compileSingleRowExpression(\n expr: BasicExpression,\n): CompiledSingleRowExpression {\n const compiledFn = compileExpressionInternal(expr, true)\n return compiledFn as CompiledSingleRowExpression\n}\n\n/**\n * Internal unified expression compiler that handles both namespaced and single-row evaluation\n */\nfunction compileExpressionInternal(\n expr: BasicExpression,\n isSingleRow: boolean,\n): (data: any) => any {\n switch (expr.type) {\n case `val`: {\n // For constant values, return a function that just returns the value\n const value = expr.value\n return () => value\n }\n\n case `ref`: {\n // For references, compile based on evaluation mode\n return isSingleRow ? compileSingleRowRef(expr) : compileRef(expr)\n }\n\n case `func`: {\n // For functions, use the unified compiler\n return compileFunction(expr, isSingleRow)\n }\n\n default:\n throw new UnknownExpressionTypeError((expr as any).type)\n }\n}\n\n/**\n * Compiles a reference expression into an optimized evaluator\n */\nfunction compileRef(ref: PropRef): CompiledExpression {\n const [namespace, ...propertyPath] = ref.path\n\n if (!namespace) {\n throw new EmptyReferencePathError()\n }\n\n // Handle $selected namespace - references SELECT result fields\n if (namespace === `$selected`) {\n // Access $selected directly\n if (propertyPath.length === 0) {\n // Just $selected - return entire $selected object\n return (namespacedRow) => (namespacedRow as any).$selected\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const selectResults = (namespacedRow as any).$selected\n return selectResults?.[prop]\n }\n } else {\n // Multiple property navigation (nested SELECT fields)\n return (namespacedRow) => {\n const selectResults = (namespacedRow as any).$selected\n if (selectResults === undefined) {\n return undefined\n }\n\n let value: any = selectResults\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n }\n\n // Handle table alias namespace (existing logic)\n const tableAlias = namespace\n\n // Pre-compile the property path navigation\n if (propertyPath.length === 0) {\n // Simple table reference\n return (namespacedRow) => namespacedRow[tableAlias]\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n return tableData?.[prop]\n }\n } else {\n // Multiple property navigation\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n if (tableData === undefined) {\n return undefined\n }\n\n let value: any = tableData\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n}\n\n/**\n * Compiles a reference expression for single-row evaluation\n */\nfunction compileSingleRowRef(ref: PropRef): CompiledSingleRowExpression {\n const propertyPath = ref.path\n\n // This function works for all path lengths including empty path\n return (item) => {\n let value: any = item\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n}\n\n/**\n * Compiles a function expression for both namespaced and single-row evaluation\n */\nfunction compileFunction(func: Func, isSingleRow: boolean): (data: any) => any {\n // Pre-compile all arguments using the appropriate compiler\n const compiledArgs = func.args.map((arg) =>\n compileExpressionInternal(arg, isSingleRow),\n )\n\n switch (func.name) {\n // Comparison operators\n case `eq`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = normalizeValue(argA(data))\n const b = normalizeValue(argB(data))\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n // Use areValuesEqual for proper Uint8Array/Buffer comparison\n return areValuesEqual(a, b)\n }\n }\n case `gt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a > b\n }\n }\n case `gte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a >= b\n }\n }\n case `lt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a < b\n }\n }\n case `lte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a <= b\n }\n }\n\n // Boolean operators\n case `and`:\n return (data) => {\n // 3-valued logic for AND:\n // - false AND anything = false (short-circuit)\n // - null AND false = false\n // - null AND anything (except false) = null\n // - anything (except false) AND null = null\n // - true AND true = true\n let hasUnknown = false\n for (const compiledArg of compiledArgs) {\n const result = compiledArg(data)\n if (result === false) {\n return false\n }\n if (isUnknown(result)) {\n hasUnknown = true\n }\n }\n // If we got here, no operand was false\n // If any operand was null, return null (UNKNOWN)\n if (hasUnknown) {\n return null\n }\n\n return true\n }\n case `or`:\n return (data) => {\n // 3-valued logic for OR:\n // - true OR anything = true (short-circuit)\n // - null OR anything (except true) = null\n // - false OR false = false\n let hasUnknown = false\n for (const compiledArg of compiledArgs) {\n const result = compiledArg(data)\n if (result === true) {\n return true\n }\n if (isUnknown(result)) {\n hasUnknown = true\n }\n }\n // If we got here, no operand was true\n // If any operand was null, return null (UNKNOWN)\n if (hasUnknown) {\n return null\n }\n\n return false\n }\n case `not`: {\n const arg = compiledArgs[0]!\n return (data) => {\n // 3-valued logic for NOT:\n // - NOT null = null\n // - NOT true = false\n // - NOT false = true\n const result = arg(data)\n if (isUnknown(result)) {\n return null\n }\n return !result\n }\n }\n\n // Array operators\n case `in`: {\n const valueEvaluator = compiledArgs[0]!\n const arrayEvaluator = compiledArgs[1]!\n return (data) => {\n const value = normalizeValue(valueEvaluator(data))\n const array = arrayEvaluator(data)\n // In 3-valued logic, if the value is null/undefined, return UNKNOWN\n if (isUnknown(value)) {\n return null\n }\n if (!Array.isArray(array)) {\n return false\n }\n return array.some((item) => normalizeValue(item) === value)\n }\n }\n\n // String operators\n case `like`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n // In 3-valued logic, if value or pattern is null/undefined, return UNKNOWN\n if (isUnknown(value) || isUnknown(pattern)) {\n return null\n }\n return evaluateLike(value, pattern, false)\n }\n }\n case `ilike`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n // In 3-valued logic, if value or pattern is null/undefined, return UNKNOWN\n if (isUnknown(value) || isUnknown(pattern)) {\n return null\n }\n return evaluateLike(value, pattern, true)\n }\n }\n\n // String functions\n case `upper`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toUpperCase() : value\n }\n }\n case `lower`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toLowerCase() : value\n }\n }\n case `length`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n if (typeof value === `string`) {\n return value.length\n }\n if (Array.isArray(value)) {\n return value.length\n }\n return 0\n }\n }\n case `concat`:\n return (data) => {\n return compiledArgs\n .map((evaluator) => {\n const arg = evaluator(data)\n try {\n return String(arg ?? ``)\n } catch {\n try {\n return JSON.stringify(arg) || ``\n } catch {\n return `[object]`\n }\n }\n })\n .join(``)\n }\n case `coalesce`:\n return (data) => {\n for (const evaluator of compiledArgs) {\n const value = evaluator(data)\n if (value !== null && value !== undefined) {\n return value\n }\n }\n return null\n }\n\n // Math functions\n case `add`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) + (b ?? 0)\n }\n }\n case `subtract`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) - (b ?? 0)\n }\n }\n case `multiply`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) * (b ?? 0)\n }\n }\n case `divide`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n const divisor = b ?? 0\n return divisor !== 0 ? (a ?? 0) / divisor : null\n }\n }\n case `date`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n const dateValue = toDateValue(value)\n return dateValue ? dateValue.toISOString().slice(0, 10) : null\n }\n }\n case `datetime`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n const dateValue = toDateValue(value)\n return dateValue ? dateValue.toISOString() : null\n }\n }\n case `strftime`: {\n const formatArg = compiledArgs[0]!\n const sourceArg = compiledArgs[1]!\n return (data) => {\n const format = formatArg(data)\n if (typeof format !== `string`) {\n return null\n }\n const sourceValue = sourceArg(data)\n const dateValue = toDateValue(sourceValue)\n if (!dateValue) {\n return null\n }\n return evaluateStrftime(format, dateValue)\n }\n }\n\n // Null/undefined checking functions\n case `isUndefined`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return value === undefined\n }\n }\n case `isNull`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return value === null\n }\n }\n\n default:\n throw new UnknownFunctionError(func.name)\n }\n}\n\n/**\n * Evaluates LIKE/ILIKE patterns\n */\nfunction evaluateLike(\n value: any,\n pattern: any,\n caseInsensitive: boolean,\n): boolean {\n if (typeof value !== `string` || typeof pattern !== `string`) {\n return false\n }\n\n const searchValue = caseInsensitive ? value.toLowerCase() : value\n const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern\n\n // Convert SQL LIKE pattern to regex\n // First escape all regex special chars except % and _\n let regexPattern = searchPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n\n // Then convert SQL wildcards to regex\n regexPattern = regexPattern.replace(/%/g, `.*`) // % matches any sequence\n regexPattern = regexPattern.replace(/_/g, `.`) // _ matches any single char\n\n // 's' (dotAll flag) makes '.' match all characters including line terminations\n const regex = new RegExp(`^${regexPattern}$`, 's')\n return regex.test(searchValue)\n}\n"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import { NamespacedAndKeyedStream } from '../../types.js';
4
4
  * Processes the GROUP BY clause with optional HAVING and SELECT
5
5
  * Works with the new $selected structure from early SELECT processing
6
6
  */
7
- export declare function processGroupBy(pipeline: NamespacedAndKeyedStream, groupByClause: GroupBy, havingClauses?: Array<Having>, selectClause?: Select, fnHavingClauses?: Array<(row: any) => any>): NamespacedAndKeyedStream;
7
+ export declare function processGroupBy(pipeline: NamespacedAndKeyedStream, groupByClause: GroupBy, havingClauses?: Array<Having>, selectClause?: Select, fnHavingClauses?: Array<(row: any) => any>, aggregateCollectionId?: string, mainSource?: string): NamespacedAndKeyedStream;
8
8
  /**
9
9
  * Transforms expressions to replace aggregate functions with references to computed values.
10
10
  *
@@ -25,4 +25,6 @@ export declare function replaceAggregatesByRefs(havingExpr: BasicExpression | Ag
25
25
  * Returns true for a top-level Aggregate, or a Func whose args (recursively)
26
26
  * contain an Aggregate. Safely returns false for nested Select objects.
27
27
  */
28
- export declare function containsAggregate(expr: BasicExpression | Aggregate | Select): boolean;
28
+ export declare function containsAggregate(expr: BasicExpression | Aggregate | Select | {
29
+ type: string;
30
+ }): boolean;
@@ -1,7 +1,34 @@
1
- import { groupBy, map, filter, serializeValue, groupByOperators } from "@tanstack/db-ivm";
1
+ import { groupBy, map, serializeValue, filter, groupByOperators } from "@tanstack/db-ivm";
2
2
  import { getHavingExpression, isExpressionLike, PropRef, Func } from "../ir.js";
3
3
  import { UnsupportedAggregateFunctionError, UnknownHavingExpressionTypeError, AggregateFunctionNotInSelectError, NonAggregateExpressionNotInGroupByError } from "../../errors.js";
4
4
  import { compileExpression, toBooleanPredicate } from "./evaluators.js";
5
+ const VIRTUAL_SYNCED_KEY = `__virtual_synced__`;
6
+ const VIRTUAL_HAS_LOCAL_KEY = `__virtual_has_local__`;
7
+ function getRowVirtualMetadata(row) {
8
+ let found = false;
9
+ let allSynced = true;
10
+ let hasLocal = false;
11
+ for (const [alias, value] of Object.entries(row)) {
12
+ if (alias === `$selected`) continue;
13
+ const asRecord = value;
14
+ const hasSyncedProp = `$synced` in asRecord;
15
+ const hasOriginProp = `$origin` in asRecord;
16
+ if (!hasSyncedProp && !hasOriginProp) {
17
+ continue;
18
+ }
19
+ found = true;
20
+ if (asRecord.$synced === false) {
21
+ allSynced = false;
22
+ }
23
+ if (asRecord.$origin === `local`) {
24
+ hasLocal = true;
25
+ }
26
+ }
27
+ return {
28
+ synced: found ? allSynced : true,
29
+ hasLocal
30
+ };
31
+ }
5
32
  const { sum, count, avg, min, max } = groupByOperators;
6
33
  function validateAndCreateMapping(groupByClause, selectClause) {
7
34
  const selectToGroupByIndex = /* @__PURE__ */ new Map();
@@ -23,9 +50,33 @@ function validateAndCreateMapping(groupByClause, selectClause) {
23
50
  }
24
51
  return { selectToGroupByIndex, groupByExpressions };
25
52
  }
26
- function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fnHavingClauses) {
53
+ function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fnHavingClauses, aggregateCollectionId, mainSource) {
54
+ const virtualAggregates = {
55
+ [VIRTUAL_SYNCED_KEY]: {
56
+ preMap: ([, row]) => getRowVirtualMetadata(row).synced,
57
+ reduce: (values) => {
58
+ for (const [isSynced, multiplicity] of values) {
59
+ if (!isSynced && multiplicity > 0) {
60
+ return false;
61
+ }
62
+ }
63
+ return true;
64
+ }
65
+ },
66
+ [VIRTUAL_HAS_LOCAL_KEY]: {
67
+ preMap: ([, row]) => getRowVirtualMetadata(row).hasLocal,
68
+ reduce: (values) => {
69
+ for (const [isLocal, multiplicity] of values) {
70
+ if (isLocal && multiplicity > 0) {
71
+ return true;
72
+ }
73
+ }
74
+ return false;
75
+ }
76
+ }
77
+ };
27
78
  if (groupByClause.length === 0) {
28
- const aggregates2 = {};
79
+ const aggregates2 = virtualAggregates;
29
80
  const wrappedAggExprs2 = {};
30
81
  const aggCounter2 = { value: 0 };
31
82
  if (selectClause) {
@@ -44,7 +95,10 @@ function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fn
44
95
  }
45
96
  }
46
97
  }
47
- const keyExtractor2 = () => ({ __singleGroup: true });
98
+ const keyExtractor2 = mainSource ? ([, row]) => ({
99
+ __singleGroup: true,
100
+ __correlationKey: row?.[mainSource]?.__correlationKey
101
+ }) : () => ({ __singleGroup: true });
48
102
  pipeline = pipeline.pipe(
49
103
  groupBy(keyExtractor2, aggregates2)
50
104
  );
@@ -64,13 +118,22 @@ function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fn
64
118
  wrappedAggExprs2
65
119
  );
66
120
  }
67
- return [
68
- `single_group`,
69
- {
70
- ...aggregatedRow,
71
- $selected: finalResults
72
- }
73
- ];
121
+ const correlationKey = mainSource ? aggregatedRow.__correlationKey : void 0;
122
+ const resultKey = correlationKey !== void 0 ? `single_group_${serializeValue(correlationKey)}` : `single_group`;
123
+ const resultRow = {
124
+ ...aggregatedRow,
125
+ $selected: finalResults
126
+ };
127
+ const groupSynced = aggregatedRow[VIRTUAL_SYNCED_KEY];
128
+ const groupHasLocal = aggregatedRow[VIRTUAL_HAS_LOCAL_KEY];
129
+ resultRow.$synced = groupSynced ?? true;
130
+ resultRow.$origin = groupHasLocal ? `local` : `remote`;
131
+ resultRow.$key = resultKey;
132
+ resultRow.$collectionId = aggregateCollectionId ?? resultRow.$collectionId;
133
+ if (mainSource && correlationKey !== void 0) {
134
+ resultRow[mainSource] = { __correlationKey: correlationKey };
135
+ }
136
+ return [resultKey, resultRow];
74
137
  })
75
138
  );
76
139
  if (havingClauses && havingClauses.length > 0) {
@@ -115,9 +178,12 @@ function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fn
115
178
  const value = compiledExpr(namespacedRow);
116
179
  key[`__key_${i}`] = value;
117
180
  }
181
+ if (mainSource) {
182
+ key.__correlationKey = row?.[mainSource]?.__correlationKey;
183
+ }
118
184
  return key;
119
185
  };
120
- const aggregates = {};
186
+ const aggregates = virtualAggregates;
121
187
  const wrappedAggExprs = {};
122
188
  const aggCounter = { value: 0 };
123
189
  if (selectClause) {
@@ -164,23 +230,29 @@ function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fn
164
230
  finalResults[`__key_${i}`] = aggregatedRow[`__key_${i}`];
165
231
  }
166
232
  }
167
- let finalKey;
168
- if (groupByClause.length === 1) {
169
- finalKey = aggregatedRow[`__key_0`];
170
- } else {
171
- const keyParts = [];
172
- for (let i = 0; i < groupByClause.length; i++) {
173
- keyParts.push(aggregatedRow[`__key_${i}`]);
174
- }
175
- finalKey = serializeValue(keyParts);
233
+ const correlationKey = mainSource ? aggregatedRow.__correlationKey : void 0;
234
+ const keyParts = [];
235
+ for (let i = 0; i < groupByClause.length; i++) {
236
+ keyParts.push(aggregatedRow[`__key_${i}`]);
176
237
  }
177
- return [
178
- finalKey,
179
- {
180
- ...aggregatedRow,
181
- $selected: finalResults
182
- }
183
- ];
238
+ if (correlationKey !== void 0) {
239
+ keyParts.push(correlationKey);
240
+ }
241
+ const finalKey = keyParts.length === 1 ? keyParts[0] : serializeValue(keyParts);
242
+ const resultRow = {
243
+ ...aggregatedRow,
244
+ $selected: finalResults
245
+ };
246
+ const groupSynced = aggregatedRow[VIRTUAL_SYNCED_KEY];
247
+ const groupHasLocal = aggregatedRow[VIRTUAL_HAS_LOCAL_KEY];
248
+ resultRow.$synced = groupSynced ?? true;
249
+ resultRow.$origin = groupHasLocal ? `local` : `remote`;
250
+ resultRow.$key = finalKey;
251
+ resultRow.$collectionId = aggregateCollectionId ?? resultRow.$collectionId;
252
+ if (mainSource && correlationKey !== void 0) {
253
+ resultRow[mainSource] = { __correlationKey: correlationKey };
254
+ }
255
+ return [finalKey, resultRow];
184
256
  })
185
257
  );
186
258
  if (havingClauses && havingClauses.length > 0) {
@@ -315,7 +387,7 @@ function containsAggregate(expr) {
315
387
  if (expr.type === `agg`) {
316
388
  return true;
317
389
  }
318
- if (expr.type === `func`) {
390
+ if (expr.type === `func` && `args` in expr) {
319
391
  return expr.args.some(
320
392
  (arg) => containsAggregate(arg)
321
393
  );