@tanstack/db 0.0.14 → 0.0.16

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 (197) hide show
  1. package/dist/cjs/collection.cjs +117 -104
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +18 -21
  4. package/dist/cjs/index.cjs +31 -13
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/index.d.cts +0 -1
  7. package/dist/cjs/query/builder/functions.cjs +107 -0
  8. package/dist/cjs/query/builder/functions.cjs.map +1 -0
  9. package/dist/cjs/query/builder/functions.d.cts +38 -0
  10. package/dist/cjs/query/builder/index.cjs +499 -0
  11. package/dist/cjs/query/builder/index.cjs.map +1 -0
  12. package/dist/cjs/query/builder/index.d.cts +324 -0
  13. package/dist/cjs/query/builder/ref-proxy.cjs +92 -0
  14. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -0
  15. package/dist/cjs/query/builder/ref-proxy.d.cts +28 -0
  16. package/dist/cjs/query/builder/types.d.cts +81 -0
  17. package/dist/cjs/query/compiler/evaluators.cjs +261 -0
  18. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -0
  19. package/dist/cjs/query/compiler/evaluators.d.cts +11 -0
  20. package/dist/cjs/query/compiler/group-by.cjs +271 -0
  21. package/dist/cjs/query/compiler/group-by.cjs.map +1 -0
  22. package/dist/cjs/query/compiler/group-by.d.cts +7 -0
  23. package/dist/cjs/query/compiler/index.cjs +181 -0
  24. package/dist/cjs/query/compiler/index.cjs.map +1 -0
  25. package/dist/cjs/query/compiler/index.d.cts +15 -0
  26. package/dist/cjs/query/compiler/joins.cjs +116 -0
  27. package/dist/cjs/query/compiler/joins.cjs.map +1 -0
  28. package/dist/cjs/query/compiler/joins.d.cts +11 -0
  29. package/dist/cjs/query/compiler/order-by.cjs +89 -0
  30. package/dist/cjs/query/compiler/order-by.cjs.map +1 -0
  31. package/dist/cjs/query/compiler/order-by.d.cts +9 -0
  32. package/dist/cjs/query/compiler/select.cjs +57 -0
  33. package/dist/cjs/query/compiler/select.cjs.map +1 -0
  34. package/dist/cjs/query/compiler/select.d.cts +15 -0
  35. package/dist/cjs/query/index.d.cts +5 -5
  36. package/dist/cjs/query/ir.cjs +57 -0
  37. package/dist/cjs/query/ir.cjs.map +1 -0
  38. package/dist/cjs/query/ir.d.cts +81 -0
  39. package/dist/cjs/query/live-query-collection.cjs +224 -0
  40. package/dist/cjs/query/live-query-collection.cjs.map +1 -0
  41. package/dist/cjs/query/live-query-collection.d.cts +124 -0
  42. package/dist/cjs/transactions.cjs +20 -13
  43. package/dist/cjs/transactions.cjs.map +1 -1
  44. package/dist/cjs/transactions.d.cts +10 -1
  45. package/dist/cjs/types.d.cts +13 -0
  46. package/dist/esm/collection.d.ts +18 -21
  47. package/dist/esm/collection.js +118 -105
  48. package/dist/esm/collection.js.map +1 -1
  49. package/dist/esm/index.d.ts +0 -1
  50. package/dist/esm/index.js +30 -12
  51. package/dist/esm/query/builder/functions.d.ts +38 -0
  52. package/dist/esm/query/builder/functions.js +107 -0
  53. package/dist/esm/query/builder/functions.js.map +1 -0
  54. package/dist/esm/query/builder/index.d.ts +324 -0
  55. package/dist/esm/query/builder/index.js +499 -0
  56. package/dist/esm/query/builder/index.js.map +1 -0
  57. package/dist/esm/query/builder/ref-proxy.d.ts +28 -0
  58. package/dist/esm/query/builder/ref-proxy.js +92 -0
  59. package/dist/esm/query/builder/ref-proxy.js.map +1 -0
  60. package/dist/esm/query/builder/types.d.ts +81 -0
  61. package/dist/esm/query/compiler/evaluators.d.ts +11 -0
  62. package/dist/esm/query/compiler/evaluators.js +261 -0
  63. package/dist/esm/query/compiler/evaluators.js.map +1 -0
  64. package/dist/esm/query/compiler/group-by.d.ts +7 -0
  65. package/dist/esm/query/compiler/group-by.js +271 -0
  66. package/dist/esm/query/compiler/group-by.js.map +1 -0
  67. package/dist/esm/query/compiler/index.d.ts +15 -0
  68. package/dist/esm/query/compiler/index.js +181 -0
  69. package/dist/esm/query/compiler/index.js.map +1 -0
  70. package/dist/esm/query/compiler/joins.d.ts +11 -0
  71. package/dist/esm/query/compiler/joins.js +116 -0
  72. package/dist/esm/query/compiler/joins.js.map +1 -0
  73. package/dist/esm/query/compiler/order-by.d.ts +9 -0
  74. package/dist/esm/query/compiler/order-by.js +89 -0
  75. package/dist/esm/query/compiler/order-by.js.map +1 -0
  76. package/dist/esm/query/compiler/select.d.ts +15 -0
  77. package/dist/esm/query/compiler/select.js +57 -0
  78. package/dist/esm/query/compiler/select.js.map +1 -0
  79. package/dist/esm/query/index.d.ts +5 -5
  80. package/dist/esm/query/ir.d.ts +81 -0
  81. package/dist/esm/query/ir.js +57 -0
  82. package/dist/esm/query/ir.js.map +1 -0
  83. package/dist/esm/query/live-query-collection.d.ts +124 -0
  84. package/dist/esm/query/live-query-collection.js +224 -0
  85. package/dist/esm/query/live-query-collection.js.map +1 -0
  86. package/dist/esm/transactions.d.ts +10 -1
  87. package/dist/esm/transactions.js +20 -13
  88. package/dist/esm/transactions.js.map +1 -1
  89. package/dist/esm/types.d.ts +13 -0
  90. package/package.json +3 -4
  91. package/src/collection.ts +152 -129
  92. package/src/index.ts +0 -1
  93. package/src/query/builder/functions.ts +267 -0
  94. package/src/query/builder/index.ts +648 -0
  95. package/src/query/builder/ref-proxy.ts +156 -0
  96. package/src/query/builder/types.ts +282 -0
  97. package/src/query/compiler/evaluators.ts +315 -0
  98. package/src/query/compiler/group-by.ts +428 -0
  99. package/src/query/compiler/index.ts +276 -0
  100. package/src/query/compiler/joins.ts +228 -0
  101. package/src/query/compiler/order-by.ts +139 -0
  102. package/src/query/compiler/select.ts +173 -0
  103. package/src/query/index.ts +54 -5
  104. package/src/query/ir.ts +128 -0
  105. package/src/query/live-query-collection.ts +512 -0
  106. package/src/transactions.ts +27 -16
  107. package/src/types.ts +15 -0
  108. package/dist/cjs/query/compiled-query.cjs +0 -160
  109. package/dist/cjs/query/compiled-query.cjs.map +0 -1
  110. package/dist/cjs/query/compiled-query.d.cts +0 -20
  111. package/dist/cjs/query/evaluators.cjs +0 -161
  112. package/dist/cjs/query/evaluators.cjs.map +0 -1
  113. package/dist/cjs/query/evaluators.d.cts +0 -14
  114. package/dist/cjs/query/extractors.cjs +0 -122
  115. package/dist/cjs/query/extractors.cjs.map +0 -1
  116. package/dist/cjs/query/extractors.d.cts +0 -22
  117. package/dist/cjs/query/functions.cjs +0 -152
  118. package/dist/cjs/query/functions.cjs.map +0 -1
  119. package/dist/cjs/query/functions.d.cts +0 -21
  120. package/dist/cjs/query/group-by.cjs +0 -88
  121. package/dist/cjs/query/group-by.cjs.map +0 -1
  122. package/dist/cjs/query/group-by.d.cts +0 -40
  123. package/dist/cjs/query/joins.cjs +0 -141
  124. package/dist/cjs/query/joins.cjs.map +0 -1
  125. package/dist/cjs/query/joins.d.cts +0 -14
  126. package/dist/cjs/query/order-by.cjs +0 -185
  127. package/dist/cjs/query/order-by.cjs.map +0 -1
  128. package/dist/cjs/query/order-by.d.cts +0 -3
  129. package/dist/cjs/query/pipeline-compiler.cjs +0 -89
  130. package/dist/cjs/query/pipeline-compiler.cjs.map +0 -1
  131. package/dist/cjs/query/pipeline-compiler.d.cts +0 -10
  132. package/dist/cjs/query/query-builder.cjs +0 -307
  133. package/dist/cjs/query/query-builder.cjs.map +0 -1
  134. package/dist/cjs/query/query-builder.d.cts +0 -225
  135. package/dist/cjs/query/schema.d.cts +0 -100
  136. package/dist/cjs/query/select.cjs +0 -130
  137. package/dist/cjs/query/select.cjs.map +0 -1
  138. package/dist/cjs/query/select.d.cts +0 -3
  139. package/dist/cjs/query/types.d.cts +0 -189
  140. package/dist/cjs/query/utils.cjs +0 -154
  141. package/dist/cjs/query/utils.cjs.map +0 -1
  142. package/dist/cjs/query/utils.d.cts +0 -37
  143. package/dist/cjs/utils.cjs +0 -17
  144. package/dist/cjs/utils.cjs.map +0 -1
  145. package/dist/cjs/utils.d.cts +0 -3
  146. package/dist/esm/query/compiled-query.d.ts +0 -20
  147. package/dist/esm/query/compiled-query.js +0 -160
  148. package/dist/esm/query/compiled-query.js.map +0 -1
  149. package/dist/esm/query/evaluators.d.ts +0 -14
  150. package/dist/esm/query/evaluators.js +0 -161
  151. package/dist/esm/query/evaluators.js.map +0 -1
  152. package/dist/esm/query/extractors.d.ts +0 -22
  153. package/dist/esm/query/extractors.js +0 -122
  154. package/dist/esm/query/extractors.js.map +0 -1
  155. package/dist/esm/query/functions.d.ts +0 -21
  156. package/dist/esm/query/functions.js +0 -152
  157. package/dist/esm/query/functions.js.map +0 -1
  158. package/dist/esm/query/group-by.d.ts +0 -40
  159. package/dist/esm/query/group-by.js +0 -88
  160. package/dist/esm/query/group-by.js.map +0 -1
  161. package/dist/esm/query/joins.d.ts +0 -14
  162. package/dist/esm/query/joins.js +0 -141
  163. package/dist/esm/query/joins.js.map +0 -1
  164. package/dist/esm/query/order-by.d.ts +0 -3
  165. package/dist/esm/query/order-by.js +0 -185
  166. package/dist/esm/query/order-by.js.map +0 -1
  167. package/dist/esm/query/pipeline-compiler.d.ts +0 -10
  168. package/dist/esm/query/pipeline-compiler.js +0 -89
  169. package/dist/esm/query/pipeline-compiler.js.map +0 -1
  170. package/dist/esm/query/query-builder.d.ts +0 -225
  171. package/dist/esm/query/query-builder.js +0 -307
  172. package/dist/esm/query/query-builder.js.map +0 -1
  173. package/dist/esm/query/schema.d.ts +0 -100
  174. package/dist/esm/query/select.d.ts +0 -3
  175. package/dist/esm/query/select.js +0 -130
  176. package/dist/esm/query/select.js.map +0 -1
  177. package/dist/esm/query/types.d.ts +0 -189
  178. package/dist/esm/query/utils.d.ts +0 -37
  179. package/dist/esm/query/utils.js +0 -154
  180. package/dist/esm/query/utils.js.map +0 -1
  181. package/dist/esm/utils.d.ts +0 -3
  182. package/dist/esm/utils.js +0 -17
  183. package/dist/esm/utils.js.map +0 -1
  184. package/src/query/compiled-query.ts +0 -234
  185. package/src/query/evaluators.ts +0 -250
  186. package/src/query/extractors.ts +0 -214
  187. package/src/query/functions.ts +0 -297
  188. package/src/query/group-by.ts +0 -139
  189. package/src/query/joins.ts +0 -260
  190. package/src/query/order-by.ts +0 -264
  191. package/src/query/pipeline-compiler.ts +0 -149
  192. package/src/query/query-builder.ts +0 -902
  193. package/src/query/schema.ts +0 -268
  194. package/src/query/select.ts +0 -208
  195. package/src/query/types.ts +0 -418
  196. package/src/query/utils.ts +0 -245
  197. package/src/utils.ts +0 -15
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const d2mini = require("@electric-sql/d2mini");
4
+ const evaluators = require("./evaluators.cjs");
5
+ const joins = require("./joins.cjs");
6
+ const groupBy = require("./group-by.cjs");
7
+ const orderBy = require("./order-by.cjs");
8
+ const select = require("./select.cjs");
9
+ function compileQuery(query, inputs, cache = /* @__PURE__ */ new WeakMap()) {
10
+ const cachedResult = cache.get(query);
11
+ if (cachedResult) {
12
+ return cachedResult;
13
+ }
14
+ const allInputs = { ...inputs };
15
+ const tables = {};
16
+ const { alias: mainTableAlias, input: mainInput } = processFrom(
17
+ query.from,
18
+ allInputs,
19
+ cache
20
+ );
21
+ tables[mainTableAlias] = mainInput;
22
+ let pipeline = mainInput.pipe(
23
+ d2mini.map(([key, row]) => {
24
+ const ret = [key, { [mainTableAlias]: row }];
25
+ return ret;
26
+ })
27
+ );
28
+ if (query.join && query.join.length > 0) {
29
+ pipeline = joins.processJoins(
30
+ pipeline,
31
+ query.join,
32
+ tables,
33
+ mainTableAlias,
34
+ allInputs,
35
+ cache
36
+ );
37
+ }
38
+ if (query.where && query.where.length > 0) {
39
+ const compiledWheres = query.where.map((where) => evaluators.compileExpression(where));
40
+ for (const compiledWhere of compiledWheres) {
41
+ pipeline = pipeline.pipe(
42
+ d2mini.filter(([_key, namespacedRow]) => {
43
+ return compiledWhere(namespacedRow);
44
+ })
45
+ );
46
+ }
47
+ }
48
+ if (query.fnWhere && query.fnWhere.length > 0) {
49
+ for (const fnWhere of query.fnWhere) {
50
+ pipeline = pipeline.pipe(
51
+ d2mini.filter(([_key, namespacedRow]) => {
52
+ return fnWhere(namespacedRow);
53
+ })
54
+ );
55
+ }
56
+ }
57
+ if (query.fnSelect) {
58
+ pipeline = pipeline.pipe(
59
+ d2mini.map(([key, namespacedRow]) => {
60
+ const selectResults = query.fnSelect(namespacedRow);
61
+ return [
62
+ key,
63
+ {
64
+ ...namespacedRow,
65
+ __select_results: selectResults
66
+ }
67
+ ];
68
+ })
69
+ );
70
+ } else if (query.select) {
71
+ pipeline = select.processSelectToResults(pipeline, query.select);
72
+ } else {
73
+ pipeline = pipeline.pipe(
74
+ d2mini.map(([key, namespacedRow]) => {
75
+ const selectResults = !query.join && !query.groupBy ? namespacedRow[mainTableAlias] : namespacedRow;
76
+ return [
77
+ key,
78
+ {
79
+ ...namespacedRow,
80
+ __select_results: selectResults
81
+ }
82
+ ];
83
+ })
84
+ );
85
+ }
86
+ if (query.groupBy && query.groupBy.length > 0) {
87
+ pipeline = groupBy.processGroupBy(
88
+ pipeline,
89
+ query.groupBy,
90
+ query.having,
91
+ query.select,
92
+ query.fnHaving
93
+ );
94
+ } else if (query.select) {
95
+ const hasAggregates = Object.values(query.select).some(
96
+ (expr) => expr.type === `agg`
97
+ );
98
+ if (hasAggregates) {
99
+ pipeline = groupBy.processGroupBy(
100
+ pipeline,
101
+ [],
102
+ // Empty group by means single group
103
+ query.having,
104
+ query.select,
105
+ query.fnHaving
106
+ );
107
+ }
108
+ }
109
+ if (query.having && (!query.groupBy || query.groupBy.length === 0)) {
110
+ const hasAggregates = query.select ? Object.values(query.select).some((expr) => expr.type === `agg`) : false;
111
+ if (!hasAggregates) {
112
+ throw new Error(`HAVING clause requires GROUP BY clause`);
113
+ }
114
+ }
115
+ if (query.fnHaving && query.fnHaving.length > 0 && (!query.groupBy || query.groupBy.length === 0)) {
116
+ for (const fnHaving of query.fnHaving) {
117
+ pipeline = pipeline.pipe(
118
+ d2mini.filter(([_key, namespacedRow]) => {
119
+ return fnHaving(namespacedRow);
120
+ })
121
+ );
122
+ }
123
+ }
124
+ if (query.orderBy && query.orderBy.length > 0) {
125
+ const orderedPipeline = orderBy.processOrderBy(
126
+ pipeline,
127
+ query.orderBy,
128
+ query.limit,
129
+ query.offset
130
+ );
131
+ const resultPipeline2 = orderedPipeline.pipe(
132
+ d2mini.map(([key, [row, orderByIndex]]) => {
133
+ const finalResults = row.__select_results;
134
+ return [key, [finalResults, orderByIndex]];
135
+ })
136
+ );
137
+ const result2 = resultPipeline2;
138
+ cache.set(query, result2);
139
+ return result2;
140
+ } else if (query.limit !== void 0 || query.offset !== void 0) {
141
+ throw new Error(
142
+ `LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results`
143
+ );
144
+ }
145
+ const resultPipeline = pipeline.pipe(
146
+ d2mini.map(([key, row]) => {
147
+ const finalResults = row.__select_results;
148
+ return [key, [finalResults, void 0]];
149
+ })
150
+ );
151
+ const result = resultPipeline;
152
+ cache.set(query, result);
153
+ return result;
154
+ }
155
+ function processFrom(from, allInputs, cache) {
156
+ switch (from.type) {
157
+ case `collectionRef`: {
158
+ const input = allInputs[from.collection.id];
159
+ if (!input) {
160
+ throw new Error(
161
+ `Input for collection "${from.collection.id}" not found in inputs map`
162
+ );
163
+ }
164
+ return { alias: from.alias, input };
165
+ }
166
+ case `queryRef`: {
167
+ const subQueryInput = compileQuery(from.query, allInputs, cache);
168
+ const extractedInput = subQueryInput.pipe(
169
+ d2mini.map((data) => {
170
+ const [key, [value, _orderByIndex]] = data;
171
+ return [key, value];
172
+ })
173
+ );
174
+ return { alias: from.alias, input: extractedInput };
175
+ }
176
+ default:
177
+ throw new Error(`Unsupported FROM type: ${from.type}`);
178
+ }
179
+ }
180
+ exports.compileQuery = compileQuery;
181
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/query/compiler/index.ts"],"sourcesContent":["import { filter, map } from \"@electric-sql/d2mini\"\nimport { compileExpression } from \"./evaluators.js\"\nimport { processJoins } from \"./joins.js\"\nimport { processGroupBy } from \"./group-by.js\"\nimport { processOrderBy } from \"./order-by.js\"\nimport { processSelectToResults } from \"./select.js\"\nimport type { CollectionRef, QueryIR, QueryRef } from \"../ir.js\"\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n ResultStream,\n} from \"../../types.js\"\n\n/**\n * Cache for compiled subqueries to avoid duplicate compilation\n */\ntype QueryCache = WeakMap<QueryIR, ResultStream>\n\n/**\n * Compiles a query2 IR into a D2 pipeline\n * @param query The query IR to compile\n * @param inputs Mapping of collection names to input streams\n * @param cache Optional cache for compiled subqueries (used internally for recursion)\n * @returns A stream builder representing the compiled query\n */\nexport function compileQuery(\n query: QueryIR,\n inputs: Record<string, KeyedStream>,\n cache: QueryCache = new WeakMap()\n): ResultStream {\n // Check if this query has already been compiled\n const cachedResult = cache.get(query)\n if (cachedResult) {\n return cachedResult\n }\n\n // Create a copy of the inputs map to avoid modifying the original\n const allInputs = { ...inputs }\n\n // Create a map of table aliases to inputs\n const tables: Record<string, KeyedStream> = {}\n\n // Process the FROM clause to get the main table\n const { alias: mainTableAlias, input: mainInput } = processFrom(\n query.from,\n allInputs,\n cache\n )\n tables[mainTableAlias] = mainInput\n\n // Prepare the initial pipeline with the main table wrapped in its alias\n let pipeline: NamespacedAndKeyedStream = mainInput.pipe(\n map(([key, row]) => {\n // Initialize the record with a nested structure\n const ret = [key, { [mainTableAlias]: row }] as [\n string,\n Record<string, typeof row>,\n ]\n return ret\n })\n )\n\n // Process JOIN clauses if they exist\n if (query.join && query.join.length > 0) {\n pipeline = processJoins(\n pipeline,\n query.join,\n tables,\n mainTableAlias,\n allInputs,\n cache\n )\n }\n\n // Process the WHERE clause if it exists\n if (query.where && query.where.length > 0) {\n // Compile all WHERE expressions\n const compiledWheres = query.where.map((where) => compileExpression(where))\n\n // Apply each WHERE condition as a filter (they are ANDed together)\n for (const compiledWhere of compiledWheres) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return compiledWhere(namespacedRow)\n })\n )\n }\n }\n\n // Process functional WHERE clauses if they exist\n if (query.fnWhere && query.fnWhere.length > 0) {\n for (const fnWhere of query.fnWhere) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return fnWhere(namespacedRow)\n })\n )\n }\n }\n\n // Process the SELECT clause early - always create __select_results\n // This eliminates duplication and allows for future DISTINCT implementation\n if (query.fnSelect) {\n // Handle functional select - apply the function to transform the row\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults = query.fnSelect!(namespacedRow)\n return [\n key,\n {\n ...namespacedRow,\n __select_results: selectResults,\n },\n ] as [string, typeof namespacedRow & { __select_results: any }]\n })\n )\n } else if (query.select) {\n pipeline = processSelectToResults(pipeline, query.select, allInputs)\n } else {\n // If no SELECT clause, create __select_results with the main table data\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults =\n !query.join && !query.groupBy\n ? namespacedRow[mainTableAlias]\n : namespacedRow\n\n return [\n key,\n {\n ...namespacedRow,\n __select_results: selectResults,\n },\n ] as [string, typeof namespacedRow & { __select_results: any }]\n })\n )\n }\n\n // Process the GROUP BY clause if it exists\n if (query.groupBy && query.groupBy.length > 0) {\n pipeline = processGroupBy(\n pipeline,\n query.groupBy,\n query.having,\n query.select,\n query.fnHaving\n )\n } else if (query.select) {\n // Check if SELECT contains aggregates but no GROUP BY (implicit single-group aggregation)\n const hasAggregates = Object.values(query.select).some(\n (expr) => expr.type === `agg`\n )\n if (hasAggregates) {\n // Handle implicit single-group aggregation\n pipeline = processGroupBy(\n pipeline,\n [], // Empty group by means single group\n query.having,\n query.select,\n query.fnHaving\n )\n }\n }\n\n // Process the HAVING clause if it exists (only applies after GROUP BY)\n if (query.having && (!query.groupBy || query.groupBy.length === 0)) {\n // Check if we have aggregates in SELECT that would trigger implicit grouping\n const hasAggregates = query.select\n ? Object.values(query.select).some((expr) => expr.type === `agg`)\n : false\n\n if (!hasAggregates) {\n throw new Error(`HAVING clause requires GROUP BY clause`)\n }\n }\n\n // Process functional HAVING clauses outside of GROUP BY (treat as additional WHERE filters)\n if (\n query.fnHaving &&\n query.fnHaving.length > 0 &&\n (!query.groupBy || query.groupBy.length === 0)\n ) {\n // If there's no GROUP BY but there are fnHaving clauses, apply them as filters\n for (const fnHaving of query.fnHaving) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n // Process orderBy parameter if it exists\n if (query.orderBy && query.orderBy.length > 0) {\n const orderedPipeline = processOrderBy(\n pipeline,\n query.orderBy,\n query.limit,\n query.offset\n )\n\n // Final step: extract the __select_results and include orderBy index\n const resultPipeline = orderedPipeline.pipe(\n map(([key, [row, orderByIndex]]) => {\n // Extract the final results from __select_results and include orderBy index\n const finalResults = (row as any).__select_results\n return [key, [finalResults, orderByIndex]] as [unknown, [any, string]]\n })\n )\n\n const result = resultPipeline\n // Cache the result before returning\n cache.set(query, result)\n return result\n } else if (query.limit !== undefined || query.offset !== undefined) {\n // If there's a limit or offset without orderBy, throw an error\n throw new Error(\n `LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results`\n )\n }\n\n // Final step: extract the __select_results and return tuple format (no orderBy)\n const resultPipeline: ResultStream = pipeline.pipe(\n map(([key, row]) => {\n // Extract the final results from __select_results and return [key, [results, undefined]]\n const finalResults = (row as any).__select_results\n return [key, [finalResults, undefined]] as [\n unknown,\n [any, string | undefined],\n ]\n })\n )\n\n const result = resultPipeline\n // Cache the result before returning\n cache.set(query, result)\n return result\n}\n\n/**\n * Processes the FROM clause to extract the main table alias and input stream\n */\nfunction processFrom(\n from: CollectionRef | QueryRef,\n allInputs: Record<string, KeyedStream>,\n cache: QueryCache\n): { alias: string; input: KeyedStream } {\n switch (from.type) {\n case `collectionRef`: {\n const input = allInputs[from.collection.id]\n if (!input) {\n throw new Error(\n `Input for collection \"${from.collection.id}\" not found in inputs map`\n )\n }\n return { alias: from.alias, input }\n }\n case `queryRef`: {\n // Recursively compile the sub-query with cache\n const subQueryInput = compileQuery(from.query, allInputs, cache)\n\n // Subqueries may return [key, [value, orderByIndex]] (with ORDER BY) or [key, value] (without ORDER BY)\n // We need to extract just the value for use in parent queries\n const extractedInput = subQueryInput.pipe(\n map((data: any) => {\n const [key, [value, _orderByIndex]] = data\n return [key, value] as [unknown, any]\n })\n )\n\n return { alias: from.alias, input: extractedInput }\n }\n default:\n throw new Error(`Unsupported FROM type: ${(from as any).type}`)\n }\n}\n"],"names":["map","processJoins","compileExpression","filter","processSelectToResults","processGroupBy","processOrderBy","resultPipeline","result"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { QueryIR } from '../ir.js';
2
+ import { KeyedStream, ResultStream } from '../../types.js';
3
+ /**
4
+ * Cache for compiled subqueries to avoid duplicate compilation
5
+ */
6
+ type QueryCache = WeakMap<QueryIR, ResultStream>;
7
+ /**
8
+ * Compiles a query2 IR into a D2 pipeline
9
+ * @param query The query IR to compile
10
+ * @param inputs Mapping of collection names to input streams
11
+ * @param cache Optional cache for compiled subqueries (used internally for recursion)
12
+ * @returns A stream builder representing the compiled query
13
+ */
14
+ export declare function compileQuery(query: QueryIR, inputs: Record<string, KeyedStream>, cache?: QueryCache): ResultStream;
15
+ export {};
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const d2mini = require("@electric-sql/d2mini");
4
+ const evaluators = require("./evaluators.cjs");
5
+ const index = require("./index.cjs");
6
+ function processJoins(pipeline, joinClauses, tables, mainTableAlias, allInputs, cache) {
7
+ let resultPipeline = pipeline;
8
+ for (const joinClause of joinClauses) {
9
+ resultPipeline = processJoin(
10
+ resultPipeline,
11
+ joinClause,
12
+ tables,
13
+ mainTableAlias,
14
+ allInputs,
15
+ cache
16
+ );
17
+ }
18
+ return resultPipeline;
19
+ }
20
+ function processJoin(pipeline, joinClause, tables, mainTableAlias, allInputs, cache) {
21
+ const { alias: joinedTableAlias, input: joinedInput } = processJoinSource(
22
+ joinClause.from,
23
+ allInputs,
24
+ cache
25
+ );
26
+ tables[joinedTableAlias] = joinedInput;
27
+ const joinType = joinClause.type === `cross` ? `inner` : joinClause.type === `outer` ? `full` : joinClause.type;
28
+ const compiledLeftExpr = evaluators.compileExpression(joinClause.left);
29
+ const compiledRightExpr = evaluators.compileExpression(joinClause.right);
30
+ const mainPipeline = pipeline.pipe(
31
+ d2mini.map(([currentKey, namespacedRow]) => {
32
+ const leftKey = compiledLeftExpr(namespacedRow);
33
+ return [leftKey, [currentKey, namespacedRow]];
34
+ })
35
+ );
36
+ const joinedPipeline = joinedInput.pipe(
37
+ d2mini.map(([currentKey, row]) => {
38
+ const namespacedRow = { [joinedTableAlias]: row };
39
+ const rightKey = compiledRightExpr(namespacedRow);
40
+ return [rightKey, [currentKey, namespacedRow]];
41
+ })
42
+ );
43
+ if (![`inner`, `left`, `right`, `full`].includes(joinType)) {
44
+ throw new Error(`Unsupported join type: ${joinClause.type}`);
45
+ }
46
+ return mainPipeline.pipe(
47
+ d2mini.join(joinedPipeline, joinType),
48
+ d2mini.consolidate(),
49
+ processJoinResults(joinClause.type)
50
+ );
51
+ }
52
+ function processJoinSource(from, allInputs, cache) {
53
+ switch (from.type) {
54
+ case `collectionRef`: {
55
+ const input = allInputs[from.collection.id];
56
+ if (!input) {
57
+ throw new Error(
58
+ `Input for collection "${from.collection.id}" not found in inputs map`
59
+ );
60
+ }
61
+ return { alias: from.alias, input };
62
+ }
63
+ case `queryRef`: {
64
+ const subQueryInput = index.compileQuery(from.query, allInputs, cache);
65
+ const extractedInput = subQueryInput.pipe(
66
+ d2mini.map((data) => {
67
+ const [key, [value, _orderByIndex]] = data;
68
+ return [key, value];
69
+ })
70
+ );
71
+ return { alias: from.alias, input: extractedInput };
72
+ }
73
+ default:
74
+ throw new Error(`Unsupported join source type: ${from.type}`);
75
+ }
76
+ }
77
+ function processJoinResults(joinType) {
78
+ return function(pipeline) {
79
+ return pipeline.pipe(
80
+ // Process the join result and handle nulls
81
+ d2mini.filter((result) => {
82
+ const [_key, [main, joined]] = result;
83
+ const mainNamespacedRow = main == null ? void 0 : main[1];
84
+ const joinedNamespacedRow = joined == null ? void 0 : joined[1];
85
+ if (joinType === `inner`) {
86
+ return !!(mainNamespacedRow && joinedNamespacedRow);
87
+ }
88
+ if (joinType === `left`) {
89
+ return !!mainNamespacedRow;
90
+ }
91
+ if (joinType === `right`) {
92
+ return !!joinedNamespacedRow;
93
+ }
94
+ return true;
95
+ }),
96
+ d2mini.map((result) => {
97
+ const [_key, [main, joined]] = result;
98
+ const mainKey = main == null ? void 0 : main[0];
99
+ const mainNamespacedRow = main == null ? void 0 : main[1];
100
+ const joinedKey = joined == null ? void 0 : joined[0];
101
+ const joinedNamespacedRow = joined == null ? void 0 : joined[1];
102
+ const mergedNamespacedRow = {};
103
+ if (mainNamespacedRow) {
104
+ Object.assign(mergedNamespacedRow, mainNamespacedRow);
105
+ }
106
+ if (joinedNamespacedRow) {
107
+ Object.assign(mergedNamespacedRow, joinedNamespacedRow);
108
+ }
109
+ const resultKey = `[${mainKey},${joinedKey}]`;
110
+ return [resultKey, mergedNamespacedRow];
111
+ })
112
+ );
113
+ };
114
+ }
115
+ exports.processJoins = processJoins;
116
+ //# sourceMappingURL=joins.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"joins.cjs","sources":["../../../../src/query/compiler/joins.ts"],"sourcesContent":["import {\n consolidate,\n filter,\n join as joinOperator,\n map,\n} from \"@electric-sql/d2mini\"\nimport { compileExpression } from \"./evaluators.js\"\nimport { compileQuery } from \"./index.js\"\nimport type { IStreamBuilder, JoinType } from \"@electric-sql/d2mini\"\nimport type { CollectionRef, JoinClause, QueryIR, QueryRef } from \"../ir.js\"\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n NamespacedRow,\n ResultStream,\n} from \"../../types.js\"\n\n/**\n * Cache for compiled subqueries to avoid duplicate compilation\n */\ntype QueryCache = WeakMap<QueryIR, ResultStream>\n\n/**\n * Processes all join clauses in a query\n */\nexport function processJoins(\n pipeline: NamespacedAndKeyedStream,\n joinClauses: Array<JoinClause>,\n tables: Record<string, KeyedStream>,\n mainTableAlias: string,\n allInputs: Record<string, KeyedStream>,\n cache: QueryCache\n): NamespacedAndKeyedStream {\n let resultPipeline = pipeline\n\n for (const joinClause of joinClauses) {\n resultPipeline = processJoin(\n resultPipeline,\n joinClause,\n tables,\n mainTableAlias,\n allInputs,\n cache\n )\n }\n\n return resultPipeline\n}\n\n/**\n * Processes a single join clause\n */\nfunction processJoin(\n pipeline: NamespacedAndKeyedStream,\n joinClause: JoinClause,\n tables: Record<string, KeyedStream>,\n mainTableAlias: string,\n allInputs: Record<string, KeyedStream>,\n cache: QueryCache\n): NamespacedAndKeyedStream {\n // Get the joined table alias and input stream\n const { alias: joinedTableAlias, input: joinedInput } = processJoinSource(\n joinClause.from,\n allInputs,\n cache\n )\n\n // Add the joined table to the tables map\n tables[joinedTableAlias] = joinedInput\n\n // Convert join type to D2 join type\n const joinType: JoinType =\n joinClause.type === `cross`\n ? `inner`\n : joinClause.type === `outer`\n ? `full`\n : (joinClause.type as JoinType)\n\n // Pre-compile the join expressions\n const compiledLeftExpr = compileExpression(joinClause.left)\n const compiledRightExpr = compileExpression(joinClause.right)\n\n // Prepare the main pipeline for joining\n const mainPipeline = pipeline.pipe(\n map(([currentKey, namespacedRow]) => {\n // Extract the join key from the left side of the join condition\n const leftKey = compiledLeftExpr(namespacedRow)\n\n // Return [joinKey, [originalKey, namespacedRow]]\n return [leftKey, [currentKey, namespacedRow]] as [\n unknown,\n [string, typeof namespacedRow],\n ]\n })\n )\n\n // Prepare the joined pipeline\n const joinedPipeline = joinedInput.pipe(\n map(([currentKey, row]) => {\n // Wrap the row in a namespaced structure\n const namespacedRow: NamespacedRow = { [joinedTableAlias]: row }\n\n // Extract the join key from the right side of the join condition\n const rightKey = compiledRightExpr(namespacedRow)\n\n // Return [joinKey, [originalKey, namespacedRow]]\n return [rightKey, [currentKey, namespacedRow]] as [\n unknown,\n [string, typeof namespacedRow],\n ]\n })\n )\n\n // Apply the join operation\n if (![`inner`, `left`, `right`, `full`].includes(joinType)) {\n throw new Error(`Unsupported join type: ${joinClause.type}`)\n }\n return mainPipeline.pipe(\n joinOperator(joinedPipeline, joinType),\n consolidate(),\n processJoinResults(joinClause.type)\n )\n}\n\n/**\n * Processes the join source (collection or sub-query)\n */\nfunction processJoinSource(\n from: CollectionRef | QueryRef,\n allInputs: Record<string, KeyedStream>,\n cache: QueryCache\n): { alias: string; input: KeyedStream } {\n switch (from.type) {\n case `collectionRef`: {\n const input = allInputs[from.collection.id]\n if (!input) {\n throw new Error(\n `Input for collection \"${from.collection.id}\" not found in inputs map`\n )\n }\n return { alias: from.alias, input }\n }\n case `queryRef`: {\n // Recursively compile the sub-query with cache\n const subQueryInput = compileQuery(from.query, allInputs, cache)\n\n // Subqueries may return [key, [value, orderByIndex]] (with ORDER BY) or [key, value] (without ORDER BY)\n // We need to extract just the value for use in parent queries\n const extractedInput = subQueryInput.pipe(\n map((data: any) => {\n const [key, [value, _orderByIndex]] = data\n return [key, value] as [unknown, any]\n })\n )\n\n return { alias: from.alias, input: extractedInput as KeyedStream }\n }\n default:\n throw new Error(`Unsupported join source type: ${(from as any).type}`)\n }\n}\n\n/**\n * Processes the results of a join operation\n */\nfunction processJoinResults(joinType: string) {\n return function (\n pipeline: IStreamBuilder<\n [\n key: string,\n [\n [string, NamespacedRow] | undefined,\n [string, NamespacedRow] | undefined,\n ],\n ]\n >\n ): NamespacedAndKeyedStream {\n return pipeline.pipe(\n // Process the join result and handle nulls\n filter((result) => {\n const [_key, [main, joined]] = result\n const mainNamespacedRow = main?.[1]\n const joinedNamespacedRow = joined?.[1]\n\n // Handle different join types\n if (joinType === `inner`) {\n return !!(mainNamespacedRow && joinedNamespacedRow)\n }\n\n if (joinType === `left`) {\n return !!mainNamespacedRow\n }\n\n if (joinType === `right`) {\n return !!joinedNamespacedRow\n }\n\n // For full joins, always include\n return true\n }),\n map((result) => {\n const [_key, [main, joined]] = result\n const mainKey = main?.[0]\n const mainNamespacedRow = main?.[1]\n const joinedKey = joined?.[0]\n const joinedNamespacedRow = joined?.[1]\n\n // Merge the namespaced rows\n const mergedNamespacedRow: NamespacedRow = {}\n\n // Add main row data if it exists\n if (mainNamespacedRow) {\n Object.assign(mergedNamespacedRow, mainNamespacedRow)\n }\n\n // Add joined row data if it exists\n if (joinedNamespacedRow) {\n Object.assign(mergedNamespacedRow, joinedNamespacedRow)\n }\n\n // We create a composite key that combines the main and joined keys\n const resultKey = `[${mainKey},${joinedKey}]`\n\n return [resultKey, mergedNamespacedRow] as [string, NamespacedRow]\n })\n )\n }\n}\n"],"names":["compileExpression","map","joinOperator","consolidate","compileQuery","filter"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { JoinClause, QueryIR } from '../ir.js';
2
+ import { KeyedStream, NamespacedAndKeyedStream, ResultStream } from '../../types.js';
3
+ /**
4
+ * Cache for compiled subqueries to avoid duplicate compilation
5
+ */
6
+ type QueryCache = WeakMap<QueryIR, ResultStream>;
7
+ /**
8
+ * Processes all join clauses in a query
9
+ */
10
+ export declare function processJoins(pipeline: NamespacedAndKeyedStream, joinClauses: Array<JoinClause>, tables: Record<string, KeyedStream>, mainTableAlias: string, allInputs: Record<string, KeyedStream>, cache: QueryCache): NamespacedAndKeyedStream;
11
+ export {};
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const d2mini = require("@electric-sql/d2mini");
4
+ const evaluators = require("./evaluators.cjs");
5
+ function processOrderBy(pipeline, orderByClause, limit, offset) {
6
+ const compiledOrderBy = orderByClause.map((clause) => ({
7
+ compiledExpression: evaluators.compileExpression(clause.expression),
8
+ direction: clause.direction
9
+ }));
10
+ const valueExtractor = (row) => {
11
+ const orderByContext = { ...row };
12
+ if (row.__select_results) {
13
+ Object.assign(orderByContext, row.__select_results);
14
+ }
15
+ if (orderByClause.length > 1) {
16
+ return compiledOrderBy.map(
17
+ (compiled) => compiled.compiledExpression(orderByContext)
18
+ );
19
+ } else if (orderByClause.length === 1) {
20
+ const compiled = compiledOrderBy[0];
21
+ return compiled.compiledExpression(orderByContext);
22
+ }
23
+ return null;
24
+ };
25
+ const ascComparator = (a, b) => {
26
+ if (a == null && b == null) return 0;
27
+ if (a == null) return -1;
28
+ if (b == null) return 1;
29
+ if (typeof a === `string` && typeof b === `string`) {
30
+ return a.localeCompare(b);
31
+ }
32
+ if (Array.isArray(a) && Array.isArray(b)) {
33
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
34
+ const result = ascComparator(a[i], b[i]);
35
+ if (result !== 0) {
36
+ return result;
37
+ }
38
+ }
39
+ return a.length - b.length;
40
+ }
41
+ if (a instanceof Date && b instanceof Date) {
42
+ return a.getTime() - b.getTime();
43
+ }
44
+ const bothObjects = typeof a === `object` && typeof b === `object`;
45
+ const notNull = a !== null && b !== null;
46
+ if (bothObjects && notNull) {
47
+ return a.toString().localeCompare(b.toString());
48
+ }
49
+ if (a < b) return -1;
50
+ if (a > b) return 1;
51
+ return 0;
52
+ };
53
+ const descComparator = (a, b) => {
54
+ return ascComparator(b, a);
55
+ };
56
+ const makeComparator = () => {
57
+ return (a, b) => {
58
+ if (orderByClause.length > 1) {
59
+ const arrayA = a;
60
+ const arrayB = b;
61
+ for (let i = 0; i < orderByClause.length; i++) {
62
+ const direction = orderByClause[i].direction;
63
+ const compareFn = direction === `desc` ? descComparator : ascComparator;
64
+ const result = compareFn(arrayA[i], arrayB[i]);
65
+ if (result !== 0) {
66
+ return result;
67
+ }
68
+ }
69
+ return arrayA.length - arrayB.length;
70
+ }
71
+ if (orderByClause.length === 1) {
72
+ const direction = orderByClause[0].direction;
73
+ return direction === `desc` ? descComparator(a, b) : ascComparator(a, b);
74
+ }
75
+ return ascComparator(a, b);
76
+ };
77
+ };
78
+ const comparator = makeComparator();
79
+ return pipeline.pipe(
80
+ d2mini.orderByWithFractionalIndex(valueExtractor, {
81
+ limit,
82
+ offset,
83
+ comparator
84
+ })
85
+ // orderByWithFractionalIndex returns [key, [value, index]] - we keep this format
86
+ );
87
+ }
88
+ exports.processOrderBy = processOrderBy;
89
+ //# sourceMappingURL=order-by.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order-by.cjs","sources":["../../../../src/query/compiler/order-by.ts"],"sourcesContent":["import { orderByWithFractionalIndex } from \"@electric-sql/d2mini\"\nimport { compileExpression } from \"./evaluators.js\"\nimport type { OrderByClause } from \"../ir.js\"\nimport type { NamespacedAndKeyedStream, NamespacedRow } from \"../../types.js\"\nimport type { IStreamBuilder, KeyValue } from \"@electric-sql/d2mini\"\n\n/**\n * Processes the ORDER BY clause\n * Works with the new structure that has both namespaced row data and __select_results\n * Always uses fractional indexing and adds the index as __ordering_index to the result\n */\nexport function processOrderBy(\n pipeline: NamespacedAndKeyedStream,\n orderByClause: Array<OrderByClause>,\n limit?: number,\n offset?: number\n): IStreamBuilder<KeyValue<unknown, [NamespacedRow, string]>> {\n // Pre-compile all order by expressions\n const compiledOrderBy = orderByClause.map((clause) => ({\n compiledExpression: compileExpression(clause.expression),\n direction: clause.direction,\n }))\n\n // Create a value extractor function for the orderBy operator\n const valueExtractor = (row: NamespacedRow & { __select_results?: any }) => {\n // For ORDER BY expressions, we need to provide access to both:\n // 1. The original namespaced row data (for direct table column references)\n // 2. The __select_results (for SELECT alias references)\n\n // Create a merged context for expression evaluation\n const orderByContext = { ...row }\n\n // If there are select results, merge them at the top level for alias access\n if (row.__select_results) {\n // Add select results as top-level properties for alias access\n Object.assign(orderByContext, row.__select_results)\n }\n\n if (orderByClause.length > 1) {\n // For multiple orderBy columns, create a composite key\n return compiledOrderBy.map((compiled) =>\n compiled.compiledExpression(orderByContext)\n )\n } else if (orderByClause.length === 1) {\n // For a single orderBy column, use the value directly\n const compiled = compiledOrderBy[0]!\n return compiled.compiledExpression(orderByContext)\n }\n\n // Default case - no ordering\n return null\n }\n\n // Create comparator functions\n const ascComparator = (a: any, b: any): number => {\n // Handle null/undefined\n if (a == null && b == null) return 0\n if (a == null) return -1\n if (b == null) return 1\n\n // if a and b are both strings, compare them based on locale\n if (typeof a === `string` && typeof b === `string`) {\n return a.localeCompare(b)\n }\n\n // if a and b are both arrays, compare them element by element\n if (Array.isArray(a) && Array.isArray(b)) {\n for (let i = 0; i < Math.min(a.length, b.length); i++) {\n const result = ascComparator(a[i], b[i])\n if (result !== 0) {\n return result\n }\n }\n // All elements are equal up to the minimum length\n return a.length - b.length\n }\n\n // If both are dates, compare them\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime()\n }\n\n // If at least one of the values is an object, convert to strings\n const bothObjects = typeof a === `object` && typeof b === `object`\n const notNull = a !== null && b !== null\n if (bothObjects && notNull) {\n return a.toString().localeCompare(b.toString())\n }\n\n if (a < b) return -1\n if (a > b) return 1\n return 0\n }\n\n const descComparator = (a: unknown, b: unknown): number => {\n return ascComparator(b, a)\n }\n\n // Create a multi-property comparator that respects the order and direction of each property\n const makeComparator = () => {\n return (a: unknown, b: unknown) => {\n // If we're comparing arrays (multiple properties), compare each property in order\n if (orderByClause.length > 1) {\n const arrayA = a as Array<unknown>\n const arrayB = b as Array<unknown>\n for (let i = 0; i < orderByClause.length; i++) {\n const direction = orderByClause[i]!.direction\n const compareFn =\n direction === `desc` ? descComparator : ascComparator\n const result = compareFn(arrayA[i], arrayB[i])\n if (result !== 0) {\n return result\n }\n }\n return arrayA.length - arrayB.length\n }\n\n // Single property comparison\n if (orderByClause.length === 1) {\n const direction = orderByClause[0]!.direction\n return direction === `desc` ? descComparator(a, b) : ascComparator(a, b)\n }\n\n return ascComparator(a, b)\n }\n }\n\n const comparator = makeComparator()\n\n // Use fractional indexing and return the tuple [value, index]\n return pipeline.pipe(\n orderByWithFractionalIndex(valueExtractor, {\n limit,\n offset,\n comparator,\n })\n // orderByWithFractionalIndex returns [key, [value, index]] - we keep this format\n )\n}\n"],"names":["compileExpression","orderByWithFractionalIndex"],"mappings":";;;;AAWO,SAAS,eACd,UACA,eACA,OACA,QAC4D;AAE5D,QAAM,kBAAkB,cAAc,IAAI,CAAC,YAAY;AAAA,IACrD,oBAAoBA,WAAAA,kBAAkB,OAAO,UAAU;AAAA,IACvD,WAAW,OAAO;AAAA,EAAA,EAClB;AAGI,QAAA,iBAAiB,CAAC,QAAoD;AAMpE,UAAA,iBAAiB,EAAE,GAAG,IAAI;AAGhC,QAAI,IAAI,kBAAkB;AAEjB,aAAA,OAAO,gBAAgB,IAAI,gBAAgB;AAAA,IAAA;AAGhD,QAAA,cAAc,SAAS,GAAG;AAE5B,aAAO,gBAAgB;AAAA,QAAI,CAAC,aAC1B,SAAS,mBAAmB,cAAc;AAAA,MAC5C;AAAA,IAAA,WACS,cAAc,WAAW,GAAG;AAE/B,YAAA,WAAW,gBAAgB,CAAC;AAC3B,aAAA,SAAS,mBAAmB,cAAc;AAAA,IAAA;AAI5C,WAAA;AAAA,EACT;AAGM,QAAA,gBAAgB,CAAC,GAAQ,MAAmB;AAEhD,QAAI,KAAK,QAAQ,KAAK,KAAa,QAAA;AAC/B,QAAA,KAAK,KAAa,QAAA;AAClB,QAAA,KAAK,KAAa,QAAA;AAGtB,QAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;AAC3C,aAAA,EAAE,cAAc,CAAC;AAAA,IAAA;AAI1B,QAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,GAAG;AAC/B,eAAA,IAAI,GAAG,IAAI,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK;AACrD,cAAM,SAAS,cAAc,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;AACvC,YAAI,WAAW,GAAG;AACT,iBAAA;AAAA,QAAA;AAAA,MACT;AAGK,aAAA,EAAE,SAAS,EAAE;AAAA,IAAA;AAIlB,QAAA,aAAa,QAAQ,aAAa,MAAM;AAC1C,aAAO,EAAE,YAAY,EAAE,QAAQ;AAAA,IAAA;AAIjC,UAAM,cAAc,OAAO,MAAM,YAAY,OAAO,MAAM;AACpD,UAAA,UAAU,MAAM,QAAQ,MAAM;AACpC,QAAI,eAAe,SAAS;AAC1B,aAAO,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU;AAAA,IAAA;AAG5C,QAAA,IAAI,EAAU,QAAA;AACd,QAAA,IAAI,EAAU,QAAA;AACX,WAAA;AAAA,EACT;AAEM,QAAA,iBAAiB,CAAC,GAAY,MAAuB;AAClD,WAAA,cAAc,GAAG,CAAC;AAAA,EAC3B;AAGA,QAAM,iBAAiB,MAAM;AACpB,WAAA,CAAC,GAAY,MAAe;AAE7B,UAAA,cAAc,SAAS,GAAG;AAC5B,cAAM,SAAS;AACf,cAAM,SAAS;AACf,iBAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AACvC,gBAAA,YAAY,cAAc,CAAC,EAAG;AAC9B,gBAAA,YACJ,cAAc,SAAS,iBAAiB;AAC1C,gBAAM,SAAS,UAAU,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AAC7C,cAAI,WAAW,GAAG;AACT,mBAAA;AAAA,UAAA;AAAA,QACT;AAEK,eAAA,OAAO,SAAS,OAAO;AAAA,MAAA;AAI5B,UAAA,cAAc,WAAW,GAAG;AACxB,cAAA,YAAY,cAAc,CAAC,EAAG;AAC7B,eAAA,cAAc,SAAS,eAAe,GAAG,CAAC,IAAI,cAAc,GAAG,CAAC;AAAA,MAAA;AAGlE,aAAA,cAAc,GAAG,CAAC;AAAA,IAC3B;AAAA,EACF;AAEA,QAAM,aAAa,eAAe;AAGlC,SAAO,SAAS;AAAA,IACdC,OAAAA,2BAA2B,gBAAgB;AAAA,MACzC;AAAA,MACA;AAAA,MACA;AAAA,IACD,CAAA;AAAA;AAAA,EAEH;AACF;;"}
@@ -0,0 +1,9 @@
1
+ import { OrderByClause } from '../ir.js';
2
+ import { NamespacedAndKeyedStream, NamespacedRow } from '../../types.js';
3
+ import { IStreamBuilder, KeyValue } from '@electric-sql/d2mini';
4
+ /**
5
+ * Processes the ORDER BY clause
6
+ * Works with the new structure that has both namespaced row data and __select_results
7
+ * Always uses fractional indexing and adds the index as __ordering_index to the result
8
+ */
9
+ export declare function processOrderBy(pipeline: NamespacedAndKeyedStream, orderByClause: Array<OrderByClause>, limit?: number, offset?: number): IStreamBuilder<KeyValue<unknown, [NamespacedRow, string]>>;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const d2mini = require("@electric-sql/d2mini");
4
+ const evaluators = require("./evaluators.cjs");
5
+ function processSelectToResults(pipeline, select, _allInputs) {
6
+ const compiledSelect = [];
7
+ const spreadAliases = [];
8
+ for (const [alias, expression] of Object.entries(select)) {
9
+ if (alias.startsWith(`__SPREAD_SENTINEL__`)) {
10
+ const tableAlias = alias.replace(`__SPREAD_SENTINEL__`, ``);
11
+ spreadAliases.push(tableAlias);
12
+ } else {
13
+ if (isAggregateExpression(expression)) {
14
+ compiledSelect.push({
15
+ alias,
16
+ compiledExpression: () => null
17
+ // Placeholder - will be handled by GROUP BY
18
+ });
19
+ } else {
20
+ compiledSelect.push({
21
+ alias,
22
+ compiledExpression: evaluators.compileExpression(expression)
23
+ });
24
+ }
25
+ }
26
+ }
27
+ return pipeline.pipe(
28
+ d2mini.map(([key, namespacedRow]) => {
29
+ const selectResults = {};
30
+ for (const tableAlias of spreadAliases) {
31
+ const tableData = namespacedRow[tableAlias];
32
+ if (tableData && typeof tableData === `object`) {
33
+ for (const [fieldName, fieldValue] of Object.entries(tableData)) {
34
+ if (!(fieldName in selectResults)) {
35
+ selectResults[fieldName] = fieldValue;
36
+ }
37
+ }
38
+ }
39
+ }
40
+ for (const { alias, compiledExpression } of compiledSelect) {
41
+ selectResults[alias] = compiledExpression(namespacedRow);
42
+ }
43
+ return [
44
+ key,
45
+ {
46
+ ...namespacedRow,
47
+ __select_results: selectResults
48
+ }
49
+ ];
50
+ })
51
+ );
52
+ }
53
+ function isAggregateExpression(expr) {
54
+ return expr.type === `agg`;
55
+ }
56
+ exports.processSelectToResults = processSelectToResults;
57
+ //# sourceMappingURL=select.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.cjs","sources":["../../../../src/query/compiler/select.ts"],"sourcesContent":["import { map } from \"@electric-sql/d2mini\"\nimport { compileExpression } from \"./evaluators.js\"\nimport type { Aggregate, BasicExpression, Select } from \"../ir.js\"\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n NamespacedRow,\n} from \"../../types.js\"\n\n/**\n * Processes the SELECT clause and places results in __select_results\n * while preserving the original namespaced row for ORDER BY access\n */\nexport function processSelectToResults(\n pipeline: NamespacedAndKeyedStream,\n select: Select,\n _allInputs: Record<string, KeyedStream>\n): NamespacedAndKeyedStream {\n // Pre-compile all select expressions\n const compiledSelect: Array<{\n alias: string\n compiledExpression: (row: NamespacedRow) => any\n }> = []\n const spreadAliases: Array<string> = []\n\n for (const [alias, expression] of Object.entries(select)) {\n if (alias.startsWith(`__SPREAD_SENTINEL__`)) {\n // Extract the table alias from the sentinel key\n const tableAlias = alias.replace(`__SPREAD_SENTINEL__`, ``)\n spreadAliases.push(tableAlias)\n } else {\n if (isAggregateExpression(expression)) {\n // For aggregates, we'll store the expression info for GROUP BY processing\n // but still compile a placeholder that will be replaced later\n compiledSelect.push({\n alias,\n compiledExpression: () => null, // Placeholder - will be handled by GROUP BY\n })\n } else {\n compiledSelect.push({\n alias,\n compiledExpression: compileExpression(expression as BasicExpression),\n })\n }\n }\n }\n\n return pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults: Record<string, any> = {}\n\n // First pass: spread table data for any spread sentinels\n for (const tableAlias of spreadAliases) {\n const tableData = namespacedRow[tableAlias]\n if (tableData && typeof tableData === `object`) {\n // Spread the table data into the result, but don't overwrite explicit fields\n for (const [fieldName, fieldValue] of Object.entries(tableData)) {\n if (!(fieldName in selectResults)) {\n selectResults[fieldName] = fieldValue\n }\n }\n }\n }\n\n // Second pass: evaluate all compiled select expressions (non-aggregates)\n for (const { alias, compiledExpression } of compiledSelect) {\n selectResults[alias] = compiledExpression(namespacedRow)\n }\n\n // Return the namespaced row with __select_results added\n return [\n key,\n {\n ...namespacedRow,\n __select_results: selectResults,\n },\n ] as [\n string,\n typeof namespacedRow & { __select_results: typeof selectResults },\n ]\n })\n )\n}\n\n/**\n * Processes the SELECT clause (legacy function - kept for compatibility)\n */\nexport function processSelect(\n pipeline: NamespacedAndKeyedStream,\n select: Select,\n _allInputs: Record<string, KeyedStream>\n): KeyedStream {\n // Pre-compile all select expressions\n const compiledSelect: Array<{\n alias: string\n compiledExpression: (row: NamespacedRow) => any\n }> = []\n const spreadAliases: Array<string> = []\n\n for (const [alias, expression] of Object.entries(select)) {\n if (alias.startsWith(`__SPREAD_SENTINEL__`)) {\n // Extract the table alias from the sentinel key\n const tableAlias = alias.replace(`__SPREAD_SENTINEL__`, ``)\n spreadAliases.push(tableAlias)\n } else {\n if (isAggregateExpression(expression)) {\n // Aggregates should be handled by GROUP BY processing, not here\n throw new Error(\n `Aggregate expressions in SELECT clause should be handled by GROUP BY processing`\n )\n }\n compiledSelect.push({\n alias,\n compiledExpression: compileExpression(expression as BasicExpression),\n })\n }\n }\n\n return pipeline.pipe(\n map(([key, namespacedRow]) => {\n const result: Record<string, any> = {}\n\n // First pass: spread table data for any spread sentinels\n for (const tableAlias of spreadAliases) {\n const tableData = namespacedRow[tableAlias]\n if (tableData && typeof tableData === `object`) {\n // Spread the table data into the result, but don't overwrite explicit fields\n for (const [fieldName, fieldValue] of Object.entries(tableData)) {\n if (!(fieldName in result)) {\n result[fieldName] = fieldValue\n }\n }\n }\n }\n\n // Second pass: evaluate all compiled select expressions\n for (const { alias, compiledExpression } of compiledSelect) {\n result[alias] = compiledExpression(namespacedRow)\n }\n\n return [key, result] as [string, typeof result]\n })\n )\n}\n\n/**\n * Helper function to check if an expression is an aggregate\n */\nfunction isAggregateExpression(\n expr: BasicExpression | Aggregate\n): expr is Aggregate {\n return expr.type === `agg`\n}\n\n/**\n * Processes a single argument in a function context\n */\nexport function processArgument(\n arg: BasicExpression | Aggregate,\n namespacedRow: NamespacedRow\n): any {\n if (isAggregateExpression(arg)) {\n throw new Error(\n `Aggregate expressions are not supported in this context. Use GROUP BY clause for aggregates.`\n )\n }\n\n // Pre-compile the expression and evaluate immediately\n const compiledExpression = compileExpression(arg)\n const value = compiledExpression(namespacedRow)\n\n return value\n}\n"],"names":["compileExpression","map"],"mappings":";;;;AAagB,SAAA,uBACd,UACA,QACA,YAC0B;AAE1B,QAAM,iBAGD,CAAC;AACN,QAAM,gBAA+B,CAAC;AAEtC,aAAW,CAAC,OAAO,UAAU,KAAK,OAAO,QAAQ,MAAM,GAAG;AACpD,QAAA,MAAM,WAAW,qBAAqB,GAAG;AAE3C,YAAM,aAAa,MAAM,QAAQ,uBAAuB,EAAE;AAC1D,oBAAc,KAAK,UAAU;AAAA,IAAA,OACxB;AACD,UAAA,sBAAsB,UAAU,GAAG;AAGrC,uBAAe,KAAK;AAAA,UAClB;AAAA,UACA,oBAAoB,MAAM;AAAA;AAAA,QAAA,CAC3B;AAAA,MAAA,OACI;AACL,uBAAe,KAAK;AAAA,UAClB;AAAA,UACA,oBAAoBA,6BAAkB,UAA6B;AAAA,QAAA,CACpE;AAAA,MAAA;AAAA,IACH;AAAA,EACF;AAGF,SAAO,SAAS;AAAA,IACdC,OAAAA,IAAI,CAAC,CAAC,KAAK,aAAa,MAAM;AAC5B,YAAM,gBAAqC,CAAC;AAG5C,iBAAW,cAAc,eAAe;AAChC,cAAA,YAAY,cAAc,UAAU;AACtC,YAAA,aAAa,OAAO,cAAc,UAAU;AAE9C,qBAAW,CAAC,WAAW,UAAU,KAAK,OAAO,QAAQ,SAAS,GAAG;AAC3D,gBAAA,EAAE,aAAa,gBAAgB;AACjC,4BAAc,SAAS,IAAI;AAAA,YAAA;AAAA,UAC7B;AAAA,QACF;AAAA,MACF;AAIF,iBAAW,EAAE,OAAO,mBAAmB,KAAK,gBAAgB;AAC5C,sBAAA,KAAK,IAAI,mBAAmB,aAAa;AAAA,MAAA;AAIlD,aAAA;AAAA,QACL;AAAA,QACA;AAAA,UACE,GAAG;AAAA,UACH,kBAAkB;AAAA,QAAA;AAAA,MAEtB;AAAA,IAID,CAAA;AAAA,EACH;AACF;AAkEA,SAAS,sBACP,MACmB;AACnB,SAAO,KAAK,SAAS;AACvB;;"}
@@ -0,0 +1,15 @@
1
+ import { Aggregate, BasicExpression, Select } from '../ir.js';
2
+ import { KeyedStream, NamespacedAndKeyedStream, NamespacedRow } from '../../types.js';
3
+ /**
4
+ * Processes the SELECT clause and places results in __select_results
5
+ * while preserving the original namespaced row for ORDER BY access
6
+ */
7
+ export declare function processSelectToResults(pipeline: NamespacedAndKeyedStream, select: Select, _allInputs: Record<string, KeyedStream>): NamespacedAndKeyedStream;
8
+ /**
9
+ * Processes the SELECT clause (legacy function - kept for compatibility)
10
+ */
11
+ export declare function processSelect(pipeline: NamespacedAndKeyedStream, select: Select, _allInputs: Record<string, KeyedStream>): KeyedStream;
12
+ /**
13
+ * Processes a single argument in a function context
14
+ */
15
+ export declare function processArgument(arg: BasicExpression | Aggregate, namespacedRow: NamespacedRow): any;
@@ -1,5 +1,5 @@
1
- export * from './query-builder.js';
2
- export * from './compiled-query.js';
3
- export * from './pipeline-compiler.js';
4
- export * from './schema.js';
5
- export * from './types.js';
1
+ export { BaseQueryBuilder, Query, type InitialQueryBuilder, type QueryBuilder, type Context, type Source, type GetResult, } from './builder/index.js';
2
+ export { eq, gt, gte, lt, lte, and, or, not, inArray, like, ilike, upper, lower, length, concat, coalesce, add, count, avg, sum, min, max, } from './builder/functions.js';
3
+ export type { Ref } from './builder/types.js';
4
+ export { compileQuery } from './compiler/index.js';
5
+ export { createLiveQueryCollection, liveQueryCollectionOptions, type LiveQueryCollectionConfig, } from './live-query-collection.js';
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ class BaseExpression {
4
+ }
5
+ class CollectionRef extends BaseExpression {
6
+ constructor(collection, alias) {
7
+ super();
8
+ this.collection = collection;
9
+ this.alias = alias;
10
+ this.type = `collectionRef`;
11
+ }
12
+ }
13
+ class QueryRef extends BaseExpression {
14
+ constructor(query, alias) {
15
+ super();
16
+ this.query = query;
17
+ this.alias = alias;
18
+ this.type = `queryRef`;
19
+ }
20
+ }
21
+ class PropRef extends BaseExpression {
22
+ constructor(path) {
23
+ super();
24
+ this.path = path;
25
+ this.type = `ref`;
26
+ }
27
+ }
28
+ class Value extends BaseExpression {
29
+ constructor(value) {
30
+ super();
31
+ this.value = value;
32
+ this.type = `val`;
33
+ }
34
+ }
35
+ class Func extends BaseExpression {
36
+ constructor(name, args) {
37
+ super();
38
+ this.name = name;
39
+ this.args = args;
40
+ this.type = `func`;
41
+ }
42
+ }
43
+ class Aggregate extends BaseExpression {
44
+ constructor(name, args) {
45
+ super();
46
+ this.name = name;
47
+ this.args = args;
48
+ this.type = `agg`;
49
+ }
50
+ }
51
+ exports.Aggregate = Aggregate;
52
+ exports.CollectionRef = CollectionRef;
53
+ exports.Func = Func;
54
+ exports.PropRef = PropRef;
55
+ exports.QueryRef = QueryRef;
56
+ exports.Value = Value;
57
+ //# sourceMappingURL=ir.cjs.map