@tanstack/db 0.5.33 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. package/dist/cjs/collection/change-events.cjs.map +1 -1
  2. package/dist/cjs/collection/change-events.d.cts +3 -2
  3. package/dist/cjs/collection/changes.cjs +13 -4
  4. package/dist/cjs/collection/changes.cjs.map +1 -1
  5. package/dist/cjs/collection/changes.d.cts +10 -1
  6. package/dist/cjs/collection/cleanup-queue.cjs +89 -0
  7. package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
  8. package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
  9. package/dist/cjs/collection/events.cjs +14 -0
  10. package/dist/cjs/collection/events.cjs.map +1 -1
  11. package/dist/cjs/collection/events.d.cts +39 -1
  12. package/dist/cjs/collection/index.cjs +66 -28
  13. package/dist/cjs/collection/index.cjs.map +1 -1
  14. package/dist/cjs/collection/index.d.cts +49 -36
  15. package/dist/cjs/collection/indexes.cjs +211 -62
  16. package/dist/cjs/collection/indexes.cjs.map +1 -1
  17. package/dist/cjs/collection/indexes.d.cts +27 -17
  18. package/dist/cjs/collection/lifecycle.cjs +5 -22
  19. package/dist/cjs/collection/lifecycle.cjs.map +1 -1
  20. package/dist/cjs/collection/lifecycle.d.cts +0 -1
  21. package/dist/cjs/collection/mutations.cjs +18 -0
  22. package/dist/cjs/collection/mutations.cjs.map +1 -1
  23. package/dist/cjs/collection/mutations.d.cts +1 -0
  24. package/dist/cjs/collection/state.cjs +381 -53
  25. package/dist/cjs/collection/state.cjs.map +1 -1
  26. package/dist/cjs/collection/state.d.cts +65 -1
  27. package/dist/cjs/collection/subscription.cjs +6 -0
  28. package/dist/cjs/collection/subscription.cjs.map +1 -1
  29. package/dist/cjs/collection/subscription.d.cts +4 -0
  30. package/dist/cjs/collection/sync.cjs +108 -1
  31. package/dist/cjs/collection/sync.cjs.map +1 -1
  32. package/dist/cjs/collection/sync.d.cts +2 -0
  33. package/dist/cjs/collection/transaction-metadata.cjs +5 -0
  34. package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
  35. package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
  36. package/dist/cjs/errors.cjs +8 -0
  37. package/dist/cjs/errors.cjs.map +1 -1
  38. package/dist/cjs/errors.d.cts +3 -0
  39. package/dist/cjs/index.cjs +22 -4
  40. package/dist/cjs/index.cjs.map +1 -1
  41. package/dist/cjs/index.d.cts +11 -3
  42. package/dist/cjs/indexes/auto-index.cjs +13 -6
  43. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  44. package/dist/cjs/indexes/base-index.cjs +0 -3
  45. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  46. package/dist/cjs/indexes/base-index.d.cts +2 -6
  47. package/dist/cjs/indexes/basic-index.cjs +361 -0
  48. package/dist/cjs/indexes/basic-index.cjs.map +1 -0
  49. package/dist/cjs/indexes/basic-index.d.cts +102 -0
  50. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  51. package/dist/cjs/indexes/btree-index.d.cts +1 -1
  52. package/dist/cjs/indexes/index-options.d.cts +8 -9
  53. package/dist/cjs/indexes/index-registry.cjs +89 -0
  54. package/dist/cjs/indexes/index-registry.cjs.map +1 -0
  55. package/dist/cjs/indexes/index-registry.d.cts +61 -0
  56. package/dist/cjs/local-only.cjs +5 -0
  57. package/dist/cjs/local-only.cjs.map +1 -1
  58. package/dist/cjs/query/builder/functions.cjs +27 -11
  59. package/dist/cjs/query/builder/functions.cjs.map +1 -1
  60. package/dist/cjs/query/builder/functions.d.cts +25 -3
  61. package/dist/cjs/query/builder/index.cjs +200 -39
  62. package/dist/cjs/query/builder/index.cjs.map +1 -1
  63. package/dist/cjs/query/builder/index.d.cts +4 -3
  64. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
  65. package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
  66. package/dist/cjs/query/builder/types.d.cts +84 -19
  67. package/dist/cjs/query/compiler/evaluators.cjs +51 -0
  68. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
  69. package/dist/cjs/query/compiler/group-by.cjs +100 -28
  70. package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
  71. package/dist/cjs/query/compiler/group-by.d.cts +4 -2
  72. package/dist/cjs/query/compiler/index.cjs +283 -11
  73. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  74. package/dist/cjs/query/compiler/index.d.cts +30 -2
  75. package/dist/cjs/query/compiler/order-by.cjs +29 -10
  76. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  77. package/dist/cjs/query/compiler/order-by.d.cts +1 -1
  78. package/dist/cjs/query/compiler/select.cjs +8 -0
  79. package/dist/cjs/query/compiler/select.cjs.map +1 -1
  80. package/dist/cjs/query/index.d.cts +2 -1
  81. package/dist/cjs/query/ir.cjs +18 -1
  82. package/dist/cjs/query/ir.cjs.map +1 -1
  83. package/dist/cjs/query/ir.d.cts +21 -1
  84. package/dist/cjs/query/live/collection-config-builder.cjs +501 -5
  85. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  86. package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
  87. package/dist/cjs/query/live/types.d.cts +3 -3
  88. package/dist/cjs/query/live/utils.cjs +43 -3
  89. package/dist/cjs/query/live/utils.cjs.map +1 -1
  90. package/dist/cjs/query/live/utils.d.cts +1 -0
  91. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  92. package/dist/cjs/query/live-query-collection.d.cts +9 -6
  93. package/dist/cjs/query/query-once.cjs.map +1 -1
  94. package/dist/cjs/query/query-once.d.cts +7 -5
  95. package/dist/cjs/query/subset-dedupe.cjs +9 -3
  96. package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
  97. package/dist/cjs/types.d.cts +42 -8
  98. package/dist/cjs/utils/array-utils.cjs +27 -0
  99. package/dist/cjs/utils/array-utils.cjs.map +1 -0
  100. package/dist/cjs/utils/array-utils.d.cts +16 -0
  101. package/dist/cjs/utils/comparison.cjs +11 -0
  102. package/dist/cjs/utils/comparison.cjs.map +1 -1
  103. package/dist/cjs/utils/index-optimization.cjs +4 -0
  104. package/dist/cjs/utils/index-optimization.cjs.map +1 -1
  105. package/dist/cjs/utils.cjs +7 -9
  106. package/dist/cjs/utils.cjs.map +1 -1
  107. package/dist/cjs/utils.d.cts +6 -1
  108. package/dist/cjs/virtual-props.cjs +33 -0
  109. package/dist/cjs/virtual-props.cjs.map +1 -0
  110. package/dist/cjs/virtual-props.d.cts +196 -0
  111. package/dist/esm/collection/change-events.d.ts +3 -2
  112. package/dist/esm/collection/change-events.js.map +1 -1
  113. package/dist/esm/collection/changes.d.ts +10 -1
  114. package/dist/esm/collection/changes.js +13 -4
  115. package/dist/esm/collection/changes.js.map +1 -1
  116. package/dist/esm/collection/cleanup-queue.d.ts +30 -0
  117. package/dist/esm/collection/cleanup-queue.js +89 -0
  118. package/dist/esm/collection/cleanup-queue.js.map +1 -0
  119. package/dist/esm/collection/events.d.ts +39 -1
  120. package/dist/esm/collection/events.js +14 -0
  121. package/dist/esm/collection/events.js.map +1 -1
  122. package/dist/esm/collection/index.d.ts +49 -36
  123. package/dist/esm/collection/index.js +67 -29
  124. package/dist/esm/collection/index.js.map +1 -1
  125. package/dist/esm/collection/indexes.d.ts +27 -17
  126. package/dist/esm/collection/indexes.js +211 -62
  127. package/dist/esm/collection/indexes.js.map +1 -1
  128. package/dist/esm/collection/lifecycle.d.ts +0 -1
  129. package/dist/esm/collection/lifecycle.js +5 -22
  130. package/dist/esm/collection/lifecycle.js.map +1 -1
  131. package/dist/esm/collection/mutations.d.ts +1 -0
  132. package/dist/esm/collection/mutations.js +18 -0
  133. package/dist/esm/collection/mutations.js.map +1 -1
  134. package/dist/esm/collection/state.d.ts +65 -1
  135. package/dist/esm/collection/state.js +381 -53
  136. package/dist/esm/collection/state.js.map +1 -1
  137. package/dist/esm/collection/subscription.d.ts +4 -0
  138. package/dist/esm/collection/subscription.js +6 -0
  139. package/dist/esm/collection/subscription.js.map +1 -1
  140. package/dist/esm/collection/sync.d.ts +2 -0
  141. package/dist/esm/collection/sync.js +108 -1
  142. package/dist/esm/collection/sync.js.map +1 -1
  143. package/dist/esm/collection/transaction-metadata.d.ts +1 -0
  144. package/dist/esm/collection/transaction-metadata.js +5 -0
  145. package/dist/esm/collection/transaction-metadata.js.map +1 -0
  146. package/dist/esm/errors.d.ts +3 -0
  147. package/dist/esm/errors.js +8 -0
  148. package/dist/esm/errors.js.map +1 -1
  149. package/dist/esm/index.d.ts +11 -3
  150. package/dist/esm/index.js +25 -7
  151. package/dist/esm/index.js.map +1 -1
  152. package/dist/esm/indexes/auto-index.js +13 -6
  153. package/dist/esm/indexes/auto-index.js.map +1 -1
  154. package/dist/esm/indexes/base-index.d.ts +2 -6
  155. package/dist/esm/indexes/base-index.js +1 -4
  156. package/dist/esm/indexes/base-index.js.map +1 -1
  157. package/dist/esm/indexes/basic-index.d.ts +102 -0
  158. package/dist/esm/indexes/basic-index.js +361 -0
  159. package/dist/esm/indexes/basic-index.js.map +1 -0
  160. package/dist/esm/indexes/btree-index.d.ts +1 -1
  161. package/dist/esm/indexes/btree-index.js.map +1 -1
  162. package/dist/esm/indexes/index-options.d.ts +8 -9
  163. package/dist/esm/indexes/index-registry.d.ts +61 -0
  164. package/dist/esm/indexes/index-registry.js +89 -0
  165. package/dist/esm/indexes/index-registry.js.map +1 -0
  166. package/dist/esm/local-only.js +5 -0
  167. package/dist/esm/local-only.js.map +1 -1
  168. package/dist/esm/query/builder/functions.d.ts +25 -3
  169. package/dist/esm/query/builder/functions.js +27 -11
  170. package/dist/esm/query/builder/functions.js.map +1 -1
  171. package/dist/esm/query/builder/index.d.ts +4 -3
  172. package/dist/esm/query/builder/index.js +201 -40
  173. package/dist/esm/query/builder/index.js.map +1 -1
  174. package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
  175. package/dist/esm/query/builder/ref-proxy.js.map +1 -1
  176. package/dist/esm/query/builder/types.d.ts +84 -19
  177. package/dist/esm/query/compiler/evaluators.js +51 -0
  178. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  179. package/dist/esm/query/compiler/group-by.d.ts +4 -2
  180. package/dist/esm/query/compiler/group-by.js +101 -29
  181. package/dist/esm/query/compiler/group-by.js.map +1 -1
  182. package/dist/esm/query/compiler/index.d.ts +30 -2
  183. package/dist/esm/query/compiler/index.js +285 -13
  184. package/dist/esm/query/compiler/index.js.map +1 -1
  185. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  186. package/dist/esm/query/compiler/order-by.js +30 -11
  187. package/dist/esm/query/compiler/order-by.js.map +1 -1
  188. package/dist/esm/query/compiler/select.js +8 -0
  189. package/dist/esm/query/compiler/select.js.map +1 -1
  190. package/dist/esm/query/index.d.ts +2 -1
  191. package/dist/esm/query/ir.d.ts +21 -1
  192. package/dist/esm/query/ir.js +18 -1
  193. package/dist/esm/query/ir.js.map +1 -1
  194. package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
  195. package/dist/esm/query/live/collection-config-builder.js +503 -7
  196. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  197. package/dist/esm/query/live/types.d.ts +3 -3
  198. package/dist/esm/query/live/utils.d.ts +1 -0
  199. package/dist/esm/query/live/utils.js +43 -3
  200. package/dist/esm/query/live/utils.js.map +1 -1
  201. package/dist/esm/query/live-query-collection.d.ts +9 -6
  202. package/dist/esm/query/live-query-collection.js.map +1 -1
  203. package/dist/esm/query/query-once.d.ts +7 -5
  204. package/dist/esm/query/query-once.js.map +1 -1
  205. package/dist/esm/query/subset-dedupe.js +9 -3
  206. package/dist/esm/query/subset-dedupe.js.map +1 -1
  207. package/dist/esm/types.d.ts +42 -8
  208. package/dist/esm/utils/array-utils.d.ts +16 -0
  209. package/dist/esm/utils/array-utils.js +27 -0
  210. package/dist/esm/utils/array-utils.js.map +1 -0
  211. package/dist/esm/utils/comparison.js +11 -0
  212. package/dist/esm/utils/comparison.js.map +1 -1
  213. package/dist/esm/utils/index-optimization.js +4 -0
  214. package/dist/esm/utils/index-optimization.js.map +1 -1
  215. package/dist/esm/utils.d.ts +6 -1
  216. package/dist/esm/utils.js +7 -9
  217. package/dist/esm/utils.js.map +1 -1
  218. package/dist/esm/virtual-props.d.ts +196 -0
  219. package/dist/esm/virtual-props.js +33 -0
  220. package/dist/esm/virtual-props.js.map +1 -0
  221. package/package.json +4 -3
  222. package/skills/db-core/SKILL.md +4 -2
  223. package/skills/db-core/collection-setup/SKILL.md +30 -11
  224. package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
  225. package/skills/db-core/collection-setup/references/powersync-adapter.md +4 -0
  226. package/skills/db-core/collection-setup/references/query-adapter.md +32 -0
  227. package/skills/db-core/custom-adapter/SKILL.md +58 -9
  228. package/skills/db-core/live-queries/SKILL.md +162 -2
  229. package/skills/db-core/mutations-optimistic/SKILL.md +1 -1
  230. package/skills/db-core/persistence/SKILL.md +241 -0
  231. package/skills/meta-framework/SKILL.md +1 -1
  232. package/src/collection/change-events.ts +13 -9
  233. package/src/collection/changes.ts +30 -7
  234. package/src/collection/cleanup-queue.ts +105 -0
  235. package/src/collection/events.ts +65 -0
  236. package/src/collection/index.ts +110 -45
  237. package/src/collection/indexes.ts +283 -76
  238. package/src/collection/lifecycle.ts +5 -26
  239. package/src/collection/mutations.ts +21 -0
  240. package/src/collection/state.ts +545 -71
  241. package/src/collection/subscription.ts +7 -0
  242. package/src/collection/sync.ts +137 -0
  243. package/src/collection/transaction-metadata.ts +1 -0
  244. package/src/errors.ts +9 -0
  245. package/src/index.ts +46 -3
  246. package/src/indexes/auto-index.ts +18 -8
  247. package/src/indexes/base-index.ts +2 -10
  248. package/src/indexes/basic-index.ts +507 -0
  249. package/src/indexes/btree-index.ts +1 -1
  250. package/src/indexes/index-options.ts +17 -37
  251. package/src/indexes/index-registry.ts +174 -0
  252. package/src/local-only.ts +7 -0
  253. package/src/query/builder/functions.ts +84 -7
  254. package/src/query/builder/index.ts +329 -9
  255. package/src/query/builder/ref-proxy.ts +22 -4
  256. package/src/query/builder/types.ts +257 -62
  257. package/src/query/compiler/evaluators.ts +57 -0
  258. package/src/query/compiler/group-by.ts +156 -35
  259. package/src/query/compiler/index.ts +445 -15
  260. package/src/query/compiler/order-by.ts +51 -12
  261. package/src/query/compiler/select.ts +9 -0
  262. package/src/query/index.ts +7 -0
  263. package/src/query/ir.ts +23 -2
  264. package/src/query/live/collection-config-builder.ts +809 -9
  265. package/src/query/live/types.ts +10 -4
  266. package/src/query/live/utils.ts +64 -3
  267. package/src/query/live-query-collection.ts +43 -18
  268. package/src/query/query-once.ts +31 -12
  269. package/src/query/subset-dedupe.ts +11 -7
  270. package/src/types.ts +49 -9
  271. package/src/utils/array-utils.ts +49 -0
  272. package/src/utils/comparison.ts +14 -0
  273. package/src/utils/index-optimization.ts +4 -0
  274. package/src/utils.ts +12 -9
  275. package/src/virtual-props.ts +282 -0
  276. package/dist/cjs/indexes/lazy-index.cjs +0 -190
  277. package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
  278. package/dist/cjs/indexes/lazy-index.d.cts +0 -96
  279. package/dist/esm/indexes/lazy-index.d.ts +0 -96
  280. package/dist/esm/indexes/lazy-index.js +0 -190
  281. package/dist/esm/indexes/lazy-index.js.map +0 -1
  282. package/src/indexes/lazy-index.ts +0 -251
