@tanstack/db 0.1.12 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/errors.cjs +18 -6
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +9 -3
- package/dist/cjs/index.cjs +3 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +4 -1
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +38 -21
- package/dist/cjs/query/builder/index.cjs +25 -16
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +8 -8
- package/dist/cjs/query/builder/ref-proxy.cjs +12 -8
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +2 -1
- package/dist/cjs/query/builder/types.d.cts +493 -28
- package/dist/cjs/query/compiler/evaluators.cjs +29 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +4 -2
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.cjs +13 -4
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.cjs +70 -60
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/select.cjs +131 -42
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/compiler/select.d.cts +1 -5
- package/dist/cjs/query/ir.cjs +4 -0
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +6 -1
- package/dist/cjs/query/optimizer.cjs +61 -20
- package/dist/cjs/query/optimizer.cjs.map +1 -1
- package/dist/esm/errors.d.ts +9 -3
- package/dist/esm/errors.js +18 -6
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.js +4 -2
- package/dist/esm/query/builder/functions.d.ts +38 -21
- package/dist/esm/query/builder/functions.js +4 -1
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +8 -8
- package/dist/esm/query/builder/index.js +27 -18
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +2 -1
- package/dist/esm/query/builder/ref-proxy.js +12 -8
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +493 -28
- package/dist/esm/query/compiler/evaluators.js +29 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.js +4 -2
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.js +15 -6
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.js +71 -61
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/select.d.ts +1 -5
- package/dist/esm/query/compiler/select.js +131 -42
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/ir.d.ts +6 -1
- package/dist/esm/query/ir.js +4 -0
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/optimizer.js +62 -21
- package/dist/esm/query/optimizer.js.map +1 -1
- package/package.json +2 -2
- package/src/errors.ts +17 -10
- package/src/query/builder/functions.ts +176 -108
- package/src/query/builder/index.ts +68 -48
- package/src/query/builder/ref-proxy.ts +14 -20
- package/src/query/builder/types.ts +622 -110
- package/src/query/compiler/evaluators.ts +30 -0
- package/src/query/compiler/group-by.ts +6 -1
- package/src/query/compiler/index.ts +23 -6
- package/src/query/compiler/joins.ts +132 -101
- package/src/query/compiler/select.ts +206 -113
- package/src/query/ir.ts +14 -1
- package/src/query/optimizer.ts +131 -59
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { CollectionImpl } from "../../collection.js"
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Aggregate as AggregateExpr,
|
|
4
|
+
CollectionRef,
|
|
5
|
+
Func as FuncExpr,
|
|
6
|
+
PropRef,
|
|
7
|
+
QueryRef,
|
|
8
|
+
Value as ValueExpr,
|
|
9
|
+
isExpressionLike,
|
|
10
|
+
} from "../ir.js"
|
|
3
11
|
import {
|
|
4
12
|
InvalidSourceError,
|
|
5
13
|
JoinConditionMustBeEqualityError,
|
|
@@ -7,7 +15,7 @@ import {
|
|
|
7
15
|
QueryMustHaveFromClauseError,
|
|
8
16
|
SubQueryMustHaveFromClauseError,
|
|
9
17
|
} from "../../errors.js"
|
|
10
|
-
import { createRefProxy,
|
|
18
|
+
import { createRefProxy, toExpression } from "./ref-proxy.js"
|
|
11
19
|
import type { NamespacedRow } from "../../types.js"
|
|
12
20
|
import type {
|
|
13
21
|
Aggregate,
|
|
@@ -22,11 +30,11 @@ import type {
|
|
|
22
30
|
Context,
|
|
23
31
|
GroupByCallback,
|
|
24
32
|
JoinOnCallback,
|
|
25
|
-
|
|
33
|
+
MergeContextForJoinCallback,
|
|
26
34
|
MergeContextWithJoinType,
|
|
27
35
|
OrderByCallback,
|
|
28
36
|
OrderByOptions,
|
|
29
|
-
|
|
37
|
+
RefsForContext,
|
|
30
38
|
ResultTypeFromSelect,
|
|
31
39
|
SchemaFromSource,
|
|
32
40
|
SelectObject,
|
|
@@ -141,7 +149,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
141
149
|
>(
|
|
142
150
|
source: TSource,
|
|
143
151
|
onCallback: JoinOnCallback<
|
|
144
|
-
|
|
152
|
+
MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>
|
|
145
153
|
>,
|
|
146
154
|
type: TJoinType = `left` as TJoinType
|
|
147
155
|
): QueryBuilder<
|
|
@@ -152,8 +160,8 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
152
160
|
// Create a temporary context for the callback
|
|
153
161
|
const currentAliases = this._getCurrentAliases()
|
|
154
162
|
const newAliases = [...currentAliases, alias]
|
|
155
|
-
const refProxy = createRefProxy(newAliases) as
|
|
156
|
-
|
|
163
|
+
const refProxy = createRefProxy(newAliases) as RefsForContext<
|
|
164
|
+
MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>
|
|
157
165
|
>
|
|
158
166
|
|
|
159
167
|
// Get the join condition expression
|
|
@@ -208,7 +216,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
208
216
|
leftJoin<TSource extends Source>(
|
|
209
217
|
source: TSource,
|
|
210
218
|
onCallback: JoinOnCallback<
|
|
211
|
-
|
|
219
|
+
MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>
|
|
212
220
|
>
|
|
213
221
|
): QueryBuilder<
|
|
214
222
|
MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `left`>
|
|
@@ -234,7 +242,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
234
242
|
rightJoin<TSource extends Source>(
|
|
235
243
|
source: TSource,
|
|
236
244
|
onCallback: JoinOnCallback<
|
|
237
|
-
|
|
245
|
+
MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>
|
|
238
246
|
>
|
|
239
247
|
): QueryBuilder<
|
|
240
248
|
MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `right`>
|
|
@@ -260,7 +268,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
260
268
|
innerJoin<TSource extends Source>(
|
|
261
269
|
source: TSource,
|
|
262
270
|
onCallback: JoinOnCallback<
|
|
263
|
-
|
|
271
|
+
MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>
|
|
264
272
|
>
|
|
265
273
|
): QueryBuilder<
|
|
266
274
|
MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `inner`>
|
|
@@ -286,7 +294,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
286
294
|
fullJoin<TSource extends Source>(
|
|
287
295
|
source: TSource,
|
|
288
296
|
onCallback: JoinOnCallback<
|
|
289
|
-
|
|
297
|
+
MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>
|
|
290
298
|
>
|
|
291
299
|
): QueryBuilder<
|
|
292
300
|
MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `full`>
|
|
@@ -324,7 +332,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
324
332
|
*/
|
|
325
333
|
where(callback: WhereCallback<TContext>): QueryBuilder<TContext> {
|
|
326
334
|
const aliases = this._getCurrentAliases()
|
|
327
|
-
const refProxy = createRefProxy(aliases) as
|
|
335
|
+
const refProxy = createRefProxy(aliases) as RefsForContext<TContext>
|
|
328
336
|
const expression = callback(refProxy)
|
|
329
337
|
|
|
330
338
|
const existingWhere = this.query.where || []
|
|
@@ -365,7 +373,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
365
373
|
*/
|
|
366
374
|
having(callback: WhereCallback<TContext>): QueryBuilder<TContext> {
|
|
367
375
|
const aliases = this._getCurrentAliases()
|
|
368
|
-
const refProxy = createRefProxy(aliases) as
|
|
376
|
+
const refProxy = createRefProxy(aliases) as RefsForContext<TContext>
|
|
369
377
|
const expression = callback(refProxy)
|
|
370
378
|
|
|
371
379
|
const existingHaving = this.query.having || []
|
|
@@ -411,42 +419,16 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
411
419
|
* ```
|
|
412
420
|
*/
|
|
413
421
|
select<TSelectObject extends SelectObject>(
|
|
414
|
-
callback: (refs:
|
|
422
|
+
callback: (refs: RefsForContext<TContext>) => TSelectObject
|
|
415
423
|
): QueryBuilder<WithResult<TContext, ResultTypeFromSelect<TSelectObject>>> {
|
|
416
424
|
const aliases = this._getCurrentAliases()
|
|
417
|
-
const refProxy = createRefProxy(aliases) as
|
|
425
|
+
const refProxy = createRefProxy(aliases) as RefsForContext<TContext>
|
|
418
426
|
const selectObject = callback(refProxy)
|
|
419
|
-
|
|
420
|
-
// Check if any tables were spread during the callback
|
|
421
|
-
const spreadSentinels = (refProxy as any).__spreadSentinels as Set<string>
|
|
422
|
-
|
|
423
|
-
// Convert the select object to use expressions, including spread sentinels
|
|
424
|
-
const select: Record<string, BasicExpression | Aggregate> = {}
|
|
425
|
-
|
|
426
|
-
// First, add spread sentinels for any tables that were spread
|
|
427
|
-
for (const spreadAlias of spreadSentinels) {
|
|
428
|
-
const sentinelKey = `__SPREAD_SENTINEL__${spreadAlias}`
|
|
429
|
-
select[sentinelKey] = toExpression(spreadAlias) // Use alias as a simple reference
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// Then add the explicit select fields
|
|
433
|
-
for (const [key, value] of Object.entries(selectObject)) {
|
|
434
|
-
if (isRefProxy(value)) {
|
|
435
|
-
select[key] = toExpression(value)
|
|
436
|
-
} else if (
|
|
437
|
-
typeof value === `object` &&
|
|
438
|
-
`type` in value &&
|
|
439
|
-
(value.type === `agg` || value.type === `func`)
|
|
440
|
-
) {
|
|
441
|
-
select[key] = value as BasicExpression | Aggregate
|
|
442
|
-
} else {
|
|
443
|
-
select[key] = toExpression(value)
|
|
444
|
-
}
|
|
445
|
-
}
|
|
427
|
+
const select = buildNestedSelect(selectObject)
|
|
446
428
|
|
|
447
429
|
return new BaseQueryBuilder({
|
|
448
430
|
...this.query,
|
|
449
|
-
select,
|
|
431
|
+
select: select,
|
|
450
432
|
fnSelect: undefined, // remove the fnSelect clause if it exists
|
|
451
433
|
}) as any
|
|
452
434
|
}
|
|
@@ -482,7 +464,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
482
464
|
options: OrderByDirection | OrderByOptions = `asc`
|
|
483
465
|
): QueryBuilder<TContext> {
|
|
484
466
|
const aliases = this._getCurrentAliases()
|
|
485
|
-
const refProxy = createRefProxy(aliases) as
|
|
467
|
+
const refProxy = createRefProxy(aliases) as RefsForContext<TContext>
|
|
486
468
|
const result = callback(refProxy)
|
|
487
469
|
|
|
488
470
|
const opts: CompareOptions =
|
|
@@ -550,17 +532,18 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
550
532
|
*/
|
|
551
533
|
groupBy(callback: GroupByCallback<TContext>): QueryBuilder<TContext> {
|
|
552
534
|
const aliases = this._getCurrentAliases()
|
|
553
|
-
const refProxy = createRefProxy(aliases) as
|
|
535
|
+
const refProxy = createRefProxy(aliases) as RefsForContext<TContext>
|
|
554
536
|
const result = callback(refProxy)
|
|
555
537
|
|
|
556
538
|
const newExpressions = Array.isArray(result)
|
|
557
539
|
? result.map((r) => toExpression(r))
|
|
558
540
|
: [toExpression(result)]
|
|
559
541
|
|
|
560
|
-
//
|
|
542
|
+
// Extend existing groupBy expressions (multiple groupBy calls should accumulate)
|
|
543
|
+
const existingGroupBy = this.query.groupBy || []
|
|
561
544
|
return new BaseQueryBuilder({
|
|
562
545
|
...this.query,
|
|
563
|
-
groupBy: newExpressions,
|
|
546
|
+
groupBy: [...existingGroupBy, ...newExpressions],
|
|
564
547
|
}) as any
|
|
565
548
|
}
|
|
566
549
|
|
|
@@ -758,6 +741,43 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
758
741
|
}
|
|
759
742
|
}
|
|
760
743
|
|
|
744
|
+
// Helper to ensure we have a BasicExpression/Aggregate for a value
|
|
745
|
+
function toExpr(value: any): BasicExpression | Aggregate {
|
|
746
|
+
if (value === undefined) return toExpression(null)
|
|
747
|
+
if (
|
|
748
|
+
value instanceof AggregateExpr ||
|
|
749
|
+
value instanceof FuncExpr ||
|
|
750
|
+
value instanceof PropRef ||
|
|
751
|
+
value instanceof ValueExpr
|
|
752
|
+
) {
|
|
753
|
+
return value as BasicExpression | Aggregate
|
|
754
|
+
}
|
|
755
|
+
return toExpression(value)
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function isPlainObject(value: any): value is Record<string, any> {
|
|
759
|
+
return (
|
|
760
|
+
value !== null &&
|
|
761
|
+
typeof value === `object` &&
|
|
762
|
+
!isExpressionLike(value) &&
|
|
763
|
+
!value.__refProxy
|
|
764
|
+
)
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
function buildNestedSelect(obj: any): any {
|
|
768
|
+
if (!isPlainObject(obj)) return toExpr(obj)
|
|
769
|
+
const out: Record<string, any> = {}
|
|
770
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
771
|
+
if (typeof k === `string` && k.startsWith(`__SPREAD_SENTINEL__`)) {
|
|
772
|
+
// Preserve sentinel key and its value (value is unimportant at compile time)
|
|
773
|
+
out[k] = v
|
|
774
|
+
continue
|
|
775
|
+
}
|
|
776
|
+
out[k] = buildNestedSelect(v)
|
|
777
|
+
}
|
|
778
|
+
return out
|
|
779
|
+
}
|
|
780
|
+
|
|
761
781
|
// Internal function to build a query from a callback
|
|
762
782
|
// used by liveQueryCollectionOptions.query
|
|
763
783
|
export function buildQuery<TContext extends Context>(
|
|
@@ -797,4 +817,4 @@ export type ExtractContext<T> =
|
|
|
797
817
|
: never
|
|
798
818
|
|
|
799
819
|
// Export the types from types.ts for convenience
|
|
800
|
-
export type { Context, Source, GetResult } from "./types.js"
|
|
820
|
+
export type { Context, Source, GetResult, RefLeaf as Ref } from "./types.js"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PropRef, Value } from "../ir.js"
|
|
2
2
|
import type { BasicExpression } from "../ir.js"
|
|
3
|
+
import type { RefLeaf } from "./types.js"
|
|
3
4
|
|
|
4
5
|
export interface RefProxy<T = any> {
|
|
5
6
|
/** @internal */
|
|
@@ -19,7 +20,7 @@ export type SingleRowRefProxy<T> =
|
|
|
19
20
|
? {
|
|
20
21
|
[K in keyof T]: T[K] extends Record<string, any>
|
|
21
22
|
? SingleRowRefProxy<T[K]> & RefProxy<T[K]>
|
|
22
|
-
:
|
|
23
|
+
: RefLeaf<T[K]>
|
|
23
24
|
} & RefProxy<T>
|
|
24
25
|
: RefProxy<T>
|
|
25
26
|
|
|
@@ -83,7 +84,7 @@ export function createRefProxy<T extends Record<string, any>>(
|
|
|
83
84
|
aliases: Array<string>
|
|
84
85
|
): RefProxy<T> & T {
|
|
85
86
|
const cache = new Map<string, any>()
|
|
86
|
-
|
|
87
|
+
let accessId = 0 // Monotonic counter to record evaluation order
|
|
87
88
|
|
|
88
89
|
function createProxy(path: Array<string>): any {
|
|
89
90
|
const pathKey = path.join(`.`)
|
|
@@ -109,10 +110,14 @@ export function createRefProxy<T extends Record<string, any>>(
|
|
|
109
110
|
},
|
|
110
111
|
|
|
111
112
|
ownKeys(target) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
const id = ++accessId
|
|
114
|
+
const sentinelKey = `__SPREAD_SENTINEL__${path.join(`.`)}__${id}`
|
|
115
|
+
if (!Object.prototype.hasOwnProperty.call(target, sentinelKey)) {
|
|
116
|
+
Object.defineProperty(target, sentinelKey, {
|
|
117
|
+
enumerable: true,
|
|
118
|
+
configurable: true,
|
|
119
|
+
value: true,
|
|
120
|
+
})
|
|
116
121
|
}
|
|
117
122
|
return Reflect.ownKeys(target)
|
|
118
123
|
},
|
|
@@ -135,7 +140,6 @@ export function createRefProxy<T extends Record<string, any>>(
|
|
|
135
140
|
if (prop === `__refProxy`) return true
|
|
136
141
|
if (prop === `__path`) return []
|
|
137
142
|
if (prop === `__type`) return undefined // Type is only for TypeScript inference
|
|
138
|
-
if (prop === `__spreadSentinels`) return spreadSentinels // Expose spread sentinels
|
|
139
143
|
if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver)
|
|
140
144
|
|
|
141
145
|
const propStr = String(prop)
|
|
@@ -147,28 +151,18 @@ export function createRefProxy<T extends Record<string, any>>(
|
|
|
147
151
|
},
|
|
148
152
|
|
|
149
153
|
has(target, prop) {
|
|
150
|
-
if (
|
|
151
|
-
prop === `__refProxy` ||
|
|
152
|
-
prop === `__path` ||
|
|
153
|
-
prop === `__type` ||
|
|
154
|
-
prop === `__spreadSentinels`
|
|
155
|
-
)
|
|
154
|
+
if (prop === `__refProxy` || prop === `__path` || prop === `__type`)
|
|
156
155
|
return true
|
|
157
156
|
if (typeof prop === `string` && aliases.includes(prop)) return true
|
|
158
157
|
return Reflect.has(target, prop)
|
|
159
158
|
},
|
|
160
159
|
|
|
161
160
|
ownKeys(_target) {
|
|
162
|
-
return [...aliases, `__refProxy`, `__path`, `__type
|
|
161
|
+
return [...aliases, `__refProxy`, `__path`, `__type`]
|
|
163
162
|
},
|
|
164
163
|
|
|
165
164
|
getOwnPropertyDescriptor(target, prop) {
|
|
166
|
-
if (
|
|
167
|
-
prop === `__refProxy` ||
|
|
168
|
-
prop === `__path` ||
|
|
169
|
-
prop === `__type` ||
|
|
170
|
-
prop === `__spreadSentinels`
|
|
171
|
-
) {
|
|
165
|
+
if (prop === `__refProxy` || prop === `__path` || prop === `__type`) {
|
|
172
166
|
return { enumerable: false, configurable: true }
|
|
173
167
|
}
|
|
174
168
|
if (typeof prop === `string` && aliases.includes(prop)) {
|