@tanstack/db 0.0.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 (154) hide show
  1. package/README.md +37 -0
  2. package/dist/cjs/SortedMap.cjs +140 -0
  3. package/dist/cjs/SortedMap.cjs.map +1 -0
  4. package/dist/cjs/SortedMap.d.cts +91 -0
  5. package/dist/cjs/collection.cjs +597 -0
  6. package/dist/cjs/collection.cjs.map +1 -0
  7. package/dist/cjs/collection.d.cts +176 -0
  8. package/dist/cjs/deferred.cjs +25 -0
  9. package/dist/cjs/deferred.cjs.map +1 -0
  10. package/dist/cjs/deferred.d.cts +20 -0
  11. package/dist/cjs/errors.cjs +10 -0
  12. package/dist/cjs/errors.cjs.map +1 -0
  13. package/dist/cjs/errors.d.cts +3 -0
  14. package/dist/cjs/index.cjs +33 -0
  15. package/dist/cjs/index.cjs.map +1 -0
  16. package/dist/cjs/index.d.cts +9 -0
  17. package/dist/cjs/proxy.cjs +654 -0
  18. package/dist/cjs/proxy.cjs.map +1 -0
  19. package/dist/cjs/proxy.d.cts +59 -0
  20. package/dist/cjs/query/compiled-query.cjs +162 -0
  21. package/dist/cjs/query/compiled-query.cjs.map +1 -0
  22. package/dist/cjs/query/compiled-query.d.cts +22 -0
  23. package/dist/cjs/query/evaluators.cjs +146 -0
  24. package/dist/cjs/query/evaluators.cjs.map +1 -0
  25. package/dist/cjs/query/evaluators.d.cts +9 -0
  26. package/dist/cjs/query/extractors.cjs +122 -0
  27. package/dist/cjs/query/extractors.cjs.map +1 -0
  28. package/dist/cjs/query/extractors.d.cts +22 -0
  29. package/dist/cjs/query/functions.cjs +152 -0
  30. package/dist/cjs/query/functions.cjs.map +1 -0
  31. package/dist/cjs/query/functions.d.cts +21 -0
  32. package/dist/cjs/query/group-by.cjs +91 -0
  33. package/dist/cjs/query/group-by.cjs.map +1 -0
  34. package/dist/cjs/query/group-by.d.cts +40 -0
  35. package/dist/cjs/query/index.d.cts +5 -0
  36. package/dist/cjs/query/joins.cjs +155 -0
  37. package/dist/cjs/query/joins.cjs.map +1 -0
  38. package/dist/cjs/query/joins.d.cts +14 -0
  39. package/dist/cjs/query/key-by.cjs +43 -0
  40. package/dist/cjs/query/key-by.cjs.map +1 -0
  41. package/dist/cjs/query/key-by.d.cts +3 -0
  42. package/dist/cjs/query/order-by.cjs +229 -0
  43. package/dist/cjs/query/order-by.cjs.map +1 -0
  44. package/dist/cjs/query/order-by.d.cts +3 -0
  45. package/dist/cjs/query/pipeline-compiler.cjs +94 -0
  46. package/dist/cjs/query/pipeline-compiler.cjs.map +1 -0
  47. package/dist/cjs/query/pipeline-compiler.d.cts +9 -0
  48. package/dist/cjs/query/query-builder.cjs +314 -0
  49. package/dist/cjs/query/query-builder.cjs.map +1 -0
  50. package/dist/cjs/query/query-builder.d.cts +219 -0
  51. package/dist/cjs/query/schema.d.cts +98 -0
  52. package/dist/cjs/query/select.cjs +107 -0
  53. package/dist/cjs/query/select.cjs.map +1 -0
  54. package/dist/cjs/query/select.d.cts +3 -0
  55. package/dist/cjs/query/types.d.cts +188 -0
  56. package/dist/cjs/query/utils.cjs +154 -0
  57. package/dist/cjs/query/utils.cjs.map +1 -0
  58. package/dist/cjs/query/utils.d.cts +37 -0
  59. package/dist/cjs/transactions.cjs +137 -0
  60. package/dist/cjs/transactions.cjs.map +1 -0
  61. package/dist/cjs/transactions.d.cts +27 -0
  62. package/dist/cjs/types.d.cts +94 -0
  63. package/dist/cjs/utils.cjs +17 -0
  64. package/dist/cjs/utils.cjs.map +1 -0
  65. package/dist/cjs/utils.d.cts +3 -0
  66. package/dist/esm/SortedMap.d.ts +91 -0
  67. package/dist/esm/SortedMap.js +140 -0
  68. package/dist/esm/SortedMap.js.map +1 -0
  69. package/dist/esm/collection.d.ts +176 -0
  70. package/dist/esm/collection.js +597 -0
  71. package/dist/esm/collection.js.map +1 -0
  72. package/dist/esm/deferred.d.ts +20 -0
  73. package/dist/esm/deferred.js +25 -0
  74. package/dist/esm/deferred.js.map +1 -0
  75. package/dist/esm/errors.d.ts +3 -0
  76. package/dist/esm/errors.js +10 -0
  77. package/dist/esm/errors.js.map +1 -0
  78. package/dist/esm/index.d.ts +9 -0
  79. package/dist/esm/index.js +33 -0
  80. package/dist/esm/index.js.map +1 -0
  81. package/dist/esm/proxy.d.ts +59 -0
  82. package/dist/esm/proxy.js +654 -0
  83. package/dist/esm/proxy.js.map +1 -0
  84. package/dist/esm/query/compiled-query.d.ts +22 -0
  85. package/dist/esm/query/compiled-query.js +162 -0
  86. package/dist/esm/query/compiled-query.js.map +1 -0
  87. package/dist/esm/query/evaluators.d.ts +9 -0
  88. package/dist/esm/query/evaluators.js +146 -0
  89. package/dist/esm/query/evaluators.js.map +1 -0
  90. package/dist/esm/query/extractors.d.ts +22 -0
  91. package/dist/esm/query/extractors.js +122 -0
  92. package/dist/esm/query/extractors.js.map +1 -0
  93. package/dist/esm/query/functions.d.ts +21 -0
  94. package/dist/esm/query/functions.js +152 -0
  95. package/dist/esm/query/functions.js.map +1 -0
  96. package/dist/esm/query/group-by.d.ts +40 -0
  97. package/dist/esm/query/group-by.js +91 -0
  98. package/dist/esm/query/group-by.js.map +1 -0
  99. package/dist/esm/query/index.d.ts +5 -0
  100. package/dist/esm/query/joins.d.ts +14 -0
  101. package/dist/esm/query/joins.js +155 -0
  102. package/dist/esm/query/joins.js.map +1 -0
  103. package/dist/esm/query/key-by.d.ts +3 -0
  104. package/dist/esm/query/key-by.js +43 -0
  105. package/dist/esm/query/key-by.js.map +1 -0
  106. package/dist/esm/query/order-by.d.ts +3 -0
  107. package/dist/esm/query/order-by.js +229 -0
  108. package/dist/esm/query/order-by.js.map +1 -0
  109. package/dist/esm/query/pipeline-compiler.d.ts +9 -0
  110. package/dist/esm/query/pipeline-compiler.js +94 -0
  111. package/dist/esm/query/pipeline-compiler.js.map +1 -0
  112. package/dist/esm/query/query-builder.d.ts +219 -0
  113. package/dist/esm/query/query-builder.js +314 -0
  114. package/dist/esm/query/query-builder.js.map +1 -0
  115. package/dist/esm/query/schema.d.ts +98 -0
  116. package/dist/esm/query/select.d.ts +3 -0
  117. package/dist/esm/query/select.js +107 -0
  118. package/dist/esm/query/select.js.map +1 -0
  119. package/dist/esm/query/types.d.ts +188 -0
  120. package/dist/esm/query/utils.d.ts +37 -0
  121. package/dist/esm/query/utils.js +154 -0
  122. package/dist/esm/query/utils.js.map +1 -0
  123. package/dist/esm/transactions.d.ts +27 -0
  124. package/dist/esm/transactions.js +137 -0
  125. package/dist/esm/transactions.js.map +1 -0
  126. package/dist/esm/types.d.ts +94 -0
  127. package/dist/esm/utils.d.ts +3 -0
  128. package/dist/esm/utils.js +17 -0
  129. package/dist/esm/utils.js.map +1 -0
  130. package/package.json +57 -0
  131. package/src/SortedMap.ts +163 -0
  132. package/src/collection.ts +919 -0
  133. package/src/deferred.ts +47 -0
  134. package/src/errors.ts +6 -0
  135. package/src/index.ts +12 -0
  136. package/src/proxy.ts +1104 -0
  137. package/src/query/compiled-query.ts +193 -0
  138. package/src/query/evaluators.ts +222 -0
  139. package/src/query/extractors.ts +211 -0
  140. package/src/query/functions.ts +297 -0
  141. package/src/query/group-by.ts +137 -0
  142. package/src/query/index.ts +5 -0
  143. package/src/query/joins.ts +247 -0
  144. package/src/query/key-by.ts +61 -0
  145. package/src/query/order-by.ts +312 -0
  146. package/src/query/pipeline-compiler.ts +152 -0
  147. package/src/query/query-builder.ts +898 -0
  148. package/src/query/schema.ts +255 -0
  149. package/src/query/select.ts +173 -0
  150. package/src/query/types.ts +417 -0
  151. package/src/query/utils.ts +245 -0
  152. package/src/transactions.ts +198 -0
  153. package/src/types.ts +125 -0
  154. package/src/utils.ts +15 -0