@@ -1 +1 @@
1
- {"version":3,"file":"ir.cjs","sources":["../../../src/query/ir.ts"],"sourcesContent":["/*\nThis is the intermediate representation of the query.\n*/\n\nimport type { CompareOptions } from './builder/types'\nimport type { Collection, CollectionImpl } from '../collection/index.js'\nimport type { NamespacedRow } from '../types'\n\nexport interface QueryIR {\n from: From\n select?: Select\n join?: Join\n where?: Array<Where>\n groupBy?: GroupBy\n having?: Array<Having>\n orderBy?: OrderBy\n limit?: Limit\n offset?: Offset\n distinct?: true\n singleResult?: true\n\n // Functional variants\n fnSelect?: (row: NamespacedRow) => any\n fnWhere?: Array<(row: NamespacedRow) => any>\n fnHaving?: Array<(row: NamespacedRow) => any>\n}\n\nexport type From = CollectionRef | QueryRef\n\nexport type Select = {\n [alias: string]: BasicExpression | Aggregate | Select\n}\n\nexport type Join = Array<JoinClause>\n\nexport interface JoinClause {\n from: CollectionRef | QueryRef\n type: `left` | `right` | `inner` | `outer` | `full` | `cross`\n left: BasicExpression\n right: BasicExpression\n}\n\nexport type Where =\n | BasicExpression<boolean>\n | { expression: BasicExpression<boolean>; residual?: boolean }\n\nexport type GroupBy = Array<BasicExpression>\n\nexport type Having = Where\n\nexport type OrderBy = Array<OrderByClause>\n\nexport type OrderByClause = {\n expression: BasicExpression\n compareOptions: CompareOptions\n}\n\nexport type OrderByDirection = `asc` | `desc`\n\nexport type Limit = number\n\nexport type Offset = number\n\n/* Expressions */\n\nabstract class BaseExpression<T = any> {\n public abstract type: string\n /** @internal - Type brand for TypeScript inference */\n declare readonly __returnType: T\n}\n\nexport class CollectionRef extends BaseExpression {\n public type = `collectionRef` as const\n constructor(\n public collection: CollectionImpl,\n public alias: string,\n ) {\n super()\n }\n}\n\nexport class QueryRef extends BaseExpression {\n public type = `queryRef` as const\n constructor(\n public query: QueryIR,\n public alias: string,\n ) {\n super()\n }\n}\n\nexport class PropRef<T = any> extends BaseExpression<T> {\n public type = `ref` as const\n constructor(\n public path: Array<string>, // path to the property in the collection, with the alias as the first element\n ) {\n super()\n }\n}\n\nexport class Value<T = any> extends BaseExpression<T> {\n public type = `val` as const\n constructor(\n public value: T, // any js value\n ) {\n super()\n }\n}\n\nexport class Func<T = any> extends BaseExpression<T> {\n public type = `func` as const\n constructor(\n public name: string, // such as eq, gt, lt, upper, lower, etc.\n public args: Array<BasicExpression>,\n ) {\n super()\n }\n}\n\n// This is the basic expression type that is used in the majority of expression\n// builder callbacks (select, where, groupBy, having, orderBy, etc.)\n// it doesn't include aggregate functions as those are only used in the select clause\nexport type BasicExpression<T = any> = PropRef<T> | Value<T> | Func<T>\n\nexport class Aggregate<T = any> extends BaseExpression<T> {\n public type = `agg` as const\n constructor(\n public name: string, // such as count, avg, sum, min, max, etc.\n public args: Array<BasicExpression>,\n ) {\n super()\n }\n}\n\n/**\n * Runtime helper to detect IR expression-like objects.\n * Prefer this over ad-hoc local implementations to keep behavior consistent.\n */\nexport function isExpressionLike(value: any): boolean {\n return (\n value instanceof Aggregate ||\n value instanceof Func ||\n value instanceof PropRef ||\n value instanceof Value\n )\n}\n\n/**\n * Helper functions for working with Where clauses\n */\n\n/**\n * Extract the expression from a Where clause\n */\nexport function getWhereExpression(where: Where): BasicExpression<boolean> {\n return typeof where === `object` && `expression` in where\n ? where.expression\n : where\n}\n\n/**\n * Extract the expression from a HAVING clause\n * HAVING clauses can contain aggregates, unlike regular WHERE clauses\n */\nexport function getHavingExpression(\n having: Having,\n): BasicExpression | Aggregate {\n return typeof having === `object` && `expression` in having\n ? having.expression\n : having\n}\n\n/**\n * Check if a Where clause is marked as residual\n */\nexport function isResidualWhere(where: Where): boolean {\n return (\n typeof where === `object` &&\n `expression` in where &&\n where.residual === true\n )\n}\n\n/**\n * Create a residual Where clause from an expression\n */\nexport function createResidualWhere(\n expression: BasicExpression<boolean>,\n): Where {\n return { expression, residual: true }\n}\n\nfunction getRefFromAlias(\n query: QueryIR,\n alias: string,\n): CollectionRef | QueryRef | void {\n if (query.from.alias === alias) {\n return query.from\n }\n\n for (const join of query.join || []) {\n if (join.from.alias === alias) {\n return join.from\n }\n }\n}\n\n/**\n * Follows the given reference in a query\n * until its finds the root field the reference points to.\n * @returns The collection, its alias, and the path to the root field in this collection\n */\nexport function followRef(\n query: QueryIR,\n ref: PropRef<any>,\n collection: Collection,\n): { collection: Collection; path: Array<string> } | void {\n if (ref.path.length === 0) {\n return\n }\n\n if (ref.path.length === 1) {\n // This field should be part of this collection\n const field = ref.path[0]!\n // is it part of the select clause?\n if (query.select) {\n const selectedField = query.select[field]\n if (selectedField && selectedField.type === `ref`) {\n return followRef(query, selectedField, collection)\n }\n }\n\n // Either this field is not part of the select clause\n // and thus it must be part of the collection itself\n // or it is part of the select but is not a reference\n // so we can stop here and don't have to follow it\n return { collection, path: [field] }\n }\n\n if (ref.path.length > 1) {\n // This is a nested field\n const [alias, ...rest] = ref.path\n const aliasRef = getRefFromAlias(query, alias!)\n if (!aliasRef) {\n return\n }\n\n if (aliasRef.type === `queryRef`) {\n return followRef(aliasRef.query, new PropRef(rest), collection)\n } else {\n // This is a reference to a collection\n // we can't follow it further\n // so the field must be on the collection itself\n return { collection: aliasRef.collection, path: rest }\n }\n }\n}\n"],"names":[],"mappings":";;AAiEA,MAAe,eAAwB;AAIvC;AAEO,MAAM,sBAAsB,eAAe;AAAA,EAEhD,YACS,YACA,OACP;AACA,UAAA;AAHO,SAAA,aAAA;AACA,SAAA,QAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAEO,MAAM,iBAAiB,eAAe;AAAA,EAE3C,YACS,OACA,OACP;AACA,UAAA;AAHO,SAAA,QAAA;AACA,SAAA,QAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAEO,MAAM,gBAAyB,eAAkB;AAAA,EAEtD,YACS,MACP;AACA,UAAA;AAFO,SAAA,OAAA;AAFT,SAAO,OAAO;AAAA,EAKd;AACF;AAEO,MAAM,cAAuB,eAAkB;AAAA,EAEpD,YACS,OACP;AACA,UAAA;AAFO,SAAA,QAAA;AAFT,SAAO,OAAO;AAAA,EAKd;AACF;AAEO,MAAM,aAAsB,eAAkB;AAAA,EAEnD,YACS,MACA,MACP;AACA,UAAA;AAHO,SAAA,OAAA;AACA,SAAA,OAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAOO,MAAM,kBAA2B,eAAkB;AAAA,EAExD,YACS,MACA,MACP;AACA,UAAA;AAHO,SAAA,OAAA;AACA,SAAA,OAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAMO,SAAS,iBAAiB,OAAqB;AACpD,SACE,iBAAiB,aACjB,iBAAiB,QACjB,iBAAiB,WACjB,iBAAiB;AAErB;AASO,SAAS,mBAAmB,OAAwC;AACzE,SAAO,OAAO,UAAU,YAAY,gBAAgB,QAChD,MAAM,aACN;AACN;AAMO,SAAS,oBACd,QAC6B;AAC7B,SAAO,OAAO,WAAW,YAAY,gBAAgB,SACjD,OAAO,aACP;AACN;AAKO,SAAS,gBAAgB,OAAuB;AACrD,SACE,OAAO,UAAU,YACjB,gBAAgB,SAChB,MAAM,aAAa;AAEvB;AAKO,SAAS,oBACd,YACO;AACP,SAAO,EAAE,YAAY,UAAU,KAAA;AACjC;AAEA,SAAS,gBACP,OACA,OACiC;AACjC,MAAI,MAAM,KAAK,UAAU,OAAO;AAC9B,WAAO,MAAM;AAAA,EACf;AAEA,aAAW,QAAQ,MAAM,QAAQ,CAAA,GAAI;AACnC,QAAI,KAAK,KAAK,UAAU,OAAO;AAC7B,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AACF;AAOO,SAAS,UACd,OACA,KACA,YACwD;AACxD,MAAI,IAAI,KAAK,WAAW,GAAG;AACzB;AAAA,EACF;AAEA,MAAI,IAAI,KAAK,WAAW,GAAG;AAEzB,UAAM,QAAQ,IAAI,KAAK,CAAC;AAExB,QAAI,MAAM,QAAQ;AAChB,YAAM,gBAAgB,MAAM,OAAO,KAAK;AACxC,UAAI,iBAAiB,cAAc,SAAS,OAAO;AACjD,eAAO,UAAU,OAAO,eAAe,UAAU;AAAA,MACnD;AAAA,IACF;AAMA,WAAO,EAAE,YAAY,MAAM,CAAC,KAAK,EAAA;AAAA,EACnC;AAEA,MAAI,IAAI,KAAK,SAAS,GAAG;AAEvB,UAAM,CAAC,OAAO,GAAG,IAAI,IAAI,IAAI;AAC7B,UAAM,WAAW,gBAAgB,OAAO,KAAM;AAC9C,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,YAAY;AAChC,aAAO,UAAU,SAAS,OAAO,IAAI,QAAQ,IAAI,GAAG,UAAU;AAAA,IAChE,OAAO;AAIL,aAAO,EAAE,YAAY,SAAS,YAAY,MAAM,KAAA;AAAA,IAClD;AAAA,EACF;AACF;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"ir.cjs","sources":["../../../src/query/ir.ts"],"sourcesContent":["/*\nThis is the intermediate representation of the query.\n*/\n\nimport type { CompareOptions } from './builder/types'\nimport type { Collection, CollectionImpl } from '../collection/index.js'\nimport type { NamespacedRow } from '../types'\n\nexport interface QueryIR {\n from: From\n select?: Select\n join?: Join\n where?: Array<Where>\n groupBy?: GroupBy\n having?: Array<Having>\n orderBy?: OrderBy\n limit?: Limit\n offset?: Offset\n distinct?: true\n singleResult?: true\n\n // Functional variants\n fnSelect?: (row: NamespacedRow) => any\n fnWhere?: Array<(row: NamespacedRow) => any>\n fnHaving?: Array<(row: NamespacedRow) => any>\n}\n\nexport type IncludesMaterialization = `collection` | `array` | `concat`\n\nexport const INCLUDES_SCALAR_FIELD = `__includes_scalar__`\n\nexport type From = CollectionRef | QueryRef\n\nexport type Select = {\n [alias: string]: BasicExpression | Aggregate | Select | IncludesSubquery\n}\n\nexport type Join = Array<JoinClause>\n\nexport interface JoinClause {\n from: CollectionRef | QueryRef\n type: `left` | `right` | `inner` | `outer` | `full` | `cross`\n left: BasicExpression\n right: BasicExpression\n}\n\nexport type Where =\n | BasicExpression<boolean>\n | { expression: BasicExpression<boolean>; residual?: boolean }\n\nexport type GroupBy = Array<BasicExpression>\n\nexport type Having = Where\n\nexport type OrderBy = Array<OrderByClause>\n\nexport type OrderByClause = {\n expression: BasicExpression\n compareOptions: CompareOptions\n}\n\nexport type OrderByDirection = `asc` | `desc`\n\nexport type Limit = number\n\nexport type Offset = number\n\n/* Expressions */\n\nabstract class BaseExpression<T = any> {\n public abstract type: string\n /** @internal - Type brand for TypeScript inference */\n declare readonly __returnType: T\n}\n\nexport class CollectionRef extends BaseExpression {\n public type = `collectionRef` as const\n constructor(\n public collection: CollectionImpl,\n public alias: string,\n ) {\n super()\n }\n}\n\nexport class QueryRef extends BaseExpression {\n public type = `queryRef` as const\n constructor(\n public query: QueryIR,\n public alias: string,\n ) {\n super()\n }\n}\n\nexport class PropRef<T = any> extends BaseExpression<T> {\n public type = `ref` as const\n constructor(\n public path: Array<string>, // path to the property in the collection, with the alias as the first element\n ) {\n super()\n }\n}\n\nexport class Value<T = any> extends BaseExpression<T> {\n public type = `val` as const\n constructor(\n public value: T, // any js value\n ) {\n super()\n }\n}\n\nexport class Func<T = any> extends BaseExpression<T> {\n public type = `func` as const\n constructor(\n public name: string, // such as eq, gt, lt, upper, lower, etc.\n public args: Array<BasicExpression>,\n ) {\n super()\n }\n}\n\n// This is the basic expression type that is used in the majority of expression\n// builder callbacks (select, where, groupBy, having, orderBy, etc.)\n// it doesn't include aggregate functions as those are only used in the select clause\nexport type BasicExpression<T = any> = PropRef<T> | Value<T> | Func<T>\n\nexport class Aggregate<T = any> extends BaseExpression<T> {\n public type = `agg` as const\n constructor(\n public name: string, // such as count, avg, sum, min, max, etc.\n public args: Array<BasicExpression>,\n ) {\n super()\n }\n}\n\nexport class IncludesSubquery extends BaseExpression {\n public type = `includesSubquery` as const\n constructor(\n public query: QueryIR, // Child query (correlation WHERE removed)\n public correlationField: PropRef, // Parent-side ref (e.g., project.id)\n public childCorrelationField: PropRef, // Child-side ref (e.g., issue.projectId)\n public fieldName: string, // Result field name (e.g., \"issues\")\n public parentFilters?: Array<Where>, // WHERE clauses referencing parent aliases (applied post-join)\n public parentProjection?: Array<PropRef>, // Parent field refs used by parentFilters\n public materialization: IncludesMaterialization = `collection`,\n public scalarField?: string,\n ) {\n super()\n }\n}\n\n/**\n * Runtime helper to detect IR expression-like objects.\n * Prefer this over ad-hoc local implementations to keep behavior consistent.\n */\nexport function isExpressionLike(value: any): boolean {\n return (\n value instanceof Aggregate ||\n value instanceof Func ||\n value instanceof PropRef ||\n value instanceof Value ||\n value instanceof IncludesSubquery\n )\n}\n\n/**\n * Helper functions for working with Where clauses\n */\n\n/**\n * Extract the expression from a Where clause\n */\nexport function getWhereExpression(where: Where): BasicExpression<boolean> {\n return typeof where === `object` && `expression` in where\n ? where.expression\n : where\n}\n\n/**\n * Extract the expression from a HAVING clause\n * HAVING clauses can contain aggregates, unlike regular WHERE clauses\n */\nexport function getHavingExpression(\n having: Having,\n): BasicExpression | Aggregate {\n return typeof having === `object` && `expression` in having\n ? having.expression\n : having\n}\n\n/**\n * Check if a Where clause is marked as residual\n */\nexport function isResidualWhere(where: Where): boolean {\n return (\n typeof where === `object` &&\n `expression` in where &&\n where.residual === true\n )\n}\n\n/**\n * Create a residual Where clause from an expression\n */\nexport function createResidualWhere(\n expression: BasicExpression<boolean>,\n): Where {\n return { expression, residual: true }\n}\n\nfunction getRefFromAlias(\n query: QueryIR,\n alias: string,\n): CollectionRef | QueryRef | void {\n if (query.from.alias === alias) {\n return query.from\n }\n\n for (const join of query.join || []) {\n if (join.from.alias === alias) {\n return join.from\n }\n }\n}\n\n/**\n * Follows the given reference in a query\n * until its finds the root field the reference points to.\n * @returns The collection, its alias, and the path to the root field in this collection\n */\nexport function followRef(\n query: QueryIR,\n ref: PropRef<any>,\n collection: Collection,\n): { collection: Collection; path: Array<string> } | void {\n if (ref.path.length === 0) {\n return\n }\n\n if (ref.path.length === 1) {\n // This field should be part of this collection\n const field = ref.path[0]!\n // is it part of the select clause?\n if (query.select) {\n const selectedField = query.select[field]\n if (selectedField && selectedField.type === `ref`) {\n return followRef(query, selectedField, collection)\n }\n }\n\n // Either this field is not part of the select clause\n // and thus it must be part of the collection itself\n // or it is part of the select but is not a reference\n // so we can stop here and don't have to follow it\n return { collection, path: [field] }\n }\n\n if (ref.path.length > 1) {\n // This is a nested field\n const [alias, ...rest] = ref.path\n const aliasRef = getRefFromAlias(query, alias!)\n if (!aliasRef) {\n return\n }\n\n if (aliasRef.type === `queryRef`) {\n return followRef(aliasRef.query, new PropRef(rest), collection)\n } else {\n // This is a reference to a collection\n // we can't follow it further\n // so the field must be on the collection itself\n return { collection: aliasRef.collection, path: rest }\n }\n }\n}\n"],"names":[],"mappings":";;AA6BO,MAAM,wBAAwB;AAwCrC,MAAe,eAAwB;AAIvC;AAEO,MAAM,sBAAsB,eAAe;AAAA,EAEhD,YACS,YACA,OACP;AACA,UAAA;AAHO,SAAA,aAAA;AACA,SAAA,QAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAEO,MAAM,iBAAiB,eAAe;AAAA,EAE3C,YACS,OACA,OACP;AACA,UAAA;AAHO,SAAA,QAAA;AACA,SAAA,QAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAEO,MAAM,gBAAyB,eAAkB;AAAA,EAEtD,YACS,MACP;AACA,UAAA;AAFO,SAAA,OAAA;AAFT,SAAO,OAAO;AAAA,EAKd;AACF;AAEO,MAAM,cAAuB,eAAkB;AAAA,EAEpD,YACS,OACP;AACA,UAAA;AAFO,SAAA,QAAA;AAFT,SAAO,OAAO;AAAA,EAKd;AACF;AAEO,MAAM,aAAsB,eAAkB;AAAA,EAEnD,YACS,MACA,MACP;AACA,UAAA;AAHO,SAAA,OAAA;AACA,SAAA,OAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAOO,MAAM,kBAA2B,eAAkB;AAAA,EAExD,YACS,MACA,MACP;AACA,UAAA;AAHO,SAAA,OAAA;AACA,SAAA,OAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAEO,MAAM,yBAAyB,eAAe;AAAA,EAEnD,YACS,OACA,kBACA,uBACA,WACA,eACA,kBACA,kBAA2C,cAC3C,aACP;AACA,UAAA;AATO,SAAA,QAAA;AACA,SAAA,mBAAA;AACA,SAAA,wBAAA;AACA,SAAA,YAAA;AACA,SAAA,gBAAA;AACA,SAAA,mBAAA;AACA,SAAA,kBAAA;AACA,SAAA,cAAA;AATT,SAAO,OAAO;AAAA,EAYd;AACF;AAMO,SAAS,iBAAiB,OAAqB;AACpD,SACE,iBAAiB,aACjB,iBAAiB,QACjB,iBAAiB,WACjB,iBAAiB,SACjB,iBAAiB;AAErB;AASO,SAAS,mBAAmB,OAAwC;AACzE,SAAO,OAAO,UAAU,YAAY,gBAAgB,QAChD,MAAM,aACN;AACN;AAMO,SAAS,oBACd,QAC6B;AAC7B,SAAO,OAAO,WAAW,YAAY,gBAAgB,SACjD,OAAO,aACP;AACN;AAKO,SAAS,gBAAgB,OAAuB;AACrD,SACE,OAAO,UAAU,YACjB,gBAAgB,SAChB,MAAM,aAAa;AAEvB;AAKO,SAAS,oBACd,YACO;AACP,SAAO,EAAE,YAAY,UAAU,KAAA;AACjC;AAEA,SAAS,gBACP,OACA,OACiC;AACjC,MAAI,MAAM,KAAK,UAAU,OAAO;AAC9B,WAAO,MAAM;AAAA,EACf;AAEA,aAAW,QAAQ,MAAM,QAAQ,CAAA,GAAI;AACnC,QAAI,KAAK,KAAK,UAAU,OAAO;AAC7B,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AACF;AAOO,SAAS,UACd,OACA,KACA,YACwD;AACxD,MAAI,IAAI,KAAK,WAAW,GAAG;AACzB;AAAA,EACF;AAEA,MAAI,IAAI,KAAK,WAAW,GAAG;AAEzB,UAAM,QAAQ,IAAI,KAAK,CAAC;AAExB,QAAI,MAAM,QAAQ;AAChB,YAAM,gBAAgB,MAAM,OAAO,KAAK;AACxC,UAAI,iBAAiB,cAAc,SAAS,OAAO;AACjD,eAAO,UAAU,OAAO,eAAe,UAAU;AAAA,MACnD;AAAA,IACF;AAMA,WAAO,EAAE,YAAY,MAAM,CAAC,KAAK,EAAA;AAAA,EACnC;AAEA,MAAI,IAAI,KAAK,SAAS,GAAG;AAEvB,UAAM,CAAC,OAAO,GAAG,IAAI,IAAI,IAAI;AAC7B,UAAM,WAAW,gBAAgB,OAAO,KAAM;AAC9C,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,YAAY;AAChC,aAAO,UAAU,SAAS,OAAO,IAAI,QAAQ,IAAI,GAAG,UAAU;AAAA,IAChE,OAAO;AAIL,aAAO,EAAE,YAAY,SAAS,YAAY,MAAM,KAAA;AAAA,IAClD;AAAA,EACF;AACF;;;;;;;;;;;;;;;"}
@@ -17,9 +17,11 @@ export interface QueryIR {
17
17
  fnWhere?: Array<(row: NamespacedRow) => any>;
18
18
  fnHaving?: Array<(row: NamespacedRow) => any>;
19
19
  }
20
+ export type IncludesMaterialization = `collection` | `array` | `concat`;
21
+ export declare const INCLUDES_SCALAR_FIELD = "__includes_scalar__";
20
22
  export type From = CollectionRef | QueryRef;
21
23
  export type Select = {
22
- [alias: string]: BasicExpression | Aggregate | Select;
24
+ [alias: string]: BasicExpression | Aggregate | Select | IncludesSubquery;
23
25
  };
24
26
  export type Join = Array<JoinClause>;
25
27
  export interface JoinClause {
@@ -84,6 +86,24 @@ export declare class Aggregate<T = any> extends BaseExpression<T> {
84
86
  constructor(name: string, // such as count, avg, sum, min, max, etc.
85
87
  args: Array<BasicExpression>);
86
88
  }
89
+ export declare class IncludesSubquery extends BaseExpression {
90
+ query: QueryIR;
91
+ correlationField: PropRef;
92
+ childCorrelationField: PropRef;
93
+ fieldName: string;
94
+ parentFilters?: Array<Where> | undefined;
95
+ parentProjection?: Array<PropRef> | undefined;
96
+ materialization: IncludesMaterialization;
97
+ scalarField?: string | undefined;
98
+ type: "includesSubquery";
99
+ constructor(query: QueryIR, // Child query (correlation WHERE removed)
100
+ correlationField: PropRef, // Parent-side ref (e.g., project.id)
101
+ childCorrelationField: PropRef, // Child-side ref (e.g., issue.projectId)
102
+ fieldName: string, // Result field name (e.g., "issues")
103
+ parentFilters?: Array<Where> | undefined, // WHERE clauses referencing parent aliases (applied post-join)
104
+ parentProjection?: Array<PropRef> | undefined, // Parent field refs used by parentFilters
105
+ materialization?: IncludesMaterialization, scalarField?: string | undefined);
106
+ }
87
107
  /**
88
108
  * Runtime helper to detect IR expression-like objects.
89
109
  * Prefer this over ad-hoc local implementations to keep behavior consistent.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const dbIvm = require("@tanstack/db-ivm");
4
4
  const index = require("../compiler/index.cjs");
5
+ const index$1 = require("../../collection/index.cjs");
5
6
  const errors = require("../../errors.cjs");
6
7
  const scheduler = require("../../scheduler.cjs");
7
8
  const transactions = require("../../transactions.cjs");
@@ -27,7 +28,10 @@ class CollectionConfigBuilder {
27
28
  this.lazySources = /* @__PURE__ */ new Set();
28
29
  this.optimizableOrderByCollections = {};
29
30
  this.id = config.id || `live-query-${++liveQueryCollectionCounter}`;
30
- this.query = utils.buildQueryFromConfig(config);
31
+ this.query = utils.buildQueryFromConfig({
32
+ query: config.query,
33
+ requireObjectResult: true
34
+ });
31
35
  this.collections = utils.extractCollectionsFromQuery(this.query);
32
36
  const collectionAliasesById = utils.extractCollectionAliases(this.query);
33
37
  this.collectionByAlias = {};
@@ -362,6 +366,7 @@ class CollectionConfigBuilder {
362
366
  this.inputsCache = void 0;
363
367
  this.pipelineCache = void 0;
364
368
  this.sourceWhereClausesCache = void 0;
369
+ this.includesCache = void 0;
365
370
  this.lazySources.clear();
366
371
  this.optimizableOrderByCollections = {};
367
372
  this.lazySourcesCallbacks = {};
@@ -399,6 +404,7 @@ class CollectionConfigBuilder {
399
404
  this.pipelineCache = compilation.pipeline;
400
405
  this.sourceWhereClausesCache = compilation.sourceWhereClauses;
401
406
  this.compiledAliasToCollectionId = compilation.aliasToCollectionId;
407
+ this.includesCache = compilation.includes;
402
408
  const missingAliases = Object.keys(this.compiledAliasToCollectionId).filter(
403
409
  (alias) => !Object.hasOwn(this.inputsCache, alias)
404
410
  );
@@ -427,8 +433,14 @@ class CollectionConfigBuilder {
427
433
  messages.reduce(accumulateChanges, pendingChanges);
428
434
  })
429
435
  );
436
+ const includesState = this.setupIncludesOutput(
437
+ this.includesCache,
438
+ syncState
439
+ );
430
440
  syncState.flushPendingChanges = () => {
431
- if (pendingChanges.size === 0) {
441
+ const hasParentChanges = pendingChanges.size > 0;
442
+ const hasChildChanges = hasPendingIncludesChanges(includesState);
443
+ if (!hasParentChanges && !hasChildChanges) {
432
444
  return;
433
445
  }
434
446
  let changesToApply = pendingChanges;
@@ -452,10 +464,19 @@ class CollectionConfigBuilder {
452
464
  }
453
465
  changesToApply = merged;
454
466
  }
455
- begin();
456
- changesToApply.forEach(this.applyChanges.bind(this, config));
457
- commit();
467
+ if (hasParentChanges) {
468
+ begin();
469
+ changesToApply.forEach(this.applyChanges.bind(this, config));
470
+ commit();
471
+ }
458
472
  pendingChanges = /* @__PURE__ */ new Map();
473
+ flushIncludesState(
474
+ includesState,
475
+ config.collection,
476
+ this.id,
477
+ hasParentChanges ? changesToApply : null,
478
+ config
479
+ );
459
480
  };
460
481
  graph.finalize();
461
482
  syncState.graph = graph;
@@ -463,6 +484,65 @@ class CollectionConfigBuilder {
463
484
  syncState.pipeline = pipeline;
464
485
  return syncState;
465
486
  }
487
+ /**
488
+ * Sets up output callbacks for includes child pipelines.
489
+ * Each includes entry gets its own output callback that accumulates child changes,
490
+ * and a child registry that maps correlation key → child Collection.
491
+ */
492
+ setupIncludesOutput(includesEntries, syncState) {
493
+ if (!includesEntries || includesEntries.length === 0) {
494
+ return [];
495
+ }
496
+ return includesEntries.map((entry) => {
497
+ const state = {
498
+ fieldName: entry.fieldName,
499
+ childCorrelationField: entry.childCorrelationField,
500
+ hasOrderBy: entry.hasOrderBy,
501
+ materialization: entry.materialization,
502
+ scalarField: entry.scalarField,
503
+ childRegistry: /* @__PURE__ */ new Map(),
504
+ pendingChildChanges: /* @__PURE__ */ new Map(),
505
+ correlationToParentKeys: /* @__PURE__ */ new Map()
506
+ };
507
+ entry.pipeline.pipe(
508
+ dbIvm.output((data) => {
509
+ const messages = data.getInner();
510
+ syncState.messagesCount += messages.length;
511
+ for (const [[childKey, tupleData], multiplicity] of messages) {
512
+ const [childResult, _orderByIndex, correlationKey, parentContext] = tupleData;
513
+ const routingKey = computeRoutingKey(correlationKey, parentContext);
514
+ let byChild = state.pendingChildChanges.get(routingKey);
515
+ if (!byChild) {
516
+ byChild = /* @__PURE__ */ new Map();
517
+ state.pendingChildChanges.set(routingKey, byChild);
518
+ }
519
+ const existing = byChild.get(childKey) || {
520
+ deletes: 0,
521
+ inserts: 0,
522
+ value: childResult,
523
+ orderByIndex: _orderByIndex
524
+ };
525
+ if (multiplicity < 0) {
526
+ existing.deletes += Math.abs(multiplicity);
527
+ } else if (multiplicity > 0) {
528
+ existing.inserts += multiplicity;
529
+ existing.value = childResult;
530
+ }
531
+ byChild.set(childKey, existing);
532
+ }
533
+ })
534
+ );
535
+ if (entry.childCompilationResult.includes) {
536
+ state.nestedSetups = setupNestedPipelines(
537
+ entry.childCompilationResult.includes,
538
+ syncState
539
+ );
540
+ state.nestedRoutingIndex = /* @__PURE__ */ new Map();
541
+ state.nestedRoutingReverseIndex = /* @__PURE__ */ new Map();
542
+ }
543
+ return state;
544
+ });
545
+ }
466
546
  applyChanges(config, changes, key) {
467
547
  const { write, collection } = config;
468
548
  const { deletes, inserts, value, orderByIndex } = changes;
@@ -606,6 +686,422 @@ function createOrderByComparator(orderByIndices) {
606
686
  return 0;
607
687
  };
608
688
  }
689
+ function materializesInline(state) {
690
+ return state.materialization !== `collection`;
691
+ }
692
+ function materializeIncludedValue(state, entry) {
693
+ if (!entry) {
694
+ if (state.materialization === `array`) {
695
+ return [];
696
+ }
697
+ if (state.materialization === `concat`) {
698
+ return ``;
699
+ }
700
+ return void 0;
701
+ }
702
+ if (state.materialization === `collection`) {
703
+ return entry.collection;
704
+ }
705
+ const rows = [...entry.collection.toArray];
706
+ const values = state.scalarField ? rows.map((row) => row?.[state.scalarField]) : rows;
707
+ if (state.materialization === `array`) {
708
+ return values;
709
+ }
710
+ return values.map((value) => String(value ?? ``)).join(``);
711
+ }
712
+ function setupNestedPipelines(includes, syncState) {
713
+ return includes.map((entry) => {
714
+ const buffer = /* @__PURE__ */ new Map();
715
+ entry.pipeline.pipe(
716
+ dbIvm.output((data) => {
717
+ const messages = data.getInner();
718
+ syncState.messagesCount += messages.length;
719
+ for (const [[childKey, tupleData], multiplicity] of messages) {
720
+ const [childResult, _orderByIndex, correlationKey, parentContext] = tupleData;
721
+ const routingKey = computeRoutingKey(correlationKey, parentContext);
722
+ let byChild = buffer.get(routingKey);
723
+ if (!byChild) {
724
+ byChild = /* @__PURE__ */ new Map();
725
+ buffer.set(routingKey, byChild);
726
+ }
727
+ const existing = byChild.get(childKey) || {
728
+ deletes: 0,
729
+ inserts: 0,
730
+ value: childResult,
731
+ orderByIndex: _orderByIndex
732
+ };
733
+ if (multiplicity < 0) {
734
+ existing.deletes += Math.abs(multiplicity);
735
+ } else if (multiplicity > 0) {
736
+ existing.inserts += multiplicity;
737
+ existing.value = childResult;
738
+ }
739
+ byChild.set(childKey, existing);
740
+ }
741
+ })
742
+ );
743
+ const setup = {
744
+ compilationResult: entry,
745
+ buffer
746
+ };
747
+ if (entry.childCompilationResult.includes) {
748
+ setup.nestedSetups = setupNestedPipelines(
749
+ entry.childCompilationResult.includes,
750
+ syncState
751
+ );
752
+ }
753
+ return setup;
754
+ });
755
+ }
756
+ function createPerEntryIncludesStates(setups) {
757
+ return setups.map((setup) => {
758
+ const state = {
759
+ fieldName: setup.compilationResult.fieldName,
760
+ childCorrelationField: setup.compilationResult.childCorrelationField,
761
+ hasOrderBy: setup.compilationResult.hasOrderBy,
762
+ materialization: setup.compilationResult.materialization,
763
+ scalarField: setup.compilationResult.scalarField,
764
+ childRegistry: /* @__PURE__ */ new Map(),
765
+ pendingChildChanges: /* @__PURE__ */ new Map(),
766
+ correlationToParentKeys: /* @__PURE__ */ new Map()
767
+ };
768
+ if (setup.nestedSetups) {
769
+ state.nestedSetups = setup.nestedSetups;
770
+ state.nestedRoutingIndex = /* @__PURE__ */ new Map();
771
+ state.nestedRoutingReverseIndex = /* @__PURE__ */ new Map();
772
+ }
773
+ return state;
774
+ });
775
+ }
776
+ function drainNestedBuffers(state) {
777
+ const dirtyCorrelationKeys = /* @__PURE__ */ new Set();
778
+ if (!state.nestedSetups) return dirtyCorrelationKeys;
779
+ for (let i = 0; i < state.nestedSetups.length; i++) {
780
+ const setup = state.nestedSetups[i];
781
+ const toDelete = [];
782
+ for (const [nestedCorrelationKey, childChanges] of setup.buffer) {
783
+ const parentCorrelationKey = state.nestedRoutingIndex.get(nestedCorrelationKey);
784
+ if (parentCorrelationKey === void 0) {
785
+ continue;
786
+ }
787
+ const entry = state.childRegistry.get(parentCorrelationKey);
788
+ if (!entry || !entry.includesStates) {
789
+ continue;
790
+ }
791
+ const entryState = entry.includesStates[i];
792
+ for (const [childKey, changes] of childChanges) {
793
+ let byChild = entryState.pendingChildChanges.get(nestedCorrelationKey);
794
+ if (!byChild) {
795
+ byChild = /* @__PURE__ */ new Map();
796
+ entryState.pendingChildChanges.set(nestedCorrelationKey, byChild);
797
+ }
798
+ const existing = byChild.get(childKey);
799
+ if (existing) {
800
+ existing.inserts += changes.inserts;
801
+ existing.deletes += changes.deletes;
802
+ if (changes.inserts > 0) {
803
+ existing.value = changes.value;
804
+ if (changes.orderByIndex !== void 0) {
805
+ existing.orderByIndex = changes.orderByIndex;
806
+ }
807
+ }
808
+ } else {
809
+ byChild.set(childKey, { ...changes });
810
+ }
811
+ }
812
+ dirtyCorrelationKeys.add(parentCorrelationKey);
813
+ toDelete.push(nestedCorrelationKey);
814
+ }
815
+ for (const key of toDelete) {
816
+ setup.buffer.delete(key);
817
+ }
818
+ }
819
+ return dirtyCorrelationKeys;
820
+ }
821
+ function updateRoutingIndex(state, correlationKey, childChanges) {
822
+ if (!state.nestedSetups) return;
823
+ for (const setup of state.nestedSetups) {
824
+ for (const [, change] of childChanges) {
825
+ if (change.inserts > 0) {
826
+ const nestedRouting = change.value[index.INCLUDES_ROUTING]?.[setup.compilationResult.fieldName];
827
+ const nestedCorrelationKey = nestedRouting?.correlationKey;
828
+ const nestedParentContext = nestedRouting?.parentContext ?? null;
829
+ const nestedRoutingKey = computeRoutingKey(
830
+ nestedCorrelationKey,
831
+ nestedParentContext
832
+ );
833
+ if (nestedCorrelationKey != null) {
834
+ state.nestedRoutingIndex.set(nestedRoutingKey, correlationKey);
835
+ let reverseSet = state.nestedRoutingReverseIndex.get(correlationKey);
836
+ if (!reverseSet) {
837
+ reverseSet = /* @__PURE__ */ new Set();
838
+ state.nestedRoutingReverseIndex.set(correlationKey, reverseSet);
839
+ }
840
+ reverseSet.add(nestedRoutingKey);
841
+ }
842
+ } else if (change.deletes > 0 && change.inserts === 0) {
843
+ const nestedRouting2 = change.value[index.INCLUDES_ROUTING]?.[setup.compilationResult.fieldName];
844
+ const nestedCorrelationKey = nestedRouting2?.correlationKey;
845
+ const nestedParentContext2 = nestedRouting2?.parentContext ?? null;
846
+ const nestedRoutingKey = computeRoutingKey(
847
+ nestedCorrelationKey,
848
+ nestedParentContext2
849
+ );
850
+ if (nestedCorrelationKey != null) {
851
+ state.nestedRoutingIndex.delete(nestedRoutingKey);
852
+ const reverseSet = state.nestedRoutingReverseIndex.get(correlationKey);
853
+ if (reverseSet) {
854
+ reverseSet.delete(nestedRoutingKey);
855
+ if (reverseSet.size === 0) {
856
+ state.nestedRoutingReverseIndex.delete(correlationKey);
857
+ }
858
+ }
859
+ }
860
+ }
861
+ }
862
+ }
863
+ }
864
+ function cleanRoutingIndexOnDelete(state, correlationKey) {
865
+ if (!state.nestedRoutingReverseIndex) return;
866
+ const nestedKeys = state.nestedRoutingReverseIndex.get(correlationKey);
867
+ if (nestedKeys) {
868
+ for (const nestedKey of nestedKeys) {
869
+ state.nestedRoutingIndex.delete(nestedKey);
870
+ }
871
+ state.nestedRoutingReverseIndex.delete(correlationKey);
872
+ }
873
+ }
874
+ function hasNestedBufferChanges(setups) {
875
+ for (const setup of setups) {
876
+ if (setup.buffer.size > 0) return true;
877
+ if (setup.nestedSetups && hasNestedBufferChanges(setup.nestedSetups))
878
+ return true;
879
+ }
880
+ return false;
881
+ }
882
+ function computeRoutingKey(correlationKey, parentContext) {
883
+ if (parentContext == null) return correlationKey;
884
+ return JSON.stringify([correlationKey, parentContext]);
885
+ }
886
+ function createChildCollectionEntry(parentId, fieldName, correlationKey, hasOrderBy, nestedSetups) {
887
+ const resultKeys = /* @__PURE__ */ new WeakMap();
888
+ const orderByIndices = hasOrderBy ? /* @__PURE__ */ new WeakMap() : null;
889
+ let syncMethods = null;
890
+ const compare = orderByIndices ? createOrderByComparator(orderByIndices) : void 0;
891
+ const collection = index$1.createCollection({
892
+ id: `__child-collection:${parentId}-${fieldName}-${dbIvm.serializeValue(correlationKey)}`,
893
+ getKey: (item) => resultKeys.get(item),
894
+ compare,
895
+ sync: {
896
+ rowUpdateMode: `full`,
897
+ sync: (methods) => {
898
+ syncMethods = methods;
899
+ return () => {
900
+ syncMethods = null;
901
+ };
902
+ }
903
+ },
904
+ startSync: true
905
+ });
906
+ const entry = {
907
+ collection,
908
+ get syncMethods() {
909
+ return syncMethods;
910
+ },
911
+ resultKeys,
912
+ orderByIndices
913
+ };
914
+ if (nestedSetups) {
915
+ entry.includesStates = createPerEntryIncludesStates(nestedSetups);
916
+ }
917
+ return entry;
918
+ }
919
+ function flushIncludesState(includesState, parentCollection, parentId, parentChanges, parentSyncMethods) {
920
+ for (const state of includesState) {
921
+ if (parentChanges) {
922
+ for (const [parentKey, changes] of parentChanges) {
923
+ if (changes.inserts > 0) {
924
+ const parentResult = changes.value;
925
+ const routing = parentResult[index.INCLUDES_ROUTING]?.[state.fieldName];
926
+ const correlationKey = routing?.correlationKey;
927
+ const parentContext = routing?.parentContext ?? null;
928
+ const routingKey = computeRoutingKey(correlationKey, parentContext);
929
+ if (correlationKey != null) {
930
+ if (!state.childRegistry.has(routingKey)) {
931
+ const entry = createChildCollectionEntry(
932
+ parentId,
933
+ state.fieldName,
934
+ routingKey,
935
+ state.hasOrderBy,
936
+ state.nestedSetups
937
+ );
938
+ state.childRegistry.set(routingKey, entry);
939
+ }
940
+ let parentKeys = state.correlationToParentKeys.get(routingKey);
941
+ if (!parentKeys) {
942
+ parentKeys = /* @__PURE__ */ new Set();
943
+ state.correlationToParentKeys.set(routingKey, parentKeys);
944
+ }
945
+ parentKeys.add(parentKey);
946
+ const childValue = materializeIncludedValue(
947
+ state,
948
+ state.childRegistry.get(routingKey)
949
+ );
950
+ parentResult[state.fieldName] = childValue;
951
+ const storedParent = parentCollection.get(parentKey);
952
+ if (storedParent && storedParent !== parentResult) {
953
+ storedParent[state.fieldName] = childValue;
954
+ }
955
+ }
956
+ }
957
+ }
958
+ }
959
+ const affectedCorrelationKeys = materializesInline(state) ? new Set(state.pendingChildChanges.keys()) : null;
960
+ const entriesWithChildChanges = /* @__PURE__ */ new Map();
961
+ if (state.pendingChildChanges.size > 0) {
962
+ for (const [correlationKey, childChanges] of state.pendingChildChanges) {
963
+ let entry = state.childRegistry.get(correlationKey);
964
+ if (!entry) {
965
+ entry = createChildCollectionEntry(
966
+ parentId,
967
+ state.fieldName,
968
+ correlationKey,
969
+ state.hasOrderBy,
970
+ state.nestedSetups
971
+ );
972
+ state.childRegistry.set(correlationKey, entry);
973
+ }
974
+ if (state.materialization === `collection`) {
975
+ attachChildCollectionToParent(
976
+ parentCollection,
977
+ state.fieldName,
978
+ correlationKey,
979
+ state.correlationToParentKeys,
980
+ entry.collection
981
+ );
982
+ }
983
+ if (entry.syncMethods) {
984
+ entry.syncMethods.begin();
985
+ for (const [childKey, change] of childChanges) {
986
+ entry.resultKeys.set(change.value, childKey);
987
+ if (entry.orderByIndices && change.orderByIndex !== void 0) {
988
+ entry.orderByIndices.set(change.value, change.orderByIndex);
989
+ }
990
+ if (change.inserts > 0 && change.deletes === 0) {
991
+ entry.syncMethods.write({ value: change.value, type: `insert` });
992
+ } else if (change.inserts > change.deletes || change.inserts === change.deletes && entry.syncMethods.collection.has(
993
+ entry.syncMethods.collection.getKeyFromItem(change.value)
994
+ )) {
995
+ entry.syncMethods.write({ value: change.value, type: `update` });
996
+ } else if (change.deletes > 0) {
997
+ entry.syncMethods.write({ value: change.value, type: `delete` });
998
+ }
999
+ }
1000
+ entry.syncMethods.commit();
1001
+ }
1002
+ updateRoutingIndex(state, correlationKey, childChanges);
1003
+ entriesWithChildChanges.set(correlationKey, { entry, childChanges });
1004
+ }
1005
+ state.pendingChildChanges.clear();
1006
+ }
1007
+ const dirtyFromBuffers = drainNestedBuffers(state);
1008
+ for (const [, { entry, childChanges }] of entriesWithChildChanges) {
1009
+ if (entry.includesStates) {
1010
+ flushIncludesState(
1011
+ entry.includesStates,
1012
+ entry.collection,
1013
+ entry.collection.id,
1014
+ childChanges,
1015
+ entry.syncMethods
1016
+ );
1017
+ }
1018
+ }
1019
+ for (const correlationKey of dirtyFromBuffers) {
1020
+ if (entriesWithChildChanges.has(correlationKey)) continue;
1021
+ const entry = state.childRegistry.get(correlationKey);
1022
+ if (entry?.includesStates) {
1023
+ flushIncludesState(
1024
+ entry.includesStates,
1025
+ entry.collection,
1026
+ entry.collection.id,
1027
+ null,
1028
+ entry.syncMethods
1029
+ );
1030
+ }
1031
+ }
1032
+ const inlineReEmitKeys = materializesInline(state) ? /* @__PURE__ */ new Set([...affectedCorrelationKeys || [], ...dirtyFromBuffers]) : null;
1033
+ if (parentSyncMethods && inlineReEmitKeys && inlineReEmitKeys.size > 0) {
1034
+ const events = [];
1035
+ for (const correlationKey of inlineReEmitKeys) {
1036
+ const parentKeys = state.correlationToParentKeys.get(correlationKey);
1037
+ if (!parentKeys) continue;
1038
+ const entry = state.childRegistry.get(correlationKey);
1039
+ for (const parentKey of parentKeys) {
1040
+ const item = parentCollection.get(parentKey);
1041
+ if (item) {
1042
+ const key = parentSyncMethods.collection.getKeyFromItem(item);
1043
+ const previousValue = { ...item };
1044
+ item[state.fieldName] = materializeIncludedValue(state, entry);
1045
+ events.push({
1046
+ type: `update`,
1047
+ key,
1048
+ value: item,
1049
+ previousValue
1050
+ });
1051
+ }
1052
+ }
1053
+ }
1054
+ if (events.length > 0) {
1055
+ const changesManager = parentCollection._changes;
1056
+ changesManager.emitEvents(events, true);
1057
+ }
1058
+ }
1059
+ if (parentChanges) {
1060
+ for (const [parentKey, changes] of parentChanges) {
1061
+ if (changes.deletes > 0 && changes.inserts === 0) {
1062
+ const routing = changes.value[index.INCLUDES_ROUTING]?.[state.fieldName];
1063
+ const correlationKey = routing?.correlationKey;
1064
+ const parentContext = routing?.parentContext ?? null;
1065
+ const routingKey = computeRoutingKey(correlationKey, parentContext);
1066
+ if (correlationKey != null) {
1067
+ const parentKeys = state.correlationToParentKeys.get(routingKey);
1068
+ if (parentKeys) {
1069
+ parentKeys.delete(parentKey);
1070
+ if (parentKeys.size === 0) {
1071
+ cleanRoutingIndexOnDelete(state, routingKey);
1072
+ state.childRegistry.delete(routingKey);
1073
+ state.correlationToParentKeys.delete(routingKey);
1074
+ }
1075
+ }
1076
+ }
1077
+ }
1078
+ }
1079
+ }
1080
+ }
1081
+ if (parentChanges) {
1082
+ for (const [, changes] of parentChanges) {
1083
+ delete changes.value[index.INCLUDES_ROUTING];
1084
+ }
1085
+ }
1086
+ }
1087
+ function hasPendingIncludesChanges(states) {
1088
+ for (const state of states) {
1089
+ if (state.pendingChildChanges.size > 0) return true;
1090
+ if (state.nestedSetups && hasNestedBufferChanges(state.nestedSetups))
1091
+ return true;
1092
+ }
1093
+ return false;
1094
+ }
1095
+ function attachChildCollectionToParent(parentCollection, fieldName, correlationKey, correlationToParentKeys, childCollection) {
1096
+ const parentKeys = correlationToParentKeys.get(correlationKey);
1097
+ if (!parentKeys) return;
1098
+ for (const parentKey of parentKeys) {
1099
+ const item = parentCollection.get(parentKey);
1100
+ if (item) {
1101
+ item[fieldName] = childCollection;
1102
+ }
1103
+ }
1104
+ }
609
1105
  function accumulateChanges(acc, [[key, tupleData], multiplicity]) {
610
1106
  const [value, orderByIndex] = tupleData;
611
1107
  const changes = acc.get(key) || {