@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
@@ -12,24 +12,13 @@ import type {
12
12
  const transactions: Array<Transaction<any>> = []
13
13
  let transactionStack: Array<Transaction<any>> = []
14
14
 
15
+ let sequenceNumber = 0
16
+
15
17
  export function createTransaction<
16
18
  TData extends object = Record<string, unknown>,
17
19
  >(config: TransactionConfig<TData>): Transaction<TData> {
18
- if (typeof config.mutationFn === `undefined`) {
19
- throw `mutationFn is required when creating a transaction`
20
- }
21
-
22
- let transactionId = config.id
23
- if (!transactionId) {
24
- transactionId = crypto.randomUUID()
25
- }
26
- const newTransaction = new Transaction<TData>({
27
- ...config,
28
- id: transactionId,
29
- })
30
-
20
+ const newTransaction = new Transaction<TData>(config)
31
21
  transactions.push(newTransaction)
32
-
33
22
  return newTransaction
34
23
  }
35
24
 
@@ -56,7 +45,7 @@ function removeFromPendingList(tx: Transaction<any>) {
56
45
  }
57
46
  }
58
47
 
59
- export class Transaction<
48
+ class Transaction<
60
49
  T extends object = Record<string, unknown>,
61
50
  TOperation extends OperationType = OperationType,
62
51
  > {
@@ -67,6 +56,7 @@ export class Transaction<
67
56
  public isPersisted: Deferred<Transaction<T, TOperation>>
68
57
  public autoCommit: boolean
69
58
  public createdAt: Date
59
+ public sequenceNumber: number
70
60
  public metadata: Record<string, unknown>
71
61
  public error?: {
72
62
  message: string
@@ -74,13 +64,17 @@ export class Transaction<
74
64
  }
75
65
 
76
66
  constructor(config: TransactionConfig<T>) {
77
- this.id = config.id!
67
+ if (typeof config.mutationFn === `undefined`) {
68
+ throw `mutationFn is required when creating a transaction`
69
+ }
70
+ this.id = config.id ?? crypto.randomUUID()
78
71
  this.mutationFn = config.mutationFn
79
72
  this.state = `pending`
80
73
  this.mutations = []
81
74
  this.isPersisted = createDeferred<Transaction<T, TOperation>>()
82
75
  this.autoCommit = config.autoCommit ?? true
83
76
  this.createdAt = new Date()
77
+ this.sequenceNumber = sequenceNumber++
84
78
  this.metadata = config.metadata ?? {}
85
79
  }
86
80
 
@@ -210,4 +204,21 @@ export class Transaction<
210
204
 
211
205
  return this
212
206
  }
207
+
208
+ /**
209
+ * Compare two transactions by their createdAt time and sequence number in order
210
+ * to sort them in the order they were created.
211
+ * @param other - The other transaction to compare to
212
+ * @returns -1 if this transaction was created before the other, 1 if it was created after, 0 if they were created at the same time
213
+ */
214
+ compareCreatedAt(other: Transaction<any>): number {
215
+ const createdAtComparison =
216
+ this.createdAt.getTime() - other.createdAt.getTime()
217
+ if (createdAtComparison !== 0) {
218
+ return createdAtComparison
219
+ }
220
+ return this.sequenceNumber - other.sequenceNumber
221
+ }
213
222
  }
223
+
224
+ export type { Transaction }
package/src/types.ts CHANGED
@@ -154,6 +154,15 @@ export interface SyncConfig<
154
154
  * @returns Record containing relation information
155
155
  */
156
156
  getSyncMetadata?: () => Record<string, unknown>
157
+
158
+ /**
159
+ * The row update mode used to sync to the collection.
160
+ * @default `partial`
161
+ * @description
162
+ * - `partial`: Updates contain only the changes to the row.
163
+ * - `full`: Updates contain the entire row.
164
+ */
165
+ rowUpdateMode?: `partial` | `full`
157
166
  }
158
167
 
159
168
  export interface ChangeMessage<
@@ -318,6 +327,12 @@ export type InputRow = [unknown, Record<string, unknown>]
318
327
  */
319
328
  export type KeyedStream = IStreamBuilder<InputRow>
320
329
 
330
+ /**
331
+ * Result stream type representing the output of compiled queries
332
+ * Always returns [key, [result, orderByIndex]] where orderByIndex is undefined for unordered queries
333
+ */
334
+ export type ResultStream = IStreamBuilder<[unknown, [any, string | undefined]]>
335
+
321
336
  /**
322
337
  * A namespaced row is a row withing a pipeline that had each table wrapped in its alias
323
338
  */
