@tanstack/db 0.5.32 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/dist/cjs/collection/change-events.cjs.map +1 -1
  2. package/dist/cjs/collection/change-events.d.cts +3 -2
  3. package/dist/cjs/collection/changes.cjs +13 -4
  4. package/dist/cjs/collection/changes.cjs.map +1 -1
  5. package/dist/cjs/collection/changes.d.cts +10 -1
  6. package/dist/cjs/collection/cleanup-queue.cjs +89 -0
  7. package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
  8. package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
  9. package/dist/cjs/collection/events.cjs +14 -0
  10. package/dist/cjs/collection/events.cjs.map +1 -1
  11. package/dist/cjs/collection/events.d.cts +39 -1
  12. package/dist/cjs/collection/index.cjs +66 -28
  13. package/dist/cjs/collection/index.cjs.map +1 -1
  14. package/dist/cjs/collection/index.d.cts +49 -36
  15. package/dist/cjs/collection/indexes.cjs +211 -62
  16. package/dist/cjs/collection/indexes.cjs.map +1 -1
  17. package/dist/cjs/collection/indexes.d.cts +27 -17
  18. package/dist/cjs/collection/lifecycle.cjs +5 -22
  19. package/dist/cjs/collection/lifecycle.cjs.map +1 -1
  20. package/dist/cjs/collection/lifecycle.d.cts +0 -1
  21. package/dist/cjs/collection/mutations.cjs +18 -0
  22. package/dist/cjs/collection/mutations.cjs.map +1 -1
  23. package/dist/cjs/collection/mutations.d.cts +1 -0
  24. package/dist/cjs/collection/state.cjs +381 -53
  25. package/dist/cjs/collection/state.cjs.map +1 -1
  26. package/dist/cjs/collection/state.d.cts +65 -1
  27. package/dist/cjs/collection/subscription.cjs +6 -0
  28. package/dist/cjs/collection/subscription.cjs.map +1 -1
  29. package/dist/cjs/collection/subscription.d.cts +4 -0
  30. package/dist/cjs/collection/sync.cjs +108 -1
  31. package/dist/cjs/collection/sync.cjs.map +1 -1
  32. package/dist/cjs/collection/sync.d.cts +2 -0
  33. package/dist/cjs/collection/transaction-metadata.cjs +5 -0
  34. package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
  35. package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
  36. package/dist/cjs/errors.cjs +8 -0
  37. package/dist/cjs/errors.cjs.map +1 -1
  38. package/dist/cjs/errors.d.cts +3 -0
  39. package/dist/cjs/index.cjs +24 -4
  40. package/dist/cjs/index.cjs.map +1 -1
  41. package/dist/cjs/index.d.cts +12 -3
  42. package/dist/cjs/indexes/auto-index.cjs +13 -6
  43. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  44. package/dist/cjs/indexes/base-index.cjs +0 -3
  45. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  46. package/dist/cjs/indexes/base-index.d.cts +2 -6
  47. package/dist/cjs/indexes/basic-index.cjs +361 -0
  48. package/dist/cjs/indexes/basic-index.cjs.map +1 -0
  49. package/dist/cjs/indexes/basic-index.d.cts +102 -0
  50. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  51. package/dist/cjs/indexes/btree-index.d.cts +1 -1
  52. package/dist/cjs/indexes/index-options.d.cts +8 -9
  53. package/dist/cjs/indexes/index-registry.cjs +89 -0
  54. package/dist/cjs/indexes/index-registry.cjs.map +1 -0
  55. package/dist/cjs/indexes/index-registry.d.cts +61 -0
  56. package/dist/cjs/local-only.cjs +5 -0
  57. package/dist/cjs/local-only.cjs.map +1 -1
  58. package/dist/cjs/query/builder/functions.cjs +27 -11
  59. package/dist/cjs/query/builder/functions.cjs.map +1 -1
  60. package/dist/cjs/query/builder/functions.d.cts +25 -3
  61. package/dist/cjs/query/builder/index.cjs +200 -39
  62. package/dist/cjs/query/builder/index.cjs.map +1 -1
  63. package/dist/cjs/query/builder/index.d.cts +4 -3
  64. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
  65. package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
  66. package/dist/cjs/query/builder/types.d.cts +84 -19
  67. package/dist/cjs/query/compiler/evaluators.cjs +51 -0
  68. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
  69. package/dist/cjs/query/compiler/group-by.cjs +100 -28
  70. package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
  71. package/dist/cjs/query/compiler/group-by.d.cts +4 -2
  72. package/dist/cjs/query/compiler/index.cjs +283 -11
  73. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  74. package/dist/cjs/query/compiler/index.d.cts +30 -2
  75. package/dist/cjs/query/compiler/order-by.cjs +29 -10
  76. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  77. package/dist/cjs/query/compiler/order-by.d.cts +1 -1
  78. package/dist/cjs/query/compiler/select.cjs +8 -0
  79. package/dist/cjs/query/compiler/select.cjs.map +1 -1
  80. package/dist/cjs/query/effect.cjs +602 -0
  81. package/dist/cjs/query/effect.cjs.map +1 -0
  82. package/dist/cjs/query/effect.d.cts +94 -0
  83. package/dist/cjs/query/index.d.cts +2 -1
  84. package/dist/cjs/query/ir.cjs +18 -1
  85. package/dist/cjs/query/ir.cjs.map +1 -1
  86. package/dist/cjs/query/ir.d.cts +21 -1
  87. package/dist/cjs/query/live/collection-config-builder.cjs +493 -66
  88. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  89. package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
  90. package/dist/cjs/query/live/collection-subscriber.cjs +33 -100
  91. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  92. package/dist/cjs/query/live/collection-subscriber.d.cts +0 -1
  93. package/dist/cjs/query/live/types.d.cts +3 -3
  94. package/dist/cjs/query/live/utils.cjs +219 -0
  95. package/dist/cjs/query/live/utils.cjs.map +1 -0
  96. package/dist/cjs/query/live/utils.d.cts +110 -0
  97. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  98. package/dist/cjs/query/live-query-collection.d.cts +9 -6
  99. package/dist/cjs/query/query-once.cjs.map +1 -1
  100. package/dist/cjs/query/query-once.d.cts +7 -5
  101. package/dist/cjs/query/subset-dedupe.cjs +9 -3
  102. package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
  103. package/dist/cjs/types.d.cts +42 -8
  104. package/dist/cjs/utils/array-utils.cjs +27 -0
  105. package/dist/cjs/utils/array-utils.cjs.map +1 -0
  106. package/dist/cjs/utils/array-utils.d.cts +16 -0
  107. package/dist/cjs/utils/comparison.cjs +11 -0
  108. package/dist/cjs/utils/comparison.cjs.map +1 -1
  109. package/dist/cjs/utils/index-optimization.cjs +4 -0
  110. package/dist/cjs/utils/index-optimization.cjs.map +1 -1
  111. package/dist/cjs/utils.cjs +7 -9
  112. package/dist/cjs/utils.cjs.map +1 -1
  113. package/dist/cjs/utils.d.cts +6 -1
  114. package/dist/cjs/virtual-props.cjs +33 -0
  115. package/dist/cjs/virtual-props.cjs.map +1 -0
  116. package/dist/cjs/virtual-props.d.cts +196 -0
  117. package/dist/esm/collection/change-events.d.ts +3 -2
  118. package/dist/esm/collection/change-events.js.map +1 -1
  119. package/dist/esm/collection/changes.d.ts +10 -1
  120. package/dist/esm/collection/changes.js +13 -4
  121. package/dist/esm/collection/changes.js.map +1 -1
  122. package/dist/esm/collection/cleanup-queue.d.ts +30 -0
  123. package/dist/esm/collection/cleanup-queue.js +89 -0
  124. package/dist/esm/collection/cleanup-queue.js.map +1 -0
  125. package/dist/esm/collection/events.d.ts +39 -1
  126. package/dist/esm/collection/events.js +14 -0
  127. package/dist/esm/collection/events.js.map +1 -1
  128. package/dist/esm/collection/index.d.ts +49 -36
  129. package/dist/esm/collection/index.js +67 -29
  130. package/dist/esm/collection/index.js.map +1 -1
  131. package/dist/esm/collection/indexes.d.ts +27 -17
  132. package/dist/esm/collection/indexes.js +211 -62
  133. package/dist/esm/collection/indexes.js.map +1 -1
  134. package/dist/esm/collection/lifecycle.d.ts +0 -1
  135. package/dist/esm/collection/lifecycle.js +5 -22
  136. package/dist/esm/collection/lifecycle.js.map +1 -1
  137. package/dist/esm/collection/mutations.d.ts +1 -0
  138. package/dist/esm/collection/mutations.js +18 -0
  139. package/dist/esm/collection/mutations.js.map +1 -1
  140. package/dist/esm/collection/state.d.ts +65 -1
  141. package/dist/esm/collection/state.js +381 -53
  142. package/dist/esm/collection/state.js.map +1 -1
  143. package/dist/esm/collection/subscription.d.ts +4 -0
  144. package/dist/esm/collection/subscription.js +6 -0
  145. package/dist/esm/collection/subscription.js.map +1 -1
  146. package/dist/esm/collection/sync.d.ts +2 -0
  147. package/dist/esm/collection/sync.js +108 -1
  148. package/dist/esm/collection/sync.js.map +1 -1
  149. package/dist/esm/collection/transaction-metadata.d.ts +1 -0
  150. package/dist/esm/collection/transaction-metadata.js +5 -0
  151. package/dist/esm/collection/transaction-metadata.js.map +1 -0
  152. package/dist/esm/errors.d.ts +3 -0
  153. package/dist/esm/errors.js +8 -0
  154. package/dist/esm/errors.js.map +1 -1
  155. package/dist/esm/index.d.ts +12 -3
  156. package/dist/esm/index.js +27 -7
  157. package/dist/esm/index.js.map +1 -1
  158. package/dist/esm/indexes/auto-index.js +13 -6
  159. package/dist/esm/indexes/auto-index.js.map +1 -1
  160. package/dist/esm/indexes/base-index.d.ts +2 -6
  161. package/dist/esm/indexes/base-index.js +1 -4
  162. package/dist/esm/indexes/base-index.js.map +1 -1
  163. package/dist/esm/indexes/basic-index.d.ts +102 -0
  164. package/dist/esm/indexes/basic-index.js +361 -0
  165. package/dist/esm/indexes/basic-index.js.map +1 -0
  166. package/dist/esm/indexes/btree-index.d.ts +1 -1
  167. package/dist/esm/indexes/btree-index.js.map +1 -1
  168. package/dist/esm/indexes/index-options.d.ts +8 -9
  169. package/dist/esm/indexes/index-registry.d.ts +61 -0
  170. package/dist/esm/indexes/index-registry.js +89 -0
  171. package/dist/esm/indexes/index-registry.js.map +1 -0
  172. package/dist/esm/local-only.js +5 -0
  173. package/dist/esm/local-only.js.map +1 -1
  174. package/dist/esm/query/builder/functions.d.ts +25 -3
  175. package/dist/esm/query/builder/functions.js +27 -11
  176. package/dist/esm/query/builder/functions.js.map +1 -1
  177. package/dist/esm/query/builder/index.d.ts +4 -3
  178. package/dist/esm/query/builder/index.js +201 -40
  179. package/dist/esm/query/builder/index.js.map +1 -1
  180. package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
  181. package/dist/esm/query/builder/ref-proxy.js.map +1 -1
  182. package/dist/esm/query/builder/types.d.ts +84 -19
  183. package/dist/esm/query/compiler/evaluators.js +51 -0
  184. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  185. package/dist/esm/query/compiler/group-by.d.ts +4 -2
  186. package/dist/esm/query/compiler/group-by.js +101 -29
  187. package/dist/esm/query/compiler/group-by.js.map +1 -1
  188. package/dist/esm/query/compiler/index.d.ts +30 -2
  189. package/dist/esm/query/compiler/index.js +285 -13
  190. package/dist/esm/query/compiler/index.js.map +1 -1
  191. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  192. package/dist/esm/query/compiler/order-by.js +30 -11
  193. package/dist/esm/query/compiler/order-by.js.map +1 -1
  194. package/dist/esm/query/compiler/select.js +8 -0
  195. package/dist/esm/query/compiler/select.js.map +1 -1
  196. package/dist/esm/query/effect.d.ts +94 -0
  197. package/dist/esm/query/effect.js +602 -0
  198. package/dist/esm/query/effect.js.map +1 -0
  199. package/dist/esm/query/index.d.ts +2 -1
  200. package/dist/esm/query/ir.d.ts +21 -1
  201. package/dist/esm/query/ir.js +18 -1
  202. package/dist/esm/query/ir.js.map +1 -1
  203. package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
  204. package/dist/esm/query/live/collection-config-builder.js +492 -65
  205. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  206. package/dist/esm/query/live/collection-subscriber.d.ts +0 -1
  207. package/dist/esm/query/live/collection-subscriber.js +31 -98
  208. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  209. package/dist/esm/query/live/types.d.ts +3 -3
  210. package/dist/esm/query/live/utils.d.ts +110 -0
  211. package/dist/esm/query/live/utils.js +219 -0
  212. package/dist/esm/query/live/utils.js.map +1 -0
  213. package/dist/esm/query/live-query-collection.d.ts +9 -6
  214. package/dist/esm/query/live-query-collection.js.map +1 -1
  215. package/dist/esm/query/query-once.d.ts +7 -5
  216. package/dist/esm/query/query-once.js.map +1 -1
  217. package/dist/esm/query/subset-dedupe.js +9 -3
  218. package/dist/esm/query/subset-dedupe.js.map +1 -1
  219. package/dist/esm/types.d.ts +42 -8
  220. package/dist/esm/utils/array-utils.d.ts +16 -0
  221. package/dist/esm/utils/array-utils.js +27 -0
  222. package/dist/esm/utils/array-utils.js.map +1 -0
  223. package/dist/esm/utils/comparison.js +11 -0
  224. package/dist/esm/utils/comparison.js.map +1 -1
  225. package/dist/esm/utils/index-optimization.js +4 -0
  226. package/dist/esm/utils/index-optimization.js.map +1 -1
  227. package/dist/esm/utils.d.ts +6 -1
  228. package/dist/esm/utils.js +7 -9
  229. package/dist/esm/utils.js.map +1 -1
  230. package/dist/esm/virtual-props.d.ts +196 -0
  231. package/dist/esm/virtual-props.js +33 -0
  232. package/dist/esm/virtual-props.js.map +1 -0
  233. package/package.json +2 -2
  234. package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
  235. package/src/collection/change-events.ts +13 -9
  236. package/src/collection/changes.ts +30 -7
  237. package/src/collection/cleanup-queue.ts +105 -0
  238. package/src/collection/events.ts +65 -0
  239. package/src/collection/index.ts +110 -45
  240. package/src/collection/indexes.ts +283 -76
  241. package/src/collection/lifecycle.ts +5 -26
  242. package/src/collection/mutations.ts +21 -0
  243. package/src/collection/state.ts +545 -71
  244. package/src/collection/subscription.ts +7 -0
  245. package/src/collection/sync.ts +137 -0
  246. package/src/collection/transaction-metadata.ts +1 -0
  247. package/src/errors.ts +9 -0
  248. package/src/index.ts +57 -3
  249. package/src/indexes/auto-index.ts +18 -8
  250. package/src/indexes/base-index.ts +2 -10
  251. package/src/indexes/basic-index.ts +507 -0
  252. package/src/indexes/btree-index.ts +1 -1
  253. package/src/indexes/index-options.ts +17 -37
  254. package/src/indexes/index-registry.ts +174 -0
  255. package/src/local-only.ts +7 -0
  256. package/src/query/builder/functions.ts +84 -7
  257. package/src/query/builder/index.ts +329 -9
  258. package/src/query/builder/ref-proxy.ts +22 -4
  259. package/src/query/builder/types.ts +257 -62
  260. package/src/query/compiler/evaluators.ts +57 -0
  261. package/src/query/compiler/group-by.ts +156 -35
  262. package/src/query/compiler/index.ts +445 -15
  263. package/src/query/compiler/order-by.ts +51 -12
  264. package/src/query/compiler/select.ts +9 -0
  265. package/src/query/effect.ts +1119 -0
  266. package/src/query/index.ts +7 -0
  267. package/src/query/ir.ts +23 -2
  268. package/src/query/live/collection-config-builder.ts +778 -104
  269. package/src/query/live/collection-subscriber.ts +40 -156
  270. package/src/query/live/types.ts +10 -4
  271. package/src/query/live/utils.ts +417 -0
  272. package/src/query/live-query-collection.ts +43 -18
  273. package/src/query/query-once.ts +31 -12
  274. package/src/query/subset-dedupe.ts +11 -7
  275. package/src/types.ts +49 -9
  276. package/src/utils/array-utils.ts +49 -0
  277. package/src/utils/comparison.ts +14 -0
  278. package/src/utils/index-optimization.ts +4 -0
  279. package/src/utils.ts +12 -9
  280. package/src/virtual-props.ts +282 -0
  281. package/dist/cjs/indexes/lazy-index.cjs +0 -190
  282. package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
  283. package/dist/cjs/indexes/lazy-index.d.cts +0 -96
  284. package/dist/esm/indexes/lazy-index.d.ts +0 -96
  285. package/dist/esm/indexes/lazy-index.js +0 -190
  286. package/dist/esm/indexes/lazy-index.js.map +0 -1
  287. package/src/indexes/lazy-index.ts +0 -251