@@ -0,0 +1,229 @@
1
+ import { orderByWithIndex, map, topKWithIndex, orderByWithFractionalIndex, topKWithFractionalIndex, orderBy, topK } from "@electric-sql/d2ts";
2
+ import { evaluateOperandOnNestedRow } from "./extractors.js";
3
+ import { isOrderIndexFunctionCall } from "./utils.js";
4
+ function processOrderBy(resultPipeline, query, mainTableAlias) {
5
+ let hasOrderIndexColumn = false;
6
+ let orderIndexType = `numeric`;
7
+ let orderIndexAlias = ``;
8
+ for (const item of query.select) {
9
+ if (typeof item === `object`) {
10
+ for (const [alias, expr] of Object.entries(item)) {
11
+ if (typeof expr === `object` && isOrderIndexFunctionCall(expr)) {
12
+ hasOrderIndexColumn = true;
13
+ orderIndexAlias = alias;
14
+ orderIndexType = getOrderIndexType(expr);
15
+ break;
16
+ }
17
+ }
18
+ }
19
+ if (hasOrderIndexColumn) break;
20
+ }
21
+ const orderByItems = [];
22
+ if (typeof query.orderBy === `string`) {
23
+ orderByItems.push({
24
+ operand: query.orderBy,
25
+ direction: `asc`
26
+ });
27
+ } else if (Array.isArray(query.orderBy)) {
28
+ for (const item of query.orderBy) {
29
+ if (typeof item === `string`) {
30
+ orderByItems.push({
31
+ operand: item,
32
+ direction: `asc`
33
+ });
34
+ } else if (typeof item === `object`) {
35
+ for (const [column, direction] of Object.entries(item)) {
36
+ orderByItems.push({
37
+ operand: column,
38
+ direction
39
+ });
40
+ }
41
+ }
42
+ }
43
+ } else if (typeof query.orderBy === `object`) {
44
+ for (const [column, direction] of Object.entries(query.orderBy)) {
45
+ orderByItems.push({
46
+ operand: column,
47
+ direction
48
+ });
49
+ }
50
+ }
51
+ const valueExtractor = (value) => {
52
+ const row = value;
53
+ const nestedRow = { [mainTableAlias]: row };
54
+ if (orderByItems.length > 1) {
55
+ return orderByItems.map((item) => {
56
+ const val = evaluateOperandOnNestedRow(
57
+ nestedRow,
58
+ item.operand,
59
+ mainTableAlias
60
+ );
61
+ return item.direction === `desc` && typeof val === `number` ? -val : item.direction === `desc` && typeof val === `string` ? String.fromCharCode(
62
+ ...[...val].map((c) => 65535 - c.charCodeAt(0))
63
+ ) : val;
64
+ });
65
+ } else if (orderByItems.length === 1) {
66
+ const item = orderByItems[0];
67
+ const val = evaluateOperandOnNestedRow(
68
+ nestedRow,
69
+ item.operand,
70
+ mainTableAlias
71
+ );
72
+ return item.direction === `desc` && typeof val === `number` ? -val : item.direction === `desc` && typeof val === `string` ? String.fromCharCode(
73
+ ...[...val].map((c) => 65535 - c.charCodeAt(0))
74
+ ) : val;
75
+ }
76
+ return null;
77
+ };
78
+ const comparator = (a, b) => {
79
+ if (typeof a === `number` && typeof b === `number`) {
80
+ return a - b;
81
+ }
82
+ if (typeof a === `string` && typeof b === `string`) {
83
+ return a.localeCompare(b);
84
+ }
85
+ if (typeof a === `boolean` && typeof b === `boolean`) {
86
+ return a === b ? 0 : a ? 1 : -1;
87
+ }
88
+ if (a instanceof Date && b instanceof Date) {
89
+ return a.getTime() - b.getTime();
90
+ }
91
+ if (a === null || b === null) {
92
+ return 0;
93
+ }
94
+ if (Array.isArray(a) && Array.isArray(b)) {
95
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
96
+ const aVal = a[i];
97
+ const bVal = b[i];
98
+ let result;
99
+ if (typeof aVal === `boolean` && typeof bVal === `boolean`) {
100
+ result = aVal === bVal ? 0 : aVal ? 1 : -1;
101
+ } else if (typeof aVal === `number` && typeof bVal === `number`) {
102
+ result = aVal - bVal;
103
+ } else if (typeof aVal === `string` && typeof bVal === `string`) {
104
+ result = aVal.localeCompare(bVal);
105
+ } else {
106
+ result = comparator(aVal, bVal);
107
+ }
108
+ if (result !== 0) {
109
+ return result;
110
+ }
111
+ }
112
+ return a.length - b.length;
113
+ }
114
+ if (a == null && b == null) {
115
+ return 0;
116
+ }
117
+ return a.toString().localeCompare(b.toString());
118
+ };
119
+ let topKComparator;
120
+ if (!query.keyBy) {
121
+ topKComparator = (a, b) => {
122
+ const aValue = valueExtractor(a);
123
+ const bValue = valueExtractor(b);
124
+ return comparator(aValue, bValue);
125
+ };
126
+ }
127
+ if (hasOrderIndexColumn) {
128
+ if (orderIndexType === `numeric`) {
129
+ if (query.keyBy) {
130
+ resultPipeline = resultPipeline.pipe(
131
+ orderByWithIndex(valueExtractor, {
132
+ limit: query.limit,
133
+ offset: query.offset,
134
+ comparator
135
+ }),
136
+ map(([key, [value, index]]) => {
137
+ const result = {
138
+ ...value,
139
+ [orderIndexAlias]: index
140
+ };
141
+ return [key, result];
142
+ })
143
+ );
144
+ } else {
145
+ resultPipeline = resultPipeline.pipe(
146
+ map((value) => [null, value]),
147
+ topKWithIndex(topKComparator, {
148
+ limit: query.limit,
149
+ offset: query.offset
150
+ }),
151
+ map(([_, [value, index]]) => {
152
+ return {
153
+ ...value,
154
+ [orderIndexAlias]: index
155
+ };
156
+ })
157
+ );
158
+ }
159
+ } else {
160
+ if (query.keyBy) {
161
+ resultPipeline = resultPipeline.pipe(
162
+ orderByWithFractionalIndex(valueExtractor, {
163
+ limit: query.limit,
164
+ offset: query.offset,
165
+ comparator
166
+ }),
167
+ map(([key, [value, index]]) => {
168
+ const result = {
169
+ ...value,
170
+ [orderIndexAlias]: index
171
+ };
172
+ return [key, result];
173
+ })
174
+ );
175
+ } else {
176
+ resultPipeline = resultPipeline.pipe(
177
+ map((value) => [null, value]),
178
+ topKWithFractionalIndex(topKComparator, {
179
+ limit: query.limit,
180
+ offset: query.offset
181
+ }),
182
+ map(([_, [value, index]]) => {
183
+ return {
184
+ ...value,
185
+ [orderIndexAlias]: index
186
+ };
187
+ })
188
+ );
189
+ }
190
+ }
191
+ } else {
192
+ if (query.keyBy) {
193
+ resultPipeline = resultPipeline.pipe(
194
+ orderBy(valueExtractor, {
195
+ limit: query.limit,
196
+ offset: query.offset,
197
+ comparator
198
+ })
199
+ );
200
+ } else {
201
+ resultPipeline = resultPipeline.pipe(
202
+ map((value) => [null, value]),
203
+ topK(topKComparator, {
204
+ limit: query.limit,
205
+ offset: query.offset
206
+ }),
207
+ map(([_, value]) => value)
208
+ );
209
+ }
210
+ }
211
+ return resultPipeline;
212
+ }
213
+ function getOrderIndexType(obj) {
214
+ if (!isOrderIndexFunctionCall(obj)) {
215
+ throw new Error(`Not an ORDER_INDEX function call`);
216
+ }
217
+ const arg = obj[`ORDER_INDEX`];
218
+ if (arg === `numeric` || arg === true || arg === `default`) {
219
+ return `numeric`;
220
+ } else if (arg === `fractional`) {
221
+ return `fractional`;
222
+ } else {
223
+ throw new Error(`Invalid ORDER_INDEX type: ` + arg);
224
+ }
225
+ }
226
+ export {
227
+ processOrderBy
228
+ };
229
+ //# sourceMappingURL=order-by.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order-by.js","sources":["../../../src/query/order-by.ts"],"sourcesContent":["import {\n map,\n orderBy,\n orderByWithFractionalIndex,\n orderByWithIndex,\n topK,\n topKWithFractionalIndex,\n topKWithIndex,\n} from \"@electric-sql/d2ts\"\nimport { evaluateOperandOnNestedRow } from \"./extractors\"\nimport { isOrderIndexFunctionCall } from \"./utils\"\nimport type { ConditionOperand, Query } from \"./schema\"\nimport type { IStreamBuilder } from \"@electric-sql/d2ts\"\n\nexport function processOrderBy(\n resultPipeline: IStreamBuilder<\n Record<string, unknown> | [string | number, Record<string, unknown>]\n >,\n query: Query,\n mainTableAlias: string\n) {\n // Check if any column in the SELECT clause is an ORDER_INDEX function call\n let hasOrderIndexColumn = false\n let orderIndexType: `numeric` | `fractional` = `numeric`\n let orderIndexAlias = ``\n\n // Scan the SELECT clause for ORDER_INDEX functions\n for (const item of query.select) {\n if (typeof item === `object`) {\n for (const [alias, expr] of Object.entries(item)) {\n if (typeof expr === `object` && isOrderIndexFunctionCall(expr)) {\n hasOrderIndexColumn = true\n orderIndexAlias = alias\n orderIndexType = getOrderIndexType(expr)\n break\n }\n }\n }\n if (hasOrderIndexColumn) break\n }\n\n // Normalize orderBy to an array of objects\n const orderByItems: Array<{\n operand: ConditionOperand\n direction: `asc` | `desc`\n }> = []\n\n if (typeof query.orderBy === `string`) {\n // Handle string format: '@column'\n orderByItems.push({\n operand: query.orderBy,\n direction: `asc`,\n })\n } else if (Array.isArray(query.orderBy)) {\n // Handle array format: ['@column1', { '@column2': 'desc' }]\n for (const item of query.orderBy) {\n if (typeof item === `string`) {\n orderByItems.push({\n operand: item,\n direction: `asc`,\n })\n } else if (typeof item === `object`) {\n for (const [column, direction] of Object.entries(item)) {\n orderByItems.push({\n operand: column,\n direction: direction as `asc` | `desc`,\n })\n }\n }\n }\n } else if (typeof query.orderBy === `object`) {\n // Handle object format: { '@column': 'desc' }\n for (const [column, direction] of Object.entries(query.orderBy)) {\n orderByItems.push({\n operand: column,\n direction: direction as `asc` | `desc`,\n })\n }\n }\n\n // Create a value extractor function for the orderBy operator\n const valueExtractor = (value: unknown) => {\n const row = value as Record<string, unknown>\n\n // Create a nested row structure for evaluateOperandOnNestedRow\n const nestedRow: Record<string, unknown> = { [mainTableAlias]: row }\n\n // For multiple orderBy columns, create a composite key\n if (orderByItems.length > 1) {\n return orderByItems.map((item) => {\n const val = evaluateOperandOnNestedRow(\n nestedRow,\n item.operand,\n mainTableAlias\n )\n\n // Reverse the value for 'desc' ordering\n return item.direction === `desc` && typeof val === `number`\n ? -val\n : item.direction === `desc` && typeof val === `string`\n ? String.fromCharCode(\n ...[...val].map((c) => 0xffff - c.charCodeAt(0))\n )\n : val\n })\n } else if (orderByItems.length === 1) {\n // For a single orderBy column, use the value directly\n const item = orderByItems[0]\n const val = evaluateOperandOnNestedRow(\n nestedRow,\n item!.operand,\n mainTableAlias\n )\n\n // Reverse the value for 'desc' ordering\n return item!.direction === `desc` && typeof val === `number`\n ? -val\n : item!.direction === `desc` && typeof val === `string`\n ? String.fromCharCode(\n ...[...val].map((c) => 0xffff - c.charCodeAt(0))\n )\n : val\n }\n\n // Default case - no ordering\n return null\n }\n\n const comparator = (a: unknown, b: unknown): number => {\n // if a and b are both numbers compare them directly\n if (typeof a === `number` && typeof b === `number`) {\n return a - b\n }\n // if a and b are both strings, compare them lexicographically\n if (typeof a === `string` && typeof b === `string`) {\n return a.localeCompare(b)\n }\n // if a and b are both booleans, compare them\n if (typeof a === `boolean` && typeof b === `boolean`) {\n return a === b ? 0 : a ? 1 : -1\n }\n // if a and b are both dates, compare them\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime()\n }\n // if a and b are both null, return 0\n if (a === null || b === null) {\n return 0\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 // Get the values from the array\n const aVal = a[i]\n const bVal = b[i]\n\n // Compare the values\n let result: number\n\n if (typeof aVal === `boolean` && typeof bVal === `boolean`) {\n // Special handling for booleans - false comes before true\n result = aVal === bVal ? 0 : aVal ? 1 : -1\n } else if (typeof aVal === `number` && typeof bVal === `number`) {\n // Numeric comparison\n result = aVal - bVal\n } else if (typeof aVal === `string` && typeof bVal === `string`) {\n // String comparison\n result = aVal.localeCompare(bVal)\n } else {\n // Default comparison using the general comparator\n result = comparator(aVal, bVal)\n }\n\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 // if a and b are both null/undefined, return 0\n if (a == null && b == null) {\n return 0\n }\n // Fallback to string comparison for all other cases\n return (a as any).toString().localeCompare((b as any).toString())\n }\n\n let topKComparator: (a: unknown, b: unknown) => number\n if (!query.keyBy) {\n topKComparator = (a, b) => {\n const aValue = valueExtractor(a)\n const bValue = valueExtractor(b)\n return comparator(aValue, bValue)\n }\n }\n\n // Apply the appropriate orderBy operator based on whether an ORDER_INDEX column is requested\n if (hasOrderIndexColumn) {\n if (orderIndexType === `numeric`) {\n if (query.keyBy) {\n // Use orderByWithIndex for numeric indices\n resultPipeline = resultPipeline.pipe(\n orderByWithIndex(valueExtractor, {\n limit: query.limit,\n offset: query.offset,\n comparator,\n }),\n map(([key, [value, index]]) => {\n // Add the index to the result\n const result = {\n ...(value as Record<string, unknown>),\n [orderIndexAlias]: index,\n }\n return [key, result]\n })\n )\n } else {\n // Use topKWithIndex for numeric indices\n resultPipeline = resultPipeline.pipe(\n map((value) => [null, value]),\n topKWithIndex(topKComparator!, {\n limit: query.limit,\n offset: query.offset,\n }),\n map(([_, [value, index]]) => {\n // Add the index to the result\n return {\n ...(value as Record<string, unknown>),\n [orderIndexAlias]: index,\n }\n })\n )\n }\n } else {\n if (query.keyBy) {\n // Use orderByWithFractionalIndex for fractional indices\n resultPipeline = resultPipeline.pipe(\n orderByWithFractionalIndex(valueExtractor, {\n limit: query.limit,\n offset: query.offset,\n comparator,\n }),\n map(([key, [value, index]]) => {\n // Add the index to the result\n const result = {\n ...(value as Record<string, unknown>),\n [orderIndexAlias]: index,\n }\n return [key, result]\n })\n )\n } else {\n // Use topKWithFractionalIndex for fractional indices\n resultPipeline = resultPipeline.pipe(\n map((value) => [null, value]),\n topKWithFractionalIndex(topKComparator!, {\n limit: query.limit,\n offset: query.offset,\n }),\n map(([_, [value, index]]) => {\n // Add the index to the result\n return {\n ...(value as Record<string, unknown>),\n [orderIndexAlias]: index,\n }\n })\n )\n }\n }\n } else {\n if (query.keyBy) {\n // Use regular orderBy if no index column is requested and but a keyBy is requested\n resultPipeline = resultPipeline.pipe(\n orderBy(valueExtractor, {\n limit: query.limit,\n offset: query.offset,\n comparator,\n })\n )\n } else {\n // Use topK if no index column is requested and no keyBy is requested\n resultPipeline = resultPipeline.pipe(\n map((value) => [null, value]),\n topK(topKComparator!, {\n limit: query.limit,\n offset: query.offset,\n }),\n map(([_, value]) => value as Record<string, unknown>)\n )\n }\n }\n\n return resultPipeline\n}\n\n// Helper function to extract the ORDER_INDEX type from a function call\nfunction getOrderIndexType(obj: any): `numeric` | `fractional` {\n if (!isOrderIndexFunctionCall(obj)) {\n throw new Error(`Not an ORDER_INDEX function call`)\n }\n\n const arg = obj[`ORDER_INDEX`]\n if (arg === `numeric` || arg === true || arg === `default`) {\n return `numeric`\n } else if (arg === `fractional`) {\n return `fractional`\n } else {\n throw new Error(`Invalid ORDER_INDEX type: ` + arg)\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { Query } from './schema.js';
2
+ import { IStreamBuilder } from '@electric-sql/d2ts';
3
+ /**
4
+ * Compiles a query into a D2 pipeline
5
+ * @param query The query to compile
6
+ * @param inputs Mapping of table names to input streams
7
+ * @returns A stream builder representing the compiled query
8
+ */
9
+ export declare function compileQueryPipeline<T extends IStreamBuilder<unknown>>(query: Query, inputs: Record<string, IStreamBuilder<Record<string, unknown>>>): T;
@@ -0,0 +1,94 @@
1
+ import { map, filter } from "@electric-sql/d2ts";
2
+ import { evaluateConditionOnNestedRow } from "./evaluators.js";
3
+ import { processJoinClause } from "./joins.js";
4
+ import { processGroupBy } from "./group-by.js";
5
+ import { processOrderBy } from "./order-by.js";
6
+ import { processKeyBy } from "./key-by.js";
7
+ import { processSelect } from "./select.js";
8
+ function compileQueryPipeline(query, inputs) {
9
+ const allInputs = { ...inputs };
10
+ if (query.with && query.with.length > 0) {
11
+ for (const withQuery of query.with) {
12
+ if (!withQuery.as) {
13
+ throw new Error(`WITH query must have an "as" property`);
14
+ }
15
+ if (withQuery.keyBy !== void 0) {
16
+ throw new Error(`WITH query cannot have a "keyBy" property`);
17
+ }
18
+ if (allInputs[withQuery.as]) {
19
+ throw new Error(`CTE with name "${withQuery.as}" already exists`);
20
+ }
21
+ const withQueryWithoutWith = { ...withQuery, with: void 0 };
22
+ const compiledWithQuery = compileQueryPipeline(
23
+ withQueryWithoutWith,
24
+ allInputs
25
+ );
26
+ allInputs[withQuery.as] = compiledWithQuery;
27
+ }
28
+ }
29
+ const tables = {};
30
+ const mainTableAlias = query.as || query.from;
31
+ const input = allInputs[query.from];
32
+ if (!input) {
33
+ throw new Error(`Input for table "${query.from}" not found in inputs map`);
34
+ }
35
+ tables[mainTableAlias] = input;
36
+ let pipeline = input.pipe(
37
+ map((row) => {
38
+ return { [mainTableAlias]: row };
39
+ })
40
+ );
41
+ if (query.join) {
42
+ pipeline = processJoinClause(
43
+ pipeline,
44
+ query,
45
+ tables,
46
+ mainTableAlias,
47
+ allInputs
48
+ );
49
+ }
50
+ if (query.where) {
51
+ pipeline = pipeline.pipe(
52
+ filter((nestedRow) => {
53
+ const result = evaluateConditionOnNestedRow(
54
+ nestedRow,
55
+ query.where,
56
+ mainTableAlias
57
+ );
58
+ return result;
59
+ })
60
+ );
61
+ }
62
+ if (query.groupBy) {
63
+ pipeline = processGroupBy(pipeline, query, mainTableAlias);
64
+ }
65
+ if (query.having) {
66
+ pipeline = pipeline.pipe(
67
+ filter((row) => {
68
+ const result = evaluateConditionOnNestedRow(
69
+ { [mainTableAlias]: row, ...row },
70
+ query.having,
71
+ mainTableAlias
72
+ );
73
+ return result;
74
+ })
75
+ );
76
+ }
77
+ pipeline = processSelect(pipeline, query, mainTableAlias, allInputs);
78
+ let resultPipeline = pipeline;
79
+ if (query.keyBy) {
80
+ resultPipeline = processKeyBy(resultPipeline, query);
81
+ }
82
+ if (query.orderBy) {
83
+ resultPipeline = processOrderBy(resultPipeline, query, mainTableAlias);
84
+ } else if (query.limit !== void 0 || query.offset !== void 0) {
85
+ throw new Error(
86
+ `LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results`
87
+ );
88
+ }
89
+ return resultPipeline;
90
+ }
91
+ export {
92
+ compileQueryPipeline
93
+ };
94
+ //# sourceMappingURL=pipeline-compiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-compiler.js","sources":["../../../src/query/pipeline-compiler.ts"],"sourcesContent":["import { filter, map } from \"@electric-sql/d2ts\"\nimport { evaluateConditionOnNestedRow } from \"./evaluators.js\"\nimport { processJoinClause } from \"./joins.js\"\nimport { processGroupBy } from \"./group-by.js\"\nimport { processOrderBy } from \"./order-by.js\"\nimport { processKeyBy } from \"./key-by.js\"\nimport { processSelect } from \"./select.js\"\nimport type { Condition, Query } from \"./schema.js\"\nimport type { IStreamBuilder } from \"@electric-sql/d2ts\"\n\n/**\n * Compiles a query into a D2 pipeline\n * @param query The query to compile\n * @param inputs Mapping of table names to input streams\n * @returns A stream builder representing the compiled query\n */\nexport function compileQueryPipeline<T extends IStreamBuilder<unknown>>(\n query: Query,\n inputs: Record<string, IStreamBuilder<Record<string, unknown>>>\n): T {\n // Create a copy of the inputs map to avoid modifying the original\n const allInputs = { ...inputs }\n\n // Process WITH queries if they exist\n if (query.with && query.with.length > 0) {\n // Process each WITH query in order\n for (const withQuery of query.with) {\n // Ensure the WITH query has an alias\n if (!withQuery.as) {\n throw new Error(`WITH query must have an \"as\" property`)\n }\n\n // Ensure the WITH query is not keyed\n if ((withQuery as Query).keyBy !== undefined) {\n throw new Error(`WITH query cannot have a \"keyBy\" property`)\n }\n\n // Check if this CTE name already exists in the inputs\n if (allInputs[withQuery.as]) {\n throw new Error(`CTE with name \"${withQuery.as}\" already exists`)\n }\n\n // Create a new query without the 'with' property to avoid circular references\n const withQueryWithoutWith = { ...withQuery, with: undefined }\n\n // Compile the WITH query using the current set of inputs\n // (which includes previously compiled WITH queries)\n const compiledWithQuery = compileQueryPipeline(\n withQueryWithoutWith,\n allInputs\n )\n\n // Add the compiled query to the inputs map using its alias\n allInputs[withQuery.as] = compiledWithQuery as IStreamBuilder<\n Record<string, unknown>\n >\n }\n }\n\n // Create a map of table aliases to inputs\n const tables: Record<string, IStreamBuilder<Record<string, unknown>>> = {}\n\n // The main table is the one in the FROM clause\n const mainTableAlias = query.as || query.from\n\n // Get the main input from the inputs map (now including CTEs)\n const input = allInputs[query.from]\n if (!input) {\n throw new Error(`Input for table \"${query.from}\" not found in inputs map`)\n }\n\n tables[mainTableAlias] = input\n\n // Prepare the initial pipeline with the main table wrapped in its alias\n let pipeline = input.pipe(\n map((row: unknown) => {\n // Initialize the record with a nested structure\n return { [mainTableAlias]: row } as Record<string, unknown>\n })\n )\n\n // Process JOIN clauses if they exist\n if (query.join) {\n pipeline = processJoinClause(\n pipeline,\n query,\n tables,\n mainTableAlias,\n allInputs\n )\n }\n\n // Process the WHERE clause if it exists\n if (query.where) {\n pipeline = pipeline.pipe(\n filter((nestedRow) => {\n const result = evaluateConditionOnNestedRow(\n nestedRow,\n query.where as Condition,\n mainTableAlias\n )\n return result\n })\n )\n }\n\n // Process the GROUP BY clause if it exists\n if (query.groupBy) {\n pipeline = processGroupBy(pipeline, query, mainTableAlias)\n }\n\n // Process the HAVING clause if it exists\n // This works similarly to WHERE but is applied after any aggregations\n if (query.having) {\n pipeline = pipeline.pipe(\n filter((row) => {\n // For HAVING, we're working with the flattened row that contains both\n // the group by keys and the aggregate results directly\n const result = evaluateConditionOnNestedRow(\n { [mainTableAlias]: row, ...row } as Record<string, unknown>,\n query.having as Condition,\n mainTableAlias\n )\n return result\n })\n )\n }\n\n // Process the SELECT clause - this is where we flatten the structure\n pipeline = processSelect(pipeline, query, mainTableAlias, allInputs)\n\n let resultPipeline: IStreamBuilder<\n Record<string, unknown> | [string | number, Record<string, unknown>]\n > = pipeline\n\n // Process keyBy parameter if it exists\n if (query.keyBy) {\n resultPipeline = processKeyBy(resultPipeline, query)\n }\n\n // Process orderBy parameter if it exists\n if (query.orderBy) {\n resultPipeline = processOrderBy(resultPipeline, query, mainTableAlias)\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 return resultPipeline as T\n}\n"],"names":[],"mappings":";;;;;;;AAgBgB,SAAA,qBACd,OACA,QACG;AAEG,QAAA,YAAY,EAAE,GAAG,OAAO;AAG9B,MAAI,MAAM,QAAQ,MAAM,KAAK,SAAS,GAAG;AAE5B,eAAA,aAAa,MAAM,MAAM;AAE9B,UAAA,CAAC,UAAU,IAAI;AACX,cAAA,IAAI,MAAM,uCAAuC;AAAA,MAAA;AAIpD,UAAA,UAAoB,UAAU,QAAW;AACtC,cAAA,IAAI,MAAM,2CAA2C;AAAA,MAAA;AAIzD,UAAA,UAAU,UAAU,EAAE,GAAG;AAC3B,cAAM,IAAI,MAAM,kBAAkB,UAAU,EAAE,kBAAkB;AAAA,MAAA;AAIlE,YAAM,uBAAuB,EAAE,GAAG,WAAW,MAAM,OAAU;AAI7D,YAAM,oBAAoB;AAAA,QACxB;AAAA,QACA;AAAA,MACF;AAGU,gBAAA,UAAU,EAAE,IAAI;AAAA,IAAA;AAAA,EAG5B;AAIF,QAAM,SAAkE,CAAC;AAGnE,QAAA,iBAAiB,MAAM,MAAM,MAAM;AAGnC,QAAA,QAAQ,UAAU,MAAM,IAAI;AAClC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,oBAAoB,MAAM,IAAI,2BAA2B;AAAA,EAAA;AAG3E,SAAO,cAAc,IAAI;AAGzB,MAAI,WAAW,MAAM;AAAA,IACnB,IAAI,CAAC,QAAiB;AAEpB,aAAO,EAAE,CAAC,cAAc,GAAG,IAAI;AAAA,IAChC,CAAA;AAAA,EACH;AAGA,MAAI,MAAM,MAAM;AACH,eAAA;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EAAA;AAIF,MAAI,MAAM,OAAO;AACf,eAAW,SAAS;AAAA,MAClB,OAAO,CAAC,cAAc;AACpB,cAAM,SAAS;AAAA,UACb;AAAA,UACA,MAAM;AAAA,UACN;AAAA,QACF;AACO,eAAA;AAAA,MACR,CAAA;AAAA,IACH;AAAA,EAAA;AAIF,MAAI,MAAM,SAAS;AACN,eAAA,eAAe,UAAU,OAAO,cAAc;AAAA,EAAA;AAK3D,MAAI,MAAM,QAAQ;AAChB,eAAW,SAAS;AAAA,MAClB,OAAO,CAAC,QAAQ;AAGd,cAAM,SAAS;AAAA,UACb,EAAE,CAAC,cAAc,GAAG,KAAK,GAAG,IAAI;AAAA,UAChC,MAAM;AAAA,UACN;AAAA,QACF;AACO,eAAA;AAAA,MACR,CAAA;AAAA,IACH;AAAA,EAAA;AAIF,aAAW,cAAc,UAAU,OAAO,gBAAgB,SAAS;AAEnE,MAAI,iBAEA;AAGJ,MAAI,MAAM,OAAO;AACE,qBAAA,aAAa,gBAAgB,KAAK;AAAA,EAAA;AAIrD,MAAI,MAAM,SAAS;AACA,qBAAA,eAAe,gBAAgB,OAAO,cAAc;AAAA,EAAA,WAC5D,MAAM,UAAU,UAAa,MAAM,WAAW,QAAW;AAElE,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EAAA;AAGK,SAAA;AACT;"}
@@ -0,0 +1,219 @@
1
+ import { Collection } from '../collection.js';
2
+ import { Comparator, Condition, Limit, LiteralValue, Offset, OrderBy, Query, Select } from './schema.js';
3
+ import { Context, Flatten, InferResultTypeFromSelectTuple, Input, InputReference, PropertyReference, PropertyReferenceString, RemoveIndexSignature, Schema } from './types.js';
4
+ type CollectionRef = {
5
+ [K: string]: Collection<any>;
6
+ };
7
+ export declare class BaseQueryBuilder<TContext extends Context<Schema>> {
8
+ private readonly query;
9
+ /**
10
+ * Create a new QueryBuilder instance.
11
+ */
12
+ constructor(query?: Partial<Query<TContext>>);
13
+ from<TCollectionRef extends CollectionRef>(collectionRef: TCollectionRef): QueryBuilder<{
14
+ baseSchema: Flatten<TContext[`baseSchema`] & {
15
+ [K in keyof TCollectionRef & string]: RemoveIndexSignature<(TCollectionRef[keyof TCollectionRef] extends Collection<infer T> ? T : never) & Input>;
16
+ }>;
17
+ schema: Flatten<{
18
+ [K in keyof TCollectionRef & string]: RemoveIndexSignature<(TCollectionRef[keyof TCollectionRef] extends Collection<infer T> ? T : never) & Input>;
19
+ }>;
20
+ default: keyof TCollectionRef & string;
21
+ }>;
22
+ from<T extends InputReference<{
23
+ baseSchema: TContext[`baseSchema`];
24
+ schema: TContext[`baseSchema`];
25
+ }>>(collection: T): QueryBuilder<{
26
+ baseSchema: TContext[`baseSchema`];
27
+ schema: {
28
+ [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>;
29
+ };
30
+ default: T;
31
+ }>;
32
+ from<T extends InputReference<{
33
+ baseSchema: TContext[`baseSchema`];
34
+ schema: TContext[`baseSchema`];
35
+ }>, TAs extends string>(collection: T, as: TAs): QueryBuilder<{
36
+ baseSchema: TContext[`baseSchema`];
37
+ schema: {
38
+ [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][T]>;
39
+ };
40
+ default: TAs;
41
+ }>;
42
+ private fromCollectionRef;
43
+ private fromInputReference;
44
+ /**
45
+ * Specify what columns to select.
46
+ * Overwrites any previous select clause.
47
+ *
48
+ * @param selects The columns to select
49
+ * @returns A new QueryBuilder with the select clause set
50
+ */
51
+ select<TSelects extends Array<Select<TContext>>>(this: QueryBuilder<TContext>, ...selects: TSelects): QueryBuilder<Flatten<Omit<TContext, `result`> & {
52
+ result: InferResultTypeFromSelectTuple<TContext, TSelects>;
53
+ }>>;
54
+ /**
55
+ * Add a where clause comparing two values.
56
+ */
57
+ where(left: PropertyReferenceString<TContext> | LiteralValue, operator: Comparator, right: PropertyReferenceString<TContext> | LiteralValue): QueryBuilder<TContext>;
58
+ /**
59
+ * Add a where clause with a complete condition object.
60
+ */
61
+ where(condition: Condition<TContext>): QueryBuilder<TContext>;
62
+ /**
63
+ * Add a having clause comparing two values.
64
+ * For filtering results after they have been grouped.
65
+ */
66
+ having(left: PropertyReferenceString<TContext> | LiteralValue, operator: Comparator, right: PropertyReferenceString<TContext> | LiteralValue): QueryBuilder<TContext>;
67
+ /**
68
+ * Add a having clause with a complete condition object.
69
+ * For filtering results after they have been grouped.
70
+ */
71
+ having(condition: Condition<TContext>): QueryBuilder<TContext>;
72
+ /**
73
+ * Add a join clause to the query using a CollectionRef.
74
+ */
75
+ join<TCollectionRef extends CollectionRef>(joinClause: {
76
+ type: `inner` | `left` | `right` | `full` | `cross`;
77
+ from: TCollectionRef;
78
+ on: Condition<Flatten<{
79
+ baseSchema: TContext[`baseSchema`];
80
+ schema: TContext[`schema`] & {
81
+ [K in keyof TCollectionRef & string]: RemoveIndexSignature<(TCollectionRef[keyof TCollectionRef] extends Collection<infer T> ? T : never) & Input>;
82
+ };
83
+ }>>;
84
+ where?: Condition<Flatten<{
85
+ baseSchema: TContext[`baseSchema`];
86
+ schema: {
87
+ [K in keyof TCollectionRef & string]: RemoveIndexSignature<(TCollectionRef[keyof TCollectionRef] extends Collection<infer T> ? T : never) & Input>;
88
+ };
89
+ }>>;
90
+ }): QueryBuilder<Flatten<Omit<TContext, `schema`> & {
91
+ schema: TContext[`schema`] & {
92
+ [K in keyof TCollectionRef & string]: RemoveIndexSignature<(TCollectionRef[keyof TCollectionRef] extends Collection<infer T> ? T : never) & Input>;
93
+ };
94
+ }>>;
95
+ /**
96
+ * Add a join clause to the query without specifying an alias.
97
+ * The collection name will be used as the default alias.
98
+ */
99
+ join<T extends InputReference<{
100
+ baseSchema: TContext[`baseSchema`];
101
+ schema: TContext[`baseSchema`];
102
+ }>>(joinClause: {
103
+ type: `inner` | `left` | `right` | `full` | `cross`;
104
+ from: T;
105
+ on: Condition<Flatten<{
106
+ baseSchema: TContext[`baseSchema`];
107
+ schema: TContext[`schema`] & {
108
+ [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>;
109
+ };
110
+ }>>;
111
+ where?: Condition<Flatten<{
112
+ baseSchema: TContext[`baseSchema`];
113
+ schema: {
114
+ [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>;
115
+ };
116
+ }>>;
117
+ }): QueryBuilder<Flatten<Omit<TContext, `schema`> & {
118
+ schema: TContext[`schema`] & {
119
+ [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>;
120
+ };
121
+ }>>;
122
+ /**
123
+ * Add a join clause to the query with a specified alias.
124
+ */
125
+ join<TFrom extends InputReference<{
126
+ baseSchema: TContext[`baseSchema`];
127
+ schema: TContext[`baseSchema`];
128
+ }>, TAs extends string>(joinClause: {
129
+ type: `inner` | `left` | `right` | `full` | `cross`;
130
+ from: TFrom;
131
+ as: TAs;
132
+ on: Condition<Flatten<{
133
+ baseSchema: TContext[`baseSchema`];
134
+ schema: TContext[`schema`] & {
135
+ [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>;
136
+ };
137
+ }>>;
138
+ where?: Condition<Flatten<{
139
+ baseSchema: TContext[`baseSchema`];
140
+ schema: {
141
+ [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>;
142
+ };
143
+ }>>;
144
+ }): QueryBuilder<Flatten<Omit<TContext, `schema`> & {
145
+ schema: TContext[`schema`] & {
146
+ [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>;
147
+ };
148
+ }>>;
149
+ private joinCollectionRef;
150
+ private joinInputReference;
151
+ /**
152
+ * Add an orderBy clause to sort the results.
153
+ * Overwrites any previous orderBy clause.
154
+ *
155
+ * @param orderBy The order specification
156
+ * @returns A new QueryBuilder with the orderBy clause set
157
+ */
158
+ orderBy(orderBy: OrderBy<TContext>): QueryBuilder<TContext>;
159
+ /**
160
+ * Set a limit on the number of results returned.
161
+ *
162
+ * @param limit Maximum number of results to return
163
+ * @returns A new QueryBuilder with the limit set
164
+ */
165
+ limit(limit: Limit<TContext>): QueryBuilder<TContext>;
166
+ /**
167
+ * Set an offset to skip a number of results.
168
+ *
169
+ * @param offset Number of results to skip
170
+ * @returns A new QueryBuilder with the offset set
171
+ */
172
+ offset(offset: Offset<TContext>): QueryBuilder<TContext>;
173
+ /**
174
+ * Specify which column(s) to use as keys in the output keyed stream.
175
+ *
176
+ * @param keyBy The column(s) to use as keys
177
+ * @returns A new QueryBuilder with the keyBy clause set
178
+ */
179
+ keyBy(keyBy: PropertyReference<TContext> | Array<PropertyReference<TContext>>): QueryBuilder<TContext>;
180
+ /**
181
+ * Add a groupBy clause to group the results by one or more columns.
182
+ *
183
+ * @param groupBy The column(s) to group by
184
+ * @returns A new QueryBuilder with the groupBy clause set
185
+ */
186
+ groupBy(groupBy: PropertyReference<TContext> | Array<PropertyReference<TContext>>): QueryBuilder<TContext>;
187
+ /**
188
+ * Define a Common Table Expression (CTE) that can be referenced in the main query.
189
+ * This allows referencing the CTE by name in subsequent from/join clauses.
190
+ *
191
+ * @param name The name of the CTE
192
+ * @param queryBuilderCallback A function that builds the CTE query
193
+ * @returns A new QueryBuilder with the CTE added
194
+ */
195
+ with<TName extends string, TResult = Record<string, unknown>>(name: TName, queryBuilderCallback: (builder: InitialQueryBuilder<{
196
+ baseSchema: TContext[`baseSchema`];
197
+ schema: {};
198
+ }>) => QueryBuilder<any>): InitialQueryBuilder<{
199
+ baseSchema: TContext[`baseSchema`] & {
200
+ [K in TName]: TResult;
201
+ };
202
+ schema: TContext[`schema`];
203
+ }>;
204
+ get _query(): Query<TContext>;
205
+ }
206
+ export type InitialQueryBuilder<TContext extends Context<Schema>> = Pick<BaseQueryBuilder<TContext>, `from` | `with`>;
207
+ export type QueryBuilder<TContext extends Context<Schema>> = Omit<BaseQueryBuilder<TContext>, `from`>;
208
+ /**
209
+ * Create a new query builder with the given schema
210
+ */
211
+ export declare function queryBuilder<TBaseSchema extends Schema = {}>(): InitialQueryBuilder<{
212
+ baseSchema: TBaseSchema;
213
+ schema: {};
214
+ }>;
215
+ export type ResultsFromContext<TContext extends Context<Schema>> = Flatten<TContext[`result`] extends object ? TContext[`result`] : TContext[`result`] extends undefined ? TContext[`schema`] : object>;
216
+ export type ResultFromQueryBuilder<TQueryBuilder> = Flatten<TQueryBuilder extends QueryBuilder<infer C> ? C extends {
217
+ result: infer R;
218
+ } ? R : never : never>;
219
+ export {};