@@ -1,160 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const d2mini = require("@electric-sql/d2mini");
4
- const collection = require("../collection.cjs");
5
- const pipelineCompiler = require("./pipeline-compiler.cjs");
6
- function compileQuery(queryBuilder) {
7
- return new CompiledQuery(queryBuilder);
8
- }
9
- class CompiledQuery {
10
- constructor(queryBuilder) {
11
- this.state = `compiled`;
12
- this.unsubscribeCallbacks = [];
13
- const query = queryBuilder._query;
14
- const collections = query.collections;
15
- if (!collections) {
16
- throw new Error(`No collections provided`);
17
- }
18
- this.inputCollections = collections;
19
- const graph = new d2mini.D2();
20
- const inputs = Object.fromEntries(
21
- Object.entries(collections).map(([key]) => [key, graph.newInput()])
22
- );
23
- const sync = ({
24
- begin,
25
- write,
26
- commit,
27
- collection: collection2
28
- }) => {
29
- pipelineCompiler.compileQueryPipeline(
30
- query,
31
- inputs
32
- ).pipe(
33
- d2mini.output((data) => {
34
- begin();
35
- data.getInner().reduce((acc, [[key, value], multiplicity]) => {
36
- const changes = acc.get(key) || {
37
- deletes: 0,
38
- inserts: 0,
39
- value
40
- };
41
- if (multiplicity < 0) {
42
- changes.deletes += Math.abs(multiplicity);
43
- } else if (multiplicity > 0) {
44
- changes.inserts += multiplicity;
45
- changes.value = value;
46
- }
47
- acc.set(key, changes);
48
- return acc;
49
- }, /* @__PURE__ */ new Map()).forEach((changes, rawKey) => {
50
- const { deletes, inserts, value } = changes;
51
- const valueWithKey = { ...value, _key: rawKey };
52
- if (inserts && deletes === 0) {
53
- write({
54
- value: valueWithKey,
55
- type: `insert`
56
- });
57
- } else if (
58
- // Insert & update(s) (updates are a delete & insert)
59
- inserts > deletes || // Just update(s) but the item is already in the collection (so
60
- // was inserted previously).
61
- inserts === deletes && collection2.has(valueWithKey._key)
62
- ) {
63
- write({
64
- value: valueWithKey,
65
- type: `update`
66
- });
67
- } else if (deletes > 0) {
68
- write({
69
- value: valueWithKey,
70
- type: `delete`
71
- });
72
- } else {
73
- throw new Error(
74
- `This should never happen ${JSON.stringify(changes)}`
75
- );
76
- }
77
- });
78
- commit();
79
- })
80
- );
81
- graph.finalize();
82
- };
83
- this.graph = graph;
84
- this.inputs = inputs;
85
- const compare = query.orderBy ? (val1, val2) => {
86
- const x = val1;
87
- const y = val2;
88
- if (x._orderByIndex < y._orderByIndex) {
89
- return -1;
90
- } else if (x._orderByIndex > y._orderByIndex) {
91
- return 1;
92
- } else {
93
- return 0;
94
- }
95
- } : void 0;
96
- this.resultCollection = collection.createCollection({
97
- getKey: (val) => {
98
- return val._key;
99
- },
100
- gcTime: 0,
101
- startSync: true,
102
- compare,
103
- sync: {
104
- sync
105
- }
106
- });
107
- }
108
- get results() {
109
- return this.resultCollection;
110
- }
111
- sendChangesToInput(inputKey, changes, getKey) {
112
- const input = this.inputs[inputKey];
113
- const multiSetArray = [];
114
- for (const change of changes) {
115
- const key = getKey(change.value);
116
- if (change.type === `insert`) {
117
- multiSetArray.push([[key, change.value], 1]);
118
- } else if (change.type === `update`) {
119
- multiSetArray.push([[key, change.previousValue], -1]);
120
- multiSetArray.push([[key, change.value], 1]);
121
- } else {
122
- multiSetArray.push([[key, change.value], -1]);
123
- }
124
- }
125
- input.sendData(new d2mini.MultiSet(multiSetArray));
126
- }
127
- runGraph() {
128
- this.graph.run();
129
- }
130
- start() {
131
- if (this.state === `running`) {
132
- throw new Error(`Query is already running`);
133
- } else if (this.state === `stopped`) {
134
- throw new Error(`Query is stopped`);
135
- }
136
- Object.entries(this.inputCollections).forEach(([key, collection2]) => {
137
- const unsubscribe = collection2.subscribeChanges(
138
- (changes) => {
139
- this.sendChangesToInput(key, changes, collection2.config.getKey);
140
- this.runGraph();
141
- },
142
- { includeInitialState: true }
143
- );
144
- this.unsubscribeCallbacks.push(unsubscribe);
145
- });
146
- this.runGraph();
147
- this.state = `running`;
148
- return () => {
149
- this.stop();
150
- };
151
- }
152
- stop() {
153
- this.unsubscribeCallbacks.forEach((unsubscribe) => unsubscribe());
154
- this.unsubscribeCallbacks = [];
155
- this.state = `stopped`;
156
- }
157
- }
158
- exports.CompiledQuery = CompiledQuery;
159
- exports.compileQuery = compileQuery;
160
- //# sourceMappingURL=compiled-query.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"compiled-query.cjs","sources":["../../../src/query/compiled-query.ts"],"sourcesContent":["import { D2, MultiSet, output } from \"@electric-sql/d2mini\"\nimport { createCollection } from \"../collection.js\"\nimport { compileQueryPipeline } from \"./pipeline-compiler.js\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\nimport type { Collection } from \"../collection.js\"\nimport type { ChangeMessage, ResolveType, SyncConfig } from \"../types.js\"\nimport type {\n IStreamBuilder,\n MultiSetArray,\n RootStreamBuilder,\n} from \"@electric-sql/d2mini\"\nimport type { QueryBuilder, ResultsFromContext } from \"./query-builder.js\"\nimport type { Context, Schema } from \"./types.js\"\n\nexport function compileQuery<TContext extends Context<Schema>>(\n queryBuilder: QueryBuilder<TContext>\n) {\n return new CompiledQuery<\n ResultsFromContext<TContext> & { _key?: string | number }\n >(queryBuilder)\n}\n\nexport class CompiledQuery<TResults extends object = Record<string, unknown>> {\n private graph: D2\n private inputs: Record<string, RootStreamBuilder<any>>\n private inputCollections: Record<string, Collection<any>>\n private resultCollection: Collection<TResults>\n public state: `compiled` | `running` | `stopped` = `compiled`\n private unsubscribeCallbacks: Array<() => void> = []\n\n constructor(queryBuilder: QueryBuilder<Context<Schema>>) {\n const query = queryBuilder._query\n const collections = query.collections\n\n if (!collections) {\n throw new Error(`No collections provided`)\n }\n\n this.inputCollections = collections\n\n const graph = new D2()\n const inputs = Object.fromEntries(\n Object.entries(collections).map(([key]) => [key, graph.newInput<any>()])\n )\n\n // Use TResults directly to ensure type compatibility\n const sync: SyncConfig<TResults>[`sync`] = ({\n begin,\n write,\n commit,\n collection,\n }) => {\n compileQueryPipeline<IStreamBuilder<[unknown, TResults]>>(\n query,\n inputs\n ).pipe(\n output((data) => {\n begin()\n data\n .getInner()\n .reduce((acc, [[key, value], multiplicity]) => {\n const changes = acc.get(key) || {\n deletes: 0,\n inserts: 0,\n value,\n }\n if (multiplicity < 0) {\n changes.deletes += Math.abs(multiplicity)\n } else if (multiplicity > 0) {\n changes.inserts += multiplicity\n changes.value = value\n }\n acc.set(key, changes)\n return acc\n }, new Map<unknown, { deletes: number; inserts: number; value: TResults }>())\n .forEach((changes, rawKey) => {\n const { deletes, inserts, value } = changes\n const valueWithKey = { ...value, _key: rawKey }\n\n // Simple singular insert.\n if (inserts && deletes === 0) {\n write({\n value: valueWithKey,\n type: `insert`,\n })\n } else if (\n // Insert & update(s) (updates are a delete & insert)\n inserts > deletes ||\n // Just update(s) but the item is already in the collection (so\n // was inserted previously).\n (inserts === deletes &&\n collection.has(valueWithKey._key as string | number))\n ) {\n write({\n value: valueWithKey,\n type: `update`,\n })\n // Only delete is left as an option\n } else if (deletes > 0) {\n write({\n value: valueWithKey,\n type: `delete`,\n })\n } else {\n throw new Error(\n `This should never happen ${JSON.stringify(changes)}`\n )\n }\n })\n commit()\n })\n )\n graph.finalize()\n }\n\n this.graph = graph\n this.inputs = inputs\n\n const compare = query.orderBy\n ? (\n val1: ResolveType<\n TResults,\n StandardSchemaV1,\n Record<string, unknown>\n >,\n val2: ResolveType<TResults, StandardSchemaV1, Record<string, unknown>>\n ): number => {\n // The query builder always adds an _orderByIndex property if the results are ordered\n const x = val1 as TResults & { _orderByIndex: number }\n const y = val2 as TResults & { _orderByIndex: number }\n if (x._orderByIndex < y._orderByIndex) {\n return -1\n } else if (x._orderByIndex > y._orderByIndex) {\n return 1\n } else {\n return 0\n }\n }\n : undefined\n\n this.resultCollection = createCollection<TResults>({\n getKey: (val: unknown) => {\n return (val as any)._key\n },\n gcTime: 0,\n startSync: true,\n compare,\n sync: {\n sync: sync as unknown as (params: {\n collection: Collection<\n ResolveType<TResults, never, Record<string, unknown>>,\n string | number,\n {}\n >\n begin: () => void\n write: (\n message: Omit<\n ChangeMessage<\n ResolveType<TResults, never, Record<string, unknown>>,\n string | number\n >,\n `key`\n >\n ) => void\n commit: () => void\n }) => void,\n },\n }) as unknown as Collection<TResults, string | number, {}>\n }\n\n get results() {\n return this.resultCollection\n }\n\n private sendChangesToInput(\n inputKey: string,\n changes: Array<ChangeMessage>,\n getKey: (item: ChangeMessage[`value`]) => any\n ) {\n const input = this.inputs[inputKey]!\n const multiSetArray: MultiSetArray<unknown> = []\n for (const change of changes) {\n const key = getKey(change.value)\n if (change.type === `insert`) {\n multiSetArray.push([[key, change.value], 1])\n } else if (change.type === `update`) {\n multiSetArray.push([[key, change.previousValue], -1])\n multiSetArray.push([[key, change.value], 1])\n } else {\n // change.type === `delete`\n multiSetArray.push([[key, change.value], -1])\n }\n }\n input.sendData(new MultiSet(multiSetArray))\n }\n\n private runGraph() {\n this.graph.run()\n }\n\n start() {\n if (this.state === `running`) {\n throw new Error(`Query is already running`)\n } else if (this.state === `stopped`) {\n throw new Error(`Query is stopped`)\n }\n\n // Subscribe to changes\n Object.entries(this.inputCollections).forEach(([key, collection]) => {\n const unsubscribe = collection.subscribeChanges(\n (changes) => {\n this.sendChangesToInput(key, changes, collection.config.getKey)\n this.runGraph()\n },\n { includeInitialState: true }\n )\n\n this.unsubscribeCallbacks.push(unsubscribe)\n })\n\n this.runGraph()\n\n this.state = `running`\n return () => {\n this.stop()\n }\n }\n\n stop() {\n this.unsubscribeCallbacks.forEach((unsubscribe) => unsubscribe())\n this.unsubscribeCallbacks = []\n this.state = `stopped`\n }\n}\n"],"names":["D2","collection","compileQueryPipeline","output","createCollection","MultiSet"],"mappings":";;;;;AAcO,SAAS,aACd,cACA;AACO,SAAA,IAAI,cAET,YAAY;AAChB;AAEO,MAAM,cAAiE;AAAA,EAQ5E,YAAY,cAA6C;AAHzD,SAAO,QAA4C;AACnD,SAAQ,uBAA0C,CAAC;AAGjD,UAAM,QAAQ,aAAa;AAC3B,UAAM,cAAc,MAAM;AAE1B,QAAI,CAAC,aAAa;AACV,YAAA,IAAI,MAAM,yBAAyB;AAAA,IAAA;AAG3C,SAAK,mBAAmB;AAElB,UAAA,QAAQ,IAAIA,UAAG;AACrB,UAAM,SAAS,OAAO;AAAA,MACpB,OAAO,QAAQ,WAAW,EAAE,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,MAAM,SAAA,CAAe,CAAC;AAAA,IACzE;AAGA,UAAM,OAAqC,CAAC;AAAA,MAC1C;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAAC;AAAA,IAAA,MACI;AACJC,uBAAA;AAAA,QACE;AAAA,QACA;AAAA,MAAA,EACA;AAAA,QACAC,OAAA,OAAO,CAAC,SAAS;AACT,gBAAA;AAEH,eAAA,WACA,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,KAAK,GAAG,YAAY,MAAM;AAC7C,kBAAM,UAAU,IAAI,IAAI,GAAG,KAAK;AAAA,cAC9B,SAAS;AAAA,cACT,SAAS;AAAA,cACT;AAAA,YACF;AACA,gBAAI,eAAe,GAAG;AACZ,sBAAA,WAAW,KAAK,IAAI,YAAY;AAAA,YAAA,WAC/B,eAAe,GAAG;AAC3B,sBAAQ,WAAW;AACnB,sBAAQ,QAAQ;AAAA,YAAA;AAEd,gBAAA,IAAI,KAAK,OAAO;AACb,mBAAA;AAAA,UAAA,uBACF,IAAoE,CAAC,EAC3E,QAAQ,CAAC,SAAS,WAAW;AAC5B,kBAAM,EAAE,SAAS,SAAS,MAAU,IAAA;AACpC,kBAAM,eAAe,EAAE,GAAG,OAAO,MAAM,OAAO;AAG1C,gBAAA,WAAW,YAAY,GAAG;AACtB,oBAAA;AAAA,gBACJ,OAAO;AAAA,gBACP,MAAM;AAAA,cAAA,CACP;AAAA,YAAA;AAAA;AAAA,cAGD,UAAU;AAAA;AAAA,cAGT,YAAY,WACXF,YAAW,IAAI,aAAa,IAAuB;AAAA,cACrD;AACM,oBAAA;AAAA,gBACJ,OAAO;AAAA,gBACP,MAAM;AAAA,cAAA,CACP;AAAA,YAAA,WAEQ,UAAU,GAAG;AAChB,oBAAA;AAAA,gBACJ,OAAO;AAAA,gBACP,MAAM;AAAA,cAAA,CACP;AAAA,YAAA,OACI;AACL,oBAAM,IAAI;AAAA,gBACR,4BAA4B,KAAK,UAAU,OAAO,CAAC;AAAA,cACrD;AAAA,YAAA;AAAA,UACF,CACD;AACI,iBAAA;AAAA,QACR,CAAA;AAAA,MACH;AACA,YAAM,SAAS;AAAA,IACjB;AAEA,SAAK,QAAQ;AACb,SAAK,SAAS;AAEd,UAAM,UAAU,MAAM,UAClB,CACE,MAKA,SACW;AAEX,YAAM,IAAI;AACV,YAAM,IAAI;AACN,UAAA,EAAE,gBAAgB,EAAE,eAAe;AAC9B,eAAA;AAAA,MACE,WAAA,EAAE,gBAAgB,EAAE,eAAe;AACrC,eAAA;AAAA,MAAA,OACF;AACE,eAAA;AAAA,MAAA;AAAA,IACT,IAEF;AAEJ,SAAK,mBAAmBG,4BAA2B;AAAA,MACjD,QAAQ,CAAC,QAAiB;AACxB,eAAQ,IAAY;AAAA,MACtB;AAAA,MACA,QAAQ;AAAA,MACR,WAAW;AAAA,MACX;AAAA,MACA,MAAM;AAAA,QACJ;AAAA,MAAA;AAAA,IAkBF,CACD;AAAA,EAAA;AAAA,EAGH,IAAI,UAAU;AACZ,WAAO,KAAK;AAAA,EAAA;AAAA,EAGN,mBACN,UACA,SACA,QACA;AACM,UAAA,QAAQ,KAAK,OAAO,QAAQ;AAClC,UAAM,gBAAwC,CAAC;AAC/C,eAAW,UAAU,SAAS;AACtB,YAAA,MAAM,OAAO,OAAO,KAAK;AAC3B,UAAA,OAAO,SAAS,UAAU;AACd,sBAAA,KAAK,CAAC,CAAC,KAAK,OAAO,KAAK,GAAG,CAAC,CAAC;AAAA,MAC7C,WAAW,OAAO,SAAS,UAAU;AACrB,sBAAA,KAAK,CAAC,CAAC,KAAK,OAAO,aAAa,GAAG,EAAE,CAAC;AACtC,sBAAA,KAAK,CAAC,CAAC,KAAK,OAAO,KAAK,GAAG,CAAC,CAAC;AAAA,MAAA,OACtC;AAES,sBAAA,KAAK,CAAC,CAAC,KAAK,OAAO,KAAK,GAAG,EAAE,CAAC;AAAA,MAAA;AAAA,IAC9C;AAEF,UAAM,SAAS,IAAIC,OAAS,SAAA,aAAa,CAAC;AAAA,EAAA;AAAA,EAGpC,WAAW;AACjB,SAAK,MAAM,IAAI;AAAA,EAAA;AAAA,EAGjB,QAAQ;AACF,QAAA,KAAK,UAAU,WAAW;AACtB,YAAA,IAAI,MAAM,0BAA0B;AAAA,IAC5C,WAAW,KAAK,UAAU,WAAW;AAC7B,YAAA,IAAI,MAAM,kBAAkB;AAAA,IAAA;AAI7B,WAAA,QAAQ,KAAK,gBAAgB,EAAE,QAAQ,CAAC,CAAC,KAAKJ,WAAU,MAAM;AACnE,YAAM,cAAcA,YAAW;AAAA,QAC7B,CAAC,YAAY;AACX,eAAK,mBAAmB,KAAK,SAASA,YAAW,OAAO,MAAM;AAC9D,eAAK,SAAS;AAAA,QAChB;AAAA,QACA,EAAE,qBAAqB,KAAK;AAAA,MAC9B;AAEK,WAAA,qBAAqB,KAAK,WAAW;AAAA,IAAA,CAC3C;AAED,SAAK,SAAS;AAEd,SAAK,QAAQ;AACb,WAAO,MAAM;AACX,WAAK,KAAK;AAAA,IACZ;AAAA,EAAA;AAAA,EAGF,OAAO;AACL,SAAK,qBAAqB,QAAQ,CAAC,gBAAgB,aAAa;AAChE,SAAK,uBAAuB,CAAC;AAC7B,SAAK,QAAQ;AAAA,EAAA;AAEjB;;;"}
@@ -1,20 +0,0 @@
1
- import { Collection } from '../collection.js';
2
- import { QueryBuilder } from './query-builder.js';
3
- import { Context, Schema } from './types.js';
4
- export declare function compileQuery<TContext extends Context<Schema>>(queryBuilder: QueryBuilder<TContext>): CompiledQuery<((TContext["result"] extends object ? TContext["result"] : TContext["hasJoin"] extends true ? TContext["schema"] : TContext["default"] extends keyof TContext["schema"] ? TContext["schema"][TContext["default"]] : never) extends infer T ? { [K in keyof T]: (TContext["result"] extends object ? TContext["result"] : TContext["hasJoin"] extends true ? TContext["schema"] : TContext["default"] extends keyof TContext["schema"] ? TContext["schema"][TContext["default"]] : never)[K]; } : never) & {
5
- _key?: string | number;
6
- }>;
7
- export declare class CompiledQuery<TResults extends object = Record<string, unknown>> {
8
- private graph;
9
- private inputs;
10
- private inputCollections;
11
- private resultCollection;
12
- state: `compiled` | `running` | `stopped`;
13
- private unsubscribeCallbacks;
14
- constructor(queryBuilder: QueryBuilder<Context<Schema>>);
15
- get results(): Collection<TResults, string | number, {}>;
16
- private sendChangesToInput;
17
- private runGraph;
18
- start(): () => void;
19
- stop(): void;
20
- }
@@ -1,161 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const extractors = require("./extractors.cjs");
4
- const utils = require("./utils.cjs");
5
- function evaluateWhereOnNamespacedRow(namespacedRow, where, mainTableAlias, joinedTableAlias) {
6
- return where.every((item) => {
7
- if (typeof item === `function`) {
8
- return item(namespacedRow);
9
- } else {
10
- return evaluateConditionOnNamespacedRow(
11
- namespacedRow,
12
- item,
13
- mainTableAlias,
14
- joinedTableAlias
15
- );
16
- }
17
- });
18
- }
19
- function evaluateConditionOnNamespacedRow(namespacedRow, condition, mainTableAlias, joinedTableAlias) {
20
- if (condition.length === 3 && !Array.isArray(condition[0])) {
21
- const [left, comparator, right] = condition;
22
- return evaluateSimpleConditionOnNamespacedRow(
23
- namespacedRow,
24
- left,
25
- comparator,
26
- right,
27
- mainTableAlias,
28
- joinedTableAlias
29
- );
30
- }
31
- if (condition.length > 3 && !Array.isArray(condition[0]) && typeof condition[1] === `string` && ![`and`, `or`].includes(condition[1])) {
32
- let result = evaluateSimpleConditionOnNamespacedRow(
33
- namespacedRow,
34
- condition[0],
35
- condition[1],
36
- condition[2],
37
- mainTableAlias,
38
- joinedTableAlias
39
- );
40
- for (let i = 3; i < condition.length; i += 4) {
41
- const logicalOp = condition[i];
42
- if (i + 3 <= condition.length) {
43
- const nextResult = evaluateSimpleConditionOnNamespacedRow(
44
- namespacedRow,
45
- condition[i + 1],
46
- condition[i + 2],
47
- condition[i + 3],
48
- mainTableAlias,
49
- joinedTableAlias
50
- );
51
- if (logicalOp === `and`) {
52
- result = result && nextResult;
53
- } else {
54
- result = result || nextResult;
55
- }
56
- }
57
- }
58
- return result;
59
- }
60
- if (condition.length > 0 && Array.isArray(condition[0])) {
61
- let result = evaluateConditionOnNamespacedRow(
62
- namespacedRow,
63
- condition[0],
64
- mainTableAlias,
65
- joinedTableAlias
66
- );
67
- for (let i = 1; i < condition.length; i += 2) {
68
- if (i + 1 >= condition.length) break;
69
- const operator = condition[i];
70
- const nextCondition = condition[i + 1];
71
- if (operator === `and`) {
72
- result = result && evaluateConditionOnNamespacedRow(
73
- namespacedRow,
74
- nextCondition,
75
- mainTableAlias,
76
- joinedTableAlias
77
- );
78
- } else {
79
- result = result || evaluateConditionOnNamespacedRow(
80
- namespacedRow,
81
- nextCondition,
82
- mainTableAlias,
83
- joinedTableAlias
84
- );
85
- }
86
- }
87
- return result;
88
- }
89
- return true;
90
- }
91
- function evaluateSimpleConditionOnNamespacedRow(namespacedRow, left, comparator, right, mainTableAlias, joinedTableAlias) {
92
- const leftValue = extractors.evaluateOperandOnNamespacedRow(
93
- namespacedRow,
94
- left,
95
- mainTableAlias,
96
- joinedTableAlias
97
- );
98
- const rightValue = extractors.evaluateOperandOnNamespacedRow(
99
- namespacedRow,
100
- right,
101
- mainTableAlias,
102
- joinedTableAlias
103
- );
104
- switch (comparator) {
105
- case `=`:
106
- return leftValue === rightValue;
107
- case `!=`:
108
- return leftValue !== rightValue;
109
- case `<`:
110
- return utils.compareValues(leftValue, rightValue, `<`);
111
- case `<=`:
112
- return utils.compareValues(leftValue, rightValue, `<=`);
113
- case `>`:
114
- return utils.compareValues(leftValue, rightValue, `>`);
115
- case `>=`:
116
- return utils.compareValues(leftValue, rightValue, `>=`);
117
- case `like`:
118
- case `not like`:
119
- if (typeof leftValue === `string` && typeof rightValue === `string`) {
120
- const pattern = utils.convertLikeToRegex(rightValue);
121
- const matches = new RegExp(`^${pattern}$`, `i`).test(leftValue);
122
- return comparator === `like` ? matches : !matches;
123
- }
124
- return comparator === `like` ? false : true;
125
- case `in`:
126
- if (!Array.isArray(rightValue)) {
127
- return false;
128
- }
129
- if (rightValue.length === 0) {
130
- return false;
131
- }
132
- if (Array.isArray(leftValue)) {
133
- return leftValue.some((item) => utils.isValueInArray(item, rightValue));
134
- }
135
- return utils.isValueInArray(leftValue, rightValue);
136
- case `not in`:
137
- if (!Array.isArray(rightValue)) {
138
- return true;
139
- }
140
- if (rightValue.length === 0) {
141
- return true;
142
- }
143
- if (Array.isArray(leftValue)) {
144
- return !leftValue.some((item) => utils.isValueInArray(item, rightValue));
145
- }
146
- return !utils.isValueInArray(leftValue, rightValue);
147
- case `is`:
148
- return leftValue === rightValue;
149
- case `is not`:
150
- if (rightValue === null) {
151
- return leftValue !== null && leftValue !== void 0;
152
- }
153
- return leftValue !== rightValue;
154
- default:
155
- return false;
156
- }
157
- }
158
- exports.evaluateConditionOnNamespacedRow = evaluateConditionOnNamespacedRow;
159
- exports.evaluateSimpleConditionOnNamespacedRow = evaluateSimpleConditionOnNamespacedRow;
160
- exports.evaluateWhereOnNamespacedRow = evaluateWhereOnNamespacedRow;
161
- //# sourceMappingURL=evaluators.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"evaluators.cjs","sources":["../../../src/query/evaluators.ts"],"sourcesContent":["import { evaluateOperandOnNamespacedRow } from \"./extractors.js\"\nimport { compareValues, convertLikeToRegex, isValueInArray } from \"./utils.js\"\nimport type {\n Comparator,\n Condition,\n ConditionOperand,\n LogicalOperator,\n SimpleCondition,\n Where,\n WhereCallback,\n} from \"./schema.js\"\nimport type { NamespacedRow } from \"../types.js\"\n\n/**\n * Evaluates a Where clause (which is always an array of conditions and/or callbacks) against a nested row structure\n */\nexport function evaluateWhereOnNamespacedRow(\n namespacedRow: NamespacedRow,\n where: Where,\n mainTableAlias?: string,\n joinedTableAlias?: string\n): boolean {\n // Where is always an array of conditions and/or callbacks\n // Evaluate all items and combine with AND logic\n return where.every((item) => {\n if (typeof item === `function`) {\n return (item as WhereCallback)(namespacedRow)\n } else {\n return evaluateConditionOnNamespacedRow(\n namespacedRow,\n item as Condition,\n mainTableAlias,\n joinedTableAlias\n )\n }\n })\n}\n\n/**\n * Evaluates a condition against a nested row structure\n */\nexport function evaluateConditionOnNamespacedRow(\n namespacedRow: NamespacedRow,\n condition: Condition,\n mainTableAlias?: string,\n joinedTableAlias?: string\n): boolean {\n // Handle simple conditions with exactly 3 elements\n if (condition.length === 3 && !Array.isArray(condition[0])) {\n const [left, comparator, right] = condition as SimpleCondition\n return evaluateSimpleConditionOnNamespacedRow(\n namespacedRow,\n left,\n comparator,\n right,\n mainTableAlias,\n joinedTableAlias\n )\n }\n\n // Handle flat composite conditions (multiple conditions in a single array)\n if (\n condition.length > 3 &&\n !Array.isArray(condition[0]) &&\n typeof condition[1] === `string` &&\n ![`and`, `or`].includes(condition[1] as string)\n ) {\n // Start with the first condition (first 3 elements)\n let result = evaluateSimpleConditionOnNamespacedRow(\n namespacedRow,\n condition[0],\n condition[1] as Comparator,\n condition[2],\n mainTableAlias,\n joinedTableAlias\n )\n\n // Process the rest in groups: logical operator, then 3 elements for each condition\n for (let i = 3; i < condition.length; i += 4) {\n const logicalOp = condition[i] as LogicalOperator\n\n // Make sure we have a complete condition to evaluate\n if (i + 3 <= condition.length) {\n const nextResult = evaluateSimpleConditionOnNamespacedRow(\n namespacedRow,\n condition[i + 1],\n condition[i + 2] as Comparator,\n condition[i + 3],\n mainTableAlias,\n joinedTableAlias\n )\n\n // Apply the logical operator\n if (logicalOp === `and`) {\n result = result && nextResult\n } else {\n // logicalOp === `or`\n result = result || nextResult\n }\n }\n }\n\n return result\n }\n\n // Handle nested composite conditions where the first element is an array\n if (condition.length > 0 && Array.isArray(condition[0])) {\n // Start with the first condition\n let result = evaluateConditionOnNamespacedRow(\n namespacedRow,\n condition[0] as Condition,\n mainTableAlias,\n joinedTableAlias\n )\n\n // Process the rest of the conditions and logical operators in pairs\n for (let i = 1; i < condition.length; i += 2) {\n if (i + 1 >= condition.length) break // Make sure we have a pair\n\n const operator = condition[i] as LogicalOperator\n const nextCondition = condition[i + 1] as Condition\n\n // Apply the logical operator\n if (operator === `and`) {\n result =\n result &&\n evaluateConditionOnNamespacedRow(\n namespacedRow,\n nextCondition,\n mainTableAlias,\n joinedTableAlias\n )\n } else {\n // logicalOp === `or`\n result =\n result ||\n evaluateConditionOnNamespacedRow(\n namespacedRow,\n nextCondition,\n mainTableAlias,\n joinedTableAlias\n )\n }\n }\n\n return result\n }\n\n // Fallback - this should not happen with valid conditions\n return true\n}\n\n/**\n * Evaluates a simple condition against a nested row structure\n */\nexport function evaluateSimpleConditionOnNamespacedRow(\n namespacedRow: Record<string, unknown>,\n left: ConditionOperand,\n comparator: Comparator,\n right: ConditionOperand,\n mainTableAlias?: string,\n joinedTableAlias?: string\n): boolean {\n const leftValue = evaluateOperandOnNamespacedRow(\n namespacedRow,\n left,\n mainTableAlias,\n joinedTableAlias\n )\n\n const rightValue = evaluateOperandOnNamespacedRow(\n namespacedRow,\n right,\n mainTableAlias,\n joinedTableAlias\n )\n\n // The rest of the function remains the same as evaluateSimpleCondition\n switch (comparator) {\n case `=`:\n return leftValue === rightValue\n case `!=`:\n return leftValue !== rightValue\n case `<`:\n return compareValues(leftValue, rightValue, `<`)\n case `<=`:\n return compareValues(leftValue, rightValue, `<=`)\n case `>`:\n return compareValues(leftValue, rightValue, `>`)\n case `>=`:\n return compareValues(leftValue, rightValue, `>=`)\n case `like`:\n case `not like`:\n if (typeof leftValue === `string` && typeof rightValue === `string`) {\n // Convert SQL LIKE pattern to proper regex pattern\n const pattern = convertLikeToRegex(rightValue)\n const matches = new RegExp(`^${pattern}$`, `i`).test(leftValue)\n return comparator === `like` ? matches : !matches\n }\n return comparator === `like` ? false : true\n case `in`:\n // If right value is not an array, we can't do an IN operation\n if (!Array.isArray(rightValue)) {\n return false\n }\n\n // For empty arrays, nothing is contained in them\n if (rightValue.length === 0) {\n return false\n }\n\n // Handle array-to-array comparison (check if any element in leftValue exists in rightValue)\n if (Array.isArray(leftValue)) {\n return leftValue.some((item) => isValueInArray(item, rightValue))\n }\n\n // Handle single value comparison\n return isValueInArray(leftValue, rightValue)\n\n case `not in`:\n // If right value is not an array, everything is \"not in\" it\n if (!Array.isArray(rightValue)) {\n return true\n }\n\n // For empty arrays, everything is \"not in\" them\n if (rightValue.length === 0) {\n return true\n }\n\n // Handle array-to-array comparison (check if no element in leftValue exists in rightValue)\n if (Array.isArray(leftValue)) {\n return !leftValue.some((item) => isValueInArray(item, rightValue))\n }\n\n // Handle single value comparison\n return !isValueInArray(leftValue, rightValue)\n\n case `is`:\n return leftValue === rightValue\n case `is not`:\n // Properly handle null/undefined checks\n if (rightValue === null) {\n return leftValue !== null && leftValue !== undefined\n }\n return leftValue !== rightValue\n default:\n return false\n }\n}\n"],"names":["evaluateOperandOnNamespacedRow","compareValues","convertLikeToRegex","isValueInArray"],"mappings":"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"}
@@ -1,14 +0,0 @@
1
- import { Comparator, Condition, ConditionOperand, Where } from './schema.js';
2
- import { NamespacedRow } from '../types.js';
3
- /**
4
- * Evaluates a Where clause (which is always an array of conditions and/or callbacks) against a nested row structure
5
- */
6
- export declare function evaluateWhereOnNamespacedRow(namespacedRow: NamespacedRow, where: Where, mainTableAlias?: string, joinedTableAlias?: string): boolean;
7
- /**
8
- * Evaluates a condition against a nested row structure
9
- */
10
- export declare function evaluateConditionOnNamespacedRow(namespacedRow: NamespacedRow, condition: Condition, mainTableAlias?: string, joinedTableAlias?: string): boolean;
11
- /**
12
- * Evaluates a simple condition against a nested row structure
13
- */
14
- export declare function evaluateSimpleConditionOnNamespacedRow(namespacedRow: Record<string, unknown>, left: ConditionOperand, comparator: Comparator, right: ConditionOperand, mainTableAlias?: string, joinedTableAlias?: string): boolean;
@@ -1,122 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const functions = require("./functions.cjs");
4
- function extractValueFromNamespacedRow(namespacedRow, columnRef, mainTableAlias, joinedTableAlias) {
5
- if (columnRef.includes(`.`)) {
6
- const [tableAlias, colName] = columnRef.split(`.`);
7
- const tableData = namespacedRow[tableAlias];
8
- if (!tableData) {
9
- return null;
10
- }
11
- const value = tableData[colName];
12
- return value;
13
- } else {
14
- if (mainTableAlias && namespacedRow[mainTableAlias]) {
15
- const mainTableData = namespacedRow[mainTableAlias];
16
- if (typeof mainTableData === `object` && columnRef in mainTableData) {
17
- return mainTableData[columnRef];
18
- }
19
- }
20
- if (joinedTableAlias && namespacedRow[joinedTableAlias]) {
21
- const joinedTableData = namespacedRow[joinedTableAlias];
22
- if (typeof joinedTableData === `object` && columnRef in joinedTableData) {
23
- return joinedTableData[columnRef];
24
- }
25
- }
26
- for (const [_tableAlias, tableData] of Object.entries(namespacedRow)) {
27
- if (tableData && typeof tableData === `object` && columnRef in tableData) {
28
- return tableData[columnRef];
29
- }
30
- }
31
- return void 0;
32
- }
33
- }
34
- function evaluateOperandOnNamespacedRow(namespacedRow, operand, mainTableAlias, joinedTableAlias) {
35
- if (typeof operand === `string` && operand.startsWith(`@`)) {
36
- const columnRef = operand.substring(1);
37
- return extractValueFromNamespacedRow(
38
- namespacedRow,
39
- columnRef,
40
- mainTableAlias,
41
- joinedTableAlias
42
- );
43
- }
44
- if (operand && typeof operand === `object` && `col` in operand) {
45
- const colRef = operand.col;
46
- if (typeof colRef === `string`) {
47
- const nestedValue = extractValueFromNamespacedRow(
48
- namespacedRow,
49
- colRef,
50
- mainTableAlias,
51
- joinedTableAlias
52
- );
53
- if (nestedValue === void 0 && colRef in namespacedRow) {
54
- return namespacedRow[colRef];
55
- }
56
- return nestedValue;
57
- }
58
- return void 0;
59
- }
60
- if (operand && typeof operand === `object` && functions.isFunctionCall(operand)) {
61
- const functionName = Object.keys(operand)[0];
62
- const args = operand[functionName];
63
- const evaluatedArgs = Array.isArray(args) ? args.map(
64
- (arg) => evaluateOperandOnNamespacedRow(
65
- namespacedRow,
66
- arg,
67
- mainTableAlias,
68
- joinedTableAlias
69
- )
70
- ) : evaluateOperandOnNamespacedRow(
71
- namespacedRow,
72
- args,
73
- mainTableAlias,
74
- joinedTableAlias
75
- );
76
- return functions.evaluateFunction(
77
- functionName,
78
- evaluatedArgs
79
- );
80
- }
81
- if (operand && typeof operand === `object` && `value` in operand) {
82
- return operand.value;
83
- }
84
- return operand;
85
- }
86
- function extractJoinKey(row, operand, defaultTableAlias) {
87
- let keyValue;
88
- if (typeof operand === `string` && operand.startsWith(`@`)) {
89
- const columnRef = operand.substring(1);
90
- if (columnRef.includes(`.`)) {
91
- const [tableAlias, colName] = columnRef.split(`.`);
92
- if (tableAlias === defaultTableAlias) {
93
- keyValue = row[colName];
94
- } else {
95
- keyValue = void 0;
96
- }
97
- } else {
98
- keyValue = row[columnRef];
99
- }
100
- } else if (operand && typeof operand === `object` && `col` in operand) {
101
- const colRef = operand.col;
102
- if (typeof colRef === `string`) {
103
- if (colRef.includes(`.`)) {
104
- const [tableAlias, colName] = colRef.split(`.`);
105
- if (tableAlias === defaultTableAlias) {
106
- keyValue = row[colName];
107
- } else {
108
- keyValue = void 0;
109
- }
110
- } else {
111
- keyValue = row[colRef];
112
- }
113
- }
114
- } else {
115
- keyValue = operand;
116
- }
117
- return keyValue;
118
- }
119
- exports.evaluateOperandOnNamespacedRow = evaluateOperandOnNamespacedRow;
120
- exports.extractJoinKey = extractJoinKey;
121
- exports.extractValueFromNamespacedRow = extractValueFromNamespacedRow;
122
- //# sourceMappingURL=extractors.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extractors.cjs","sources":["../../../src/query/extractors.ts"],"sourcesContent":["import { evaluateFunction, isFunctionCall } from \"./functions.js\"\nimport type { AllowedFunctionName, ConditionOperand } from \"./schema.js\"\n\n/**\n * Extracts a value from a nested row structure\n * @param namespacedRow The nested row structure\n * @param columnRef The column reference (may include table.column format)\n * @param mainTableAlias The main table alias to check first for columns without table reference\n * @param joinedTableAlias The joined table alias to check second for columns without table reference\n * @returns The extracted value or undefined if not found\n */\nexport function extractValueFromNamespacedRow(\n namespacedRow: Record<string, unknown>,\n columnRef: string,\n mainTableAlias?: string,\n joinedTableAlias?: string\n): unknown {\n // Check if it's a table.column reference\n if (columnRef.includes(`.`)) {\n const [tableAlias, colName] = columnRef.split(`.`) as [string, string]\n\n // Get the table data\n const tableData = namespacedRow[tableAlias] as\n | Record<string, unknown>\n | null\n | undefined\n\n if (!tableData) {\n return null\n }\n\n // Return the column value from that table\n const value = tableData[colName]\n return value\n } else {\n // If no table is specified, first try to find in the main table if provided\n if (mainTableAlias && namespacedRow[mainTableAlias]) {\n const mainTableData = namespacedRow[mainTableAlias] as Record<\n string,\n unknown\n >\n if (typeof mainTableData === `object` && columnRef in mainTableData) {\n return mainTableData[columnRef]\n }\n }\n\n // Then try the joined table if provided\n if (joinedTableAlias && namespacedRow[joinedTableAlias]) {\n const joinedTableData = namespacedRow[joinedTableAlias] as Record<\n string,\n unknown\n >\n if (typeof joinedTableData === `object` && columnRef in joinedTableData) {\n return joinedTableData[columnRef]\n }\n }\n\n // If not found in main or joined table, try to find the column in any table\n for (const [_tableAlias, tableData] of Object.entries(namespacedRow)) {\n if (\n tableData &&\n typeof tableData === `object` &&\n columnRef in (tableData as Record<string, unknown>)\n ) {\n return (tableData as Record<string, unknown>)[columnRef]\n }\n }\n return undefined\n }\n}\n\n/**\n * Evaluates an operand against a nested row structure\n */\nexport function evaluateOperandOnNamespacedRow(\n namespacedRow: Record<string, unknown>,\n operand: ConditionOperand,\n mainTableAlias?: string,\n joinedTableAlias?: string\n): unknown {\n // Handle column references\n if (typeof operand === `string` && operand.startsWith(`@`)) {\n const columnRef = operand.substring(1)\n return extractValueFromNamespacedRow(\n namespacedRow,\n columnRef,\n mainTableAlias,\n joinedTableAlias\n )\n }\n\n // Handle explicit column references\n if (operand && typeof operand === `object` && `col` in operand) {\n const colRef = (operand as { col: unknown }).col\n\n if (typeof colRef === `string`) {\n // First try to extract from nested row structure\n const nestedValue = extractValueFromNamespacedRow(\n namespacedRow,\n colRef,\n mainTableAlias,\n joinedTableAlias\n )\n\n // If not found in nested structure, check if it's a direct property of the row\n // This is important for HAVING clauses that reference aggregated values\n if (nestedValue === undefined && colRef in namespacedRow) {\n return namespacedRow[colRef]\n }\n\n return nestedValue\n }\n\n return undefined\n }\n\n // Handle function calls\n if (operand && typeof operand === `object` && isFunctionCall(operand)) {\n // Get the function name (the only key in the object)\n const functionName = Object.keys(operand)[0] as AllowedFunctionName\n // Get the arguments using type assertion with specific function name\n const args = (operand as any)[functionName]\n\n // If the arguments are a reference or another expression, evaluate them first\n const evaluatedArgs = Array.isArray(args)\n ? args.map((arg) =>\n evaluateOperandOnNamespacedRow(\n namespacedRow,\n arg as ConditionOperand,\n mainTableAlias,\n joinedTableAlias\n )\n )\n : evaluateOperandOnNamespacedRow(\n namespacedRow,\n args as ConditionOperand,\n mainTableAlias,\n joinedTableAlias\n )\n\n // Call the function with the evaluated arguments\n return evaluateFunction(\n functionName,\n evaluatedArgs as ConditionOperand | Array<ConditionOperand>\n )\n }\n\n // Handle explicit literals\n if (operand && typeof operand === `object` && `value` in operand) {\n return (operand as { value: unknown }).value\n }\n\n // Handle literal values\n return operand\n}\n\n/**\n * Extracts a join key value from a row based on the operand\n * @param row The data row (not nested)\n * @param operand The operand to extract the key from\n * @param defaultTableAlias The default table alias\n * @returns The extracted key value\n */\nexport function extractJoinKey<T extends Record<string, unknown>>(\n row: T,\n operand: ConditionOperand,\n defaultTableAlias?: string\n): unknown {\n let keyValue: unknown\n\n // Handle column references (e.g., \"@orders.id\" or \"@id\")\n if (typeof operand === `string` && operand.startsWith(`@`)) {\n const columnRef = operand.substring(1)\n\n // If it contains a dot, extract the table and column\n if (columnRef.includes(`.`)) {\n const [tableAlias, colName] = columnRef.split(`.`) as [string, string]\n // If this is referencing the current table, extract from row directly\n if (tableAlias === defaultTableAlias) {\n keyValue = row[colName]\n } else {\n // This might be a column from another table, return undefined\n keyValue = undefined\n }\n } else {\n // No table specified, look directly in the row\n keyValue = row[columnRef]\n }\n } else if (operand && typeof operand === `object` && `col` in operand) {\n // Handle explicit column references like { col: \"orders.id\" } or { col: \"id\" }\n const colRef = (operand as { col: unknown }).col\n\n if (typeof colRef === `string`) {\n if (colRef.includes(`.`)) {\n const [tableAlias, colName] = colRef.split(`.`) as [string, string]\n // If this is referencing the current table, extract from row directly\n if (tableAlias === defaultTableAlias) {\n keyValue = row[colName]\n } else {\n // This might be a column from another table, return undefined\n keyValue = undefined\n }\n } else {\n // No table specified, look directly in the row\n keyValue = row[colRef]\n }\n }\n } else {\n // Handle literals or other types\n keyValue = operand\n }\n\n return keyValue\n}\n"],"names":["isFunctionCall","evaluateFunction"],"mappings":"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"}