@@ -1,12 +1,13 @@
1
- import { D2, output } from "@tanstack/db-ivm";
2
- import { compileQuery } from "../compiler/index.js";
3
- import { buildQuery, getQueryIR } from "../builder/index.js";
1
+ import { D2, output, serializeValue } from "@tanstack/db-ivm";
2
+ import { compileQuery, INCLUDES_ROUTING } from "../compiler/index.js";
3
+ import { createCollection } from "../../collection/index.js";
4
4
  import { SetWindowRequiresOrderByError, MissingAliasInputsError } from "../../errors.js";
5
5
  import { transactionScopedScheduler } from "../../scheduler.js";
6
6
  import { getActiveTransaction } from "../../transactions.js";
7
7
  import { CollectionSubscriber } from "./collection-subscriber.js";
8
8
  import { getCollectionBuilder } from "./collection-registry.js";
9
9
  import { LIVE_QUERY_INTERNAL } from "./internal.js";
10
+ import { buildQueryFromConfig, extractCollectionsFromQuery, extractCollectionAliases, extractCollectionFromSource } from "./utils.js";
10
11
  let liveQueryCollectionCounter = 0;
11
12
  class CollectionConfigBuilder {
12
13
  constructor(config) {
@@ -25,7 +26,10 @@ class CollectionConfigBuilder {
25
26
  this.lazySources = /* @__PURE__ */ new Set();
26
27
  this.optimizableOrderByCollections = {};
27
28
  this.id = config.id || `live-query-${++liveQueryCollectionCounter}`;
28
- this.query = buildQueryFromConfig(config);
29
+ this.query = buildQueryFromConfig({
30
+ query: config.query,
31
+ requireObjectResult: true
32
+ });
29
33
  this.collections = extractCollectionsFromQuery(this.query);
30
34
  const collectionAliasesById = extractCollectionAliases(this.query);
31
35
  this.collectionByAlias = {};
@@ -360,6 +364,7 @@ class CollectionConfigBuilder {
360
364
  this.inputsCache = void 0;
361
365
  this.pipelineCache = void 0;
362
366
  this.sourceWhereClausesCache = void 0;
367
+ this.includesCache = void 0;
363
368
  this.lazySources.clear();
364
369
  this.optimizableOrderByCollections = {};
365
370
  this.lazySourcesCallbacks = {};
@@ -397,6 +402,7 @@ class CollectionConfigBuilder {
397
402
  this.pipelineCache = compilation.pipeline;
398
403
  this.sourceWhereClausesCache = compilation.sourceWhereClauses;
399
404
  this.compiledAliasToCollectionId = compilation.aliasToCollectionId;
405
+ this.includesCache = compilation.includes;
400
406
  const missingAliases = Object.keys(this.compiledAliasToCollectionId).filter(
401
407
  (alias) => !Object.hasOwn(this.inputsCache, alias)
402
408
  );
@@ -425,8 +431,14 @@ class CollectionConfigBuilder {
425
431
  messages.reduce(accumulateChanges, pendingChanges);
426
432
  })
427
433
  );
434
+ const includesState = this.setupIncludesOutput(
435
+ this.includesCache,
436
+ syncState
437
+ );
428
438
  syncState.flushPendingChanges = () => {
429
- if (pendingChanges.size === 0) {
439
+ const hasParentChanges = pendingChanges.size > 0;
440
+ const hasChildChanges = hasPendingIncludesChanges(includesState);
441
+ if (!hasParentChanges && !hasChildChanges) {
430
442
  return;
431
443
  }
432
444
  let changesToApply = pendingChanges;
@@ -450,10 +462,19 @@ class CollectionConfigBuilder {
450
462
  }
451
463
  changesToApply = merged;
452
464
  }
453
- begin();
454
- changesToApply.forEach(this.applyChanges.bind(this, config));
455
- commit();
465
+ if (hasParentChanges) {
466
+ begin();
467
+ changesToApply.forEach(this.applyChanges.bind(this, config));
468
+ commit();
469
+ }
456
470
  pendingChanges = /* @__PURE__ */ new Map();
471
+ flushIncludesState(
472
+ includesState,
473
+ config.collection,
474
+ this.id,
475
+ hasParentChanges ? changesToApply : null,
476
+ config
477
+ );
457
478
  };
458
479
  graph.finalize();
459
480
  syncState.graph = graph;
@@ -461,6 +482,65 @@ class CollectionConfigBuilder {
461
482
  syncState.pipeline = pipeline;
462
483
  return syncState;
463
484
  }
485
+ /**
486
+ * Sets up output callbacks for includes child pipelines.
487
+ * Each includes entry gets its own output callback that accumulates child changes,
488
+ * and a child registry that maps correlation key → child Collection.
489
+ */
490
+ setupIncludesOutput(includesEntries, syncState) {
491
+ if (!includesEntries || includesEntries.length === 0) {
492
+ return [];
493
+ }
494
+ return includesEntries.map((entry) => {
495
+ const state = {
496
+ fieldName: entry.fieldName,
497
+ childCorrelationField: entry.childCorrelationField,
498
+ hasOrderBy: entry.hasOrderBy,
499
+ materialization: entry.materialization,
500
+ scalarField: entry.scalarField,
501
+ childRegistry: /* @__PURE__ */ new Map(),
502
+ pendingChildChanges: /* @__PURE__ */ new Map(),
503
+ correlationToParentKeys: /* @__PURE__ */ new Map()
504
+ };
505
+ entry.pipeline.pipe(
506
+ output((data) => {
507
+ const messages = data.getInner();
508
+ syncState.messagesCount += messages.length;
509
+ for (const [[childKey, tupleData], multiplicity] of messages) {
510
+ const [childResult, _orderByIndex, correlationKey, parentContext] = tupleData;
511
+ const routingKey = computeRoutingKey(correlationKey, parentContext);
512
+ let byChild = state.pendingChildChanges.get(routingKey);
513
+ if (!byChild) {
514
+ byChild = /* @__PURE__ */ new Map();
515
+ state.pendingChildChanges.set(routingKey, byChild);
516
+ }
517
+ const existing = byChild.get(childKey) || {
518
+ deletes: 0,
519
+ inserts: 0,
520
+ value: childResult,
521
+ orderByIndex: _orderByIndex
522
+ };
523
+ if (multiplicity < 0) {
524
+ existing.deletes += Math.abs(multiplicity);
525
+ } else if (multiplicity > 0) {
526
+ existing.inserts += multiplicity;
527
+ existing.value = childResult;
528
+ }
529
+ byChild.set(childKey, existing);
530
+ }
531
+ })
532
+ );
533
+ if (entry.childCompilationResult.includes) {
534
+ state.nestedSetups = setupNestedPipelines(
535
+ entry.childCompilationResult.includes,
536
+ syncState
537
+ );
538
+ state.nestedRoutingIndex = /* @__PURE__ */ new Map();
539
+ state.nestedRoutingReverseIndex = /* @__PURE__ */ new Map();
540
+ }
541
+ return state;
542
+ });
543
+ }
464
544
  applyChanges(config, changes, key) {
465
545
  const { write, collection } = config;
466
546
  const { deletes, inserts, value, orderByIndex } = changes;
@@ -588,12 +668,6 @@ class CollectionConfigBuilder {
588
668
  return loadSubsetDataCallbacks;
589
669
  }
590
670
  }
591
- function buildQueryFromConfig(config) {
592
- if (typeof config.query === `function`) {
593
- return buildQuery(config.query);
594
- }
595
- return getQueryIR(config.query);
596
- }
597
671
  function createOrderByComparator(orderByIndices) {
598
672
  return (val1, val2) => {
599
673
  const index1 = orderByIndices.get(val1);
@@ -610,68 +684,421 @@ function createOrderByComparator(orderByIndices) {
610
684
  return 0;
611
685
  };
612
686
  }
613
- function extractCollectionsFromQuery(query) {
614
- const collections = {};
615
- function extractFromSource(source) {
616
- if (source.type === `collectionRef`) {
617
- collections[source.collection.id] = source.collection;
618
- } else if (source.type === `queryRef`) {
619
- extractFromQuery(source.query);
620
- }
621
- }
622
- function extractFromQuery(q) {
623
- if (q.from) {
624
- extractFromSource(q.from);
625
- }
626
- if (q.join && Array.isArray(q.join)) {
627
- for (const joinClause of q.join) {
628
- if (joinClause.from) {
629
- extractFromSource(joinClause.from);
687
+ function materializesInline(state) {
688
+ return state.materialization !== `collection`;
689
+ }
690
+ function materializeIncludedValue(state, entry) {
691
+ if (!entry) {
692
+ if (state.materialization === `array`) {
693
+ return [];
694
+ }
695
+ if (state.materialization === `concat`) {
696
+ return ``;
697
+ }
698
+ return void 0;
699
+ }
700
+ if (state.materialization === `collection`) {
701
+ return entry.collection;
702
+ }
703
+ const rows = [...entry.collection.toArray];
704
+ const values = state.scalarField ? rows.map((row) => row?.[state.scalarField]) : rows;
705
+ if (state.materialization === `array`) {
706
+ return values;
707
+ }
708
+ return values.map((value) => String(value ?? ``)).join(``);
709
+ }
710
+ function setupNestedPipelines(includes, syncState) {
711
+ return includes.map((entry) => {
712
+ const buffer = /* @__PURE__ */ new Map();
713
+ entry.pipeline.pipe(
714
+ output((data) => {
715
+ const messages = data.getInner();
716
+ syncState.messagesCount += messages.length;
717
+ for (const [[childKey, tupleData], multiplicity] of messages) {
718
+ const [childResult, _orderByIndex, correlationKey, parentContext] = tupleData;
719
+ const routingKey = computeRoutingKey(correlationKey, parentContext);
720
+ let byChild = buffer.get(routingKey);
721
+ if (!byChild) {
722
+ byChild = /* @__PURE__ */ new Map();
723
+ buffer.set(routingKey, byChild);
724
+ }
725
+ const existing = byChild.get(childKey) || {
726
+ deletes: 0,
727
+ inserts: 0,
728
+ value: childResult,
729
+ orderByIndex: _orderByIndex
730
+ };
731
+ if (multiplicity < 0) {
732
+ existing.deletes += Math.abs(multiplicity);
733
+ } else if (multiplicity > 0) {
734
+ existing.inserts += multiplicity;
735
+ existing.value = childResult;
736
+ }
737
+ byChild.set(childKey, existing);
738
+ }
739
+ })
740
+ );
741
+ const setup = {
742
+ compilationResult: entry,
743
+ buffer
744
+ };
745
+ if (entry.childCompilationResult.includes) {
746
+ setup.nestedSetups = setupNestedPipelines(
747
+ entry.childCompilationResult.includes,
748
+ syncState
749
+ );
750
+ }
751
+ return setup;
752
+ });
753
+ }
754
+ function createPerEntryIncludesStates(setups) {
755
+ return setups.map((setup) => {
756
+ const state = {
757
+ fieldName: setup.compilationResult.fieldName,
758
+ childCorrelationField: setup.compilationResult.childCorrelationField,
759
+ hasOrderBy: setup.compilationResult.hasOrderBy,
760
+ materialization: setup.compilationResult.materialization,
761
+ scalarField: setup.compilationResult.scalarField,
762
+ childRegistry: /* @__PURE__ */ new Map(),
763
+ pendingChildChanges: /* @__PURE__ */ new Map(),
764
+ correlationToParentKeys: /* @__PURE__ */ new Map()
765
+ };
766
+ if (setup.nestedSetups) {
767
+ state.nestedSetups = setup.nestedSetups;
768
+ state.nestedRoutingIndex = /* @__PURE__ */ new Map();
769
+ state.nestedRoutingReverseIndex = /* @__PURE__ */ new Map();
770
+ }
771
+ return state;
772
+ });
773
+ }
774
+ function drainNestedBuffers(state) {
775
+ const dirtyCorrelationKeys = /* @__PURE__ */ new Set();
776
+ if (!state.nestedSetups) return dirtyCorrelationKeys;
777
+ for (let i = 0; i < state.nestedSetups.length; i++) {
778
+ const setup = state.nestedSetups[i];
779
+ const toDelete = [];
780
+ for (const [nestedCorrelationKey, childChanges] of setup.buffer) {
781
+ const parentCorrelationKey = state.nestedRoutingIndex.get(nestedCorrelationKey);
782
+ if (parentCorrelationKey === void 0) {
783
+ continue;
784
+ }
785
+ const entry = state.childRegistry.get(parentCorrelationKey);
786
+ if (!entry || !entry.includesStates) {
787
+ continue;
788
+ }
789
+ const entryState = entry.includesStates[i];
790
+ for (const [childKey, changes] of childChanges) {
791
+ let byChild = entryState.pendingChildChanges.get(nestedCorrelationKey);
792
+ if (!byChild) {
793
+ byChild = /* @__PURE__ */ new Map();
794
+ entryState.pendingChildChanges.set(nestedCorrelationKey, byChild);
795
+ }
796
+ const existing = byChild.get(childKey);
797
+ if (existing) {
798
+ existing.inserts += changes.inserts;
799
+ existing.deletes += changes.deletes;
800
+ if (changes.inserts > 0) {
801
+ existing.value = changes.value;
802
+ if (changes.orderByIndex !== void 0) {
803
+ existing.orderByIndex = changes.orderByIndex;
804
+ }
805
+ }
806
+ } else {
807
+ byChild.set(childKey, { ...changes });
630
808
  }
631
809
  }
810
+ dirtyCorrelationKeys.add(parentCorrelationKey);
811
+ toDelete.push(nestedCorrelationKey);
812
+ }
813
+ for (const key of toDelete) {
814
+ setup.buffer.delete(key);
632
815
  }
633
816
  }
634
- extractFromQuery(query);
635
- return collections;
636
- }
637
- function extractCollectionFromSource(query) {
638
- const from = query.from;
639
- if (from.type === `collectionRef`) {
640
- return from.collection;
641
- } else if (from.type === `queryRef`) {
642
- return extractCollectionFromSource(from.query);
643
- }
644
- throw new Error(
645
- `Failed to extract collection. Invalid FROM clause: ${JSON.stringify(query)}`
646
- );
817
+ return dirtyCorrelationKeys;
647
818
  }
648
- function extractCollectionAliases(query) {
649
- const aliasesById = /* @__PURE__ */ new Map();
650
- function recordAlias(source) {
651
- if (!source) return;
652
- if (source.type === `collectionRef`) {
653
- const { id } = source.collection;
654
- const existing = aliasesById.get(id);
655
- if (existing) {
656
- existing.add(source.alias);
657
- } else {
658
- aliasesById.set(id, /* @__PURE__ */ new Set([source.alias]));
819
+ function updateRoutingIndex(state, correlationKey, childChanges) {
820
+ if (!state.nestedSetups) return;
821
+ for (const setup of state.nestedSetups) {
822
+ for (const [, change] of childChanges) {
823
+ if (change.inserts > 0) {
824
+ const nestedRouting = change.value[INCLUDES_ROUTING]?.[setup.compilationResult.fieldName];
825
+ const nestedCorrelationKey = nestedRouting?.correlationKey;
826
+ const nestedParentContext = nestedRouting?.parentContext ?? null;
827
+ const nestedRoutingKey = computeRoutingKey(
828
+ nestedCorrelationKey,
829
+ nestedParentContext
830
+ );
831
+ if (nestedCorrelationKey != null) {
832
+ state.nestedRoutingIndex.set(nestedRoutingKey, correlationKey);
833
+ let reverseSet = state.nestedRoutingReverseIndex.get(correlationKey);
834
+ if (!reverseSet) {
835
+ reverseSet = /* @__PURE__ */ new Set();
836
+ state.nestedRoutingReverseIndex.set(correlationKey, reverseSet);
837
+ }
838
+ reverseSet.add(nestedRoutingKey);
839
+ }
840
+ } else if (change.deletes > 0 && change.inserts === 0) {
841
+ const nestedRouting2 = change.value[INCLUDES_ROUTING]?.[setup.compilationResult.fieldName];
842
+ const nestedCorrelationKey = nestedRouting2?.correlationKey;
843
+ const nestedParentContext2 = nestedRouting2?.parentContext ?? null;
844
+ const nestedRoutingKey = computeRoutingKey(
845
+ nestedCorrelationKey,
846
+ nestedParentContext2
847
+ );
848
+ if (nestedCorrelationKey != null) {
849
+ state.nestedRoutingIndex.delete(nestedRoutingKey);
850
+ const reverseSet = state.nestedRoutingReverseIndex.get(correlationKey);
851
+ if (reverseSet) {
852
+ reverseSet.delete(nestedRoutingKey);
853
+ if (reverseSet.size === 0) {
854
+ state.nestedRoutingReverseIndex.delete(correlationKey);
855
+ }
856
+ }
857
+ }
659
858
  }
660
- } else if (source.type === `queryRef`) {
661
- traverse(source.query);
662
859
  }
663
860
  }
664
- function traverse(q) {
665
- if (!q) return;
666
- recordAlias(q.from);
667
- if (q.join) {
668
- for (const joinClause of q.join) {
669
- recordAlias(joinClause.from);
861
+ }
862
+ function cleanRoutingIndexOnDelete(state, correlationKey) {
863
+ if (!state.nestedRoutingReverseIndex) return;
864
+ const nestedKeys = state.nestedRoutingReverseIndex.get(correlationKey);
865
+ if (nestedKeys) {
866
+ for (const nestedKey of nestedKeys) {
867
+ state.nestedRoutingIndex.delete(nestedKey);
868
+ }
869
+ state.nestedRoutingReverseIndex.delete(correlationKey);
870
+ }
871
+ }
872
+ function hasNestedBufferChanges(setups) {
873
+ for (const setup of setups) {
874
+ if (setup.buffer.size > 0) return true;
875
+ if (setup.nestedSetups && hasNestedBufferChanges(setup.nestedSetups))
876
+ return true;
877
+ }
878
+ return false;
879
+ }
880
+ function computeRoutingKey(correlationKey, parentContext) {
881
+ if (parentContext == null) return correlationKey;
882
+ return JSON.stringify([correlationKey, parentContext]);
883
+ }
884
+ function createChildCollectionEntry(parentId, fieldName, correlationKey, hasOrderBy, nestedSetups) {
885
+ const resultKeys = /* @__PURE__ */ new WeakMap();
886
+ const orderByIndices = hasOrderBy ? /* @__PURE__ */ new WeakMap() : null;
887
+ let syncMethods = null;
888
+ const compare = orderByIndices ? createOrderByComparator(orderByIndices) : void 0;
889
+ const collection = createCollection({
890
+ id: `__child-collection:${parentId}-${fieldName}-${serializeValue(correlationKey)}`,
891
+ getKey: (item) => resultKeys.get(item),
892
+ compare,
893
+ sync: {
894
+ rowUpdateMode: `full`,
895
+ sync: (methods) => {
896
+ syncMethods = methods;
897
+ return () => {
898
+ syncMethods = null;
899
+ };
670
900
  }
901
+ },
902
+ startSync: true
903
+ });
904
+ const entry = {
905
+ collection,
906
+ get syncMethods() {
907
+ return syncMethods;
908
+ },
909
+ resultKeys,
910
+ orderByIndices
911
+ };
912
+ if (nestedSetups) {
913
+ entry.includesStates = createPerEntryIncludesStates(nestedSetups);
914
+ }
915
+ return entry;
916
+ }
917
+ function flushIncludesState(includesState, parentCollection, parentId, parentChanges, parentSyncMethods) {
918
+ for (const state of includesState) {
919
+ if (parentChanges) {
920
+ for (const [parentKey, changes] of parentChanges) {
921
+ if (changes.inserts > 0) {
922
+ const parentResult = changes.value;
923
+ const routing = parentResult[INCLUDES_ROUTING]?.[state.fieldName];
924
+ const correlationKey = routing?.correlationKey;
925
+ const parentContext = routing?.parentContext ?? null;
926
+ const routingKey = computeRoutingKey(correlationKey, parentContext);
927
+ if (correlationKey != null) {
928
+ if (!state.childRegistry.has(routingKey)) {
929
+ const entry = createChildCollectionEntry(
930
+ parentId,
931
+ state.fieldName,
932
+ routingKey,
933
+ state.hasOrderBy,
934
+ state.nestedSetups
935
+ );
936
+ state.childRegistry.set(routingKey, entry);
937
+ }
938
+ let parentKeys = state.correlationToParentKeys.get(routingKey);
939
+ if (!parentKeys) {
940
+ parentKeys = /* @__PURE__ */ new Set();
941
+ state.correlationToParentKeys.set(routingKey, parentKeys);
942
+ }
943
+ parentKeys.add(parentKey);
944
+ const childValue = materializeIncludedValue(
945
+ state,
946
+ state.childRegistry.get(routingKey)
947
+ );
948
+ parentResult[state.fieldName] = childValue;
949
+ const storedParent = parentCollection.get(parentKey);
950
+ if (storedParent && storedParent !== parentResult) {
951
+ storedParent[state.fieldName] = childValue;
952
+ }
953
+ }
954
+ }
955
+ }
956
+ }
957
+ const affectedCorrelationKeys = materializesInline(state) ? new Set(state.pendingChildChanges.keys()) : null;
958
+ const entriesWithChildChanges = /* @__PURE__ */ new Map();
959
+ if (state.pendingChildChanges.size > 0) {
960
+ for (const [correlationKey, childChanges] of state.pendingChildChanges) {
961
+ let entry = state.childRegistry.get(correlationKey);
962
+ if (!entry) {
963
+ entry = createChildCollectionEntry(
964
+ parentId,
965
+ state.fieldName,
966
+ correlationKey,
967
+ state.hasOrderBy,
968
+ state.nestedSetups
969
+ );
970
+ state.childRegistry.set(correlationKey, entry);
971
+ }
972
+ if (state.materialization === `collection`) {
973
+ attachChildCollectionToParent(
974
+ parentCollection,
975
+ state.fieldName,
976
+ correlationKey,
977
+ state.correlationToParentKeys,
978
+ entry.collection
979
+ );
980
+ }
981
+ if (entry.syncMethods) {
982
+ entry.syncMethods.begin();
983
+ for (const [childKey, change] of childChanges) {
984
+ entry.resultKeys.set(change.value, childKey);
985
+ if (entry.orderByIndices && change.orderByIndex !== void 0) {
986
+ entry.orderByIndices.set(change.value, change.orderByIndex);
987
+ }
988
+ if (change.inserts > 0 && change.deletes === 0) {
989
+ entry.syncMethods.write({ value: change.value, type: `insert` });
990
+ } else if (change.inserts > change.deletes || change.inserts === change.deletes && entry.syncMethods.collection.has(
991
+ entry.syncMethods.collection.getKeyFromItem(change.value)
992
+ )) {
993
+ entry.syncMethods.write({ value: change.value, type: `update` });
994
+ } else if (change.deletes > 0) {
995
+ entry.syncMethods.write({ value: change.value, type: `delete` });
996
+ }
997
+ }
998
+ entry.syncMethods.commit();
999
+ }
1000
+ updateRoutingIndex(state, correlationKey, childChanges);
1001
+ entriesWithChildChanges.set(correlationKey, { entry, childChanges });
1002
+ }
1003
+ state.pendingChildChanges.clear();
1004
+ }
1005
+ const dirtyFromBuffers = drainNestedBuffers(state);
1006
+ for (const [, { entry, childChanges }] of entriesWithChildChanges) {
1007
+ if (entry.includesStates) {
1008
+ flushIncludesState(
1009
+ entry.includesStates,
1010
+ entry.collection,
1011
+ entry.collection.id,
1012
+ childChanges,
1013
+ entry.syncMethods
1014
+ );
1015
+ }
1016
+ }
1017
+ for (const correlationKey of dirtyFromBuffers) {
1018
+ if (entriesWithChildChanges.has(correlationKey)) continue;
1019
+ const entry = state.childRegistry.get(correlationKey);
1020
+ if (entry?.includesStates) {
1021
+ flushIncludesState(
1022
+ entry.includesStates,
1023
+ entry.collection,
1024
+ entry.collection.id,
1025
+ null,
1026
+ entry.syncMethods
1027
+ );
1028
+ }
1029
+ }
1030
+ const inlineReEmitKeys = materializesInline(state) ? /* @__PURE__ */ new Set([...affectedCorrelationKeys || [], ...dirtyFromBuffers]) : null;
1031
+ if (parentSyncMethods && inlineReEmitKeys && inlineReEmitKeys.size > 0) {
1032
+ const events = [];
1033
+ for (const correlationKey of inlineReEmitKeys) {
1034
+ const parentKeys = state.correlationToParentKeys.get(correlationKey);
1035
+ if (!parentKeys) continue;
1036
+ const entry = state.childRegistry.get(correlationKey);
1037
+ for (const parentKey of parentKeys) {
1038
+ const item = parentCollection.get(parentKey);
1039
+ if (item) {
1040
+ const key = parentSyncMethods.collection.getKeyFromItem(item);
1041
+ const previousValue = { ...item };
1042
+ item[state.fieldName] = materializeIncludedValue(state, entry);
1043
+ events.push({
1044
+ type: `update`,
1045
+ key,
1046
+ value: item,
1047
+ previousValue
1048
+ });
1049
+ }
1050
+ }
1051
+ }
1052
+ if (events.length > 0) {
1053
+ const changesManager = parentCollection._changes;
1054
+ changesManager.emitEvents(events, true);
1055
+ }
1056
+ }
1057
+ if (parentChanges) {
1058
+ for (const [parentKey, changes] of parentChanges) {
1059
+ if (changes.deletes > 0 && changes.inserts === 0) {
1060
+ const routing = changes.value[INCLUDES_ROUTING]?.[state.fieldName];
1061
+ const correlationKey = routing?.correlationKey;
1062
+ const parentContext = routing?.parentContext ?? null;
1063
+ const routingKey = computeRoutingKey(correlationKey, parentContext);
1064
+ if (correlationKey != null) {
1065
+ const parentKeys = state.correlationToParentKeys.get(routingKey);
1066
+ if (parentKeys) {
1067
+ parentKeys.delete(parentKey);
1068
+ if (parentKeys.size === 0) {
1069
+ cleanRoutingIndexOnDelete(state, routingKey);
1070
+ state.childRegistry.delete(routingKey);
1071
+ state.correlationToParentKeys.delete(routingKey);
1072
+ }
1073
+ }
1074
+ }
1075
+ }
1076
+ }
1077
+ }
1078
+ }
1079
+ if (parentChanges) {
1080
+ for (const [, changes] of parentChanges) {
1081
+ delete changes.value[INCLUDES_ROUTING];
1082
+ }
1083
+ }
1084
+ }
1085
+ function hasPendingIncludesChanges(states) {
1086
+ for (const state of states) {
1087
+ if (state.pendingChildChanges.size > 0) return true;
1088
+ if (state.nestedSetups && hasNestedBufferChanges(state.nestedSetups))
1089
+ return true;
1090
+ }
1091
+ return false;
1092
+ }
1093
+ function attachChildCollectionToParent(parentCollection, fieldName, correlationKey, correlationToParentKeys, childCollection) {
1094
+ const parentKeys = correlationToParentKeys.get(correlationKey);
1095
+ if (!parentKeys) return;
1096
+ for (const parentKey of parentKeys) {
1097
+ const item = parentCollection.get(parentKey);
1098
+ if (item) {
1099
+ item[fieldName] = childCollection;
671
1100
  }
672
1101
  }
673
- traverse(query);
674
- return aliasesById;
675
1102
  }
676
1103
  function accumulateChanges(acc, [[key, tupleData], multiplicity]) {
677
1104
  const [value, orderByIndex] = tupleData;