@tanstack/db 0.5.33 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/collection/change-events.cjs.map +1 -1
- package/dist/cjs/collection/change-events.d.cts +3 -2
- package/dist/cjs/collection/changes.cjs +13 -4
- package/dist/cjs/collection/changes.cjs.map +1 -1
- package/dist/cjs/collection/changes.d.cts +10 -1
- package/dist/cjs/collection/cleanup-queue.cjs +89 -0
- package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
- package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
- package/dist/cjs/collection/events.cjs +14 -0
- package/dist/cjs/collection/events.cjs.map +1 -1
- package/dist/cjs/collection/events.d.cts +39 -1
- package/dist/cjs/collection/index.cjs +66 -28
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +49 -36
- package/dist/cjs/collection/indexes.cjs +211 -62
- package/dist/cjs/collection/indexes.cjs.map +1 -1
- package/dist/cjs/collection/indexes.d.cts +27 -17
- package/dist/cjs/collection/lifecycle.cjs +5 -22
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/lifecycle.d.cts +0 -1
- package/dist/cjs/collection/mutations.cjs +18 -0
- package/dist/cjs/collection/mutations.cjs.map +1 -1
- package/dist/cjs/collection/mutations.d.cts +1 -0
- package/dist/cjs/collection/state.cjs +381 -53
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +65 -1
- package/dist/cjs/collection/subscription.cjs +6 -0
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/collection/subscription.d.cts +4 -0
- package/dist/cjs/collection/sync.cjs +108 -1
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/collection/sync.d.cts +2 -0
- package/dist/cjs/collection/transaction-metadata.cjs +5 -0
- package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
- package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
- package/dist/cjs/errors.cjs +8 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +3 -0
- package/dist/cjs/index.cjs +22 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +11 -3
- package/dist/cjs/indexes/auto-index.cjs +13 -6
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.cjs +0 -3
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +2 -6
- package/dist/cjs/indexes/basic-index.cjs +361 -0
- package/dist/cjs/indexes/basic-index.cjs.map +1 -0
- package/dist/cjs/indexes/basic-index.d.cts +102 -0
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +1 -1
- package/dist/cjs/indexes/index-options.d.cts +8 -9
- package/dist/cjs/indexes/index-registry.cjs +89 -0
- package/dist/cjs/indexes/index-registry.cjs.map +1 -0
- package/dist/cjs/indexes/index-registry.d.cts +61 -0
- package/dist/cjs/local-only.cjs +5 -0
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +27 -11
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +25 -3
- package/dist/cjs/query/builder/index.cjs +200 -39
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +4 -3
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
- package/dist/cjs/query/builder/types.d.cts +84 -19
- package/dist/cjs/query/compiler/evaluators.cjs +51 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +100 -28
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.d.cts +4 -2
- package/dist/cjs/query/compiler/index.cjs +283 -11
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +30 -2
- package/dist/cjs/query/compiler/order-by.cjs +29 -10
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -1
- package/dist/cjs/query/compiler/select.cjs +8 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/index.d.cts +2 -1
- package/dist/cjs/query/ir.cjs +18 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +21 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +501 -5
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
- package/dist/cjs/query/live/types.d.cts +3 -3
- package/dist/cjs/query/live/utils.cjs +43 -3
- package/dist/cjs/query/live/utils.cjs.map +1 -1
- package/dist/cjs/query/live/utils.d.cts +1 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +9 -6
- package/dist/cjs/query/query-once.cjs.map +1 -1
- package/dist/cjs/query/query-once.d.cts +7 -5
- package/dist/cjs/query/subset-dedupe.cjs +9 -3
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/cjs/types.d.cts +42 -8
- package/dist/cjs/utils/array-utils.cjs +27 -0
- package/dist/cjs/utils/array-utils.cjs.map +1 -0
- package/dist/cjs/utils/array-utils.d.cts +16 -0
- package/dist/cjs/utils/comparison.cjs +11 -0
- package/dist/cjs/utils/comparison.cjs.map +1 -1
- package/dist/cjs/utils/index-optimization.cjs +4 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -1
- package/dist/cjs/utils.cjs +7 -9
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -1
- package/dist/cjs/virtual-props.cjs +33 -0
- package/dist/cjs/virtual-props.cjs.map +1 -0
- package/dist/cjs/virtual-props.d.cts +196 -0
- package/dist/esm/collection/change-events.d.ts +3 -2
- package/dist/esm/collection/change-events.js.map +1 -1
- package/dist/esm/collection/changes.d.ts +10 -1
- package/dist/esm/collection/changes.js +13 -4
- package/dist/esm/collection/changes.js.map +1 -1
- package/dist/esm/collection/cleanup-queue.d.ts +30 -0
- package/dist/esm/collection/cleanup-queue.js +89 -0
- package/dist/esm/collection/cleanup-queue.js.map +1 -0
- package/dist/esm/collection/events.d.ts +39 -1
- package/dist/esm/collection/events.js +14 -0
- package/dist/esm/collection/events.js.map +1 -1
- package/dist/esm/collection/index.d.ts +49 -36
- package/dist/esm/collection/index.js +67 -29
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/indexes.d.ts +27 -17
- package/dist/esm/collection/indexes.js +211 -62
- package/dist/esm/collection/indexes.js.map +1 -1
- package/dist/esm/collection/lifecycle.d.ts +0 -1
- package/dist/esm/collection/lifecycle.js +5 -22
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/mutations.d.ts +1 -0
- package/dist/esm/collection/mutations.js +18 -0
- package/dist/esm/collection/mutations.js.map +1 -1
- package/dist/esm/collection/state.d.ts +65 -1
- package/dist/esm/collection/state.js +381 -53
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/subscription.d.ts +4 -0
- package/dist/esm/collection/subscription.js +6 -0
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/collection/sync.d.ts +2 -0
- package/dist/esm/collection/sync.js +108 -1
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/collection/transaction-metadata.d.ts +1 -0
- package/dist/esm/collection/transaction-metadata.js +5 -0
- package/dist/esm/collection/transaction-metadata.js.map +1 -0
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.js +8 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +11 -3
- package/dist/esm/index.js +25 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.js +13 -6
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +2 -6
- package/dist/esm/indexes/base-index.js +1 -4
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/basic-index.d.ts +102 -0
- package/dist/esm/indexes/basic-index.js +361 -0
- package/dist/esm/indexes/basic-index.js.map +1 -0
- package/dist/esm/indexes/btree-index.d.ts +1 -1
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/indexes/index-options.d.ts +8 -9
- package/dist/esm/indexes/index-registry.d.ts +61 -0
- package/dist/esm/indexes/index-registry.js +89 -0
- package/dist/esm/indexes/index-registry.js.map +1 -0
- package/dist/esm/local-only.js +5 -0
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +25 -3
- package/dist/esm/query/builder/functions.js +27 -11
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +4 -3
- package/dist/esm/query/builder/index.js +201 -40
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +84 -19
- package/dist/esm/query/compiler/evaluators.js +51 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.d.ts +4 -2
- package/dist/esm/query/compiler/group-by.js +101 -29
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +30 -2
- package/dist/esm/query/compiler/index.js +285 -13
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -1
- package/dist/esm/query/compiler/order-by.js +30 -11
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js +8 -0
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/index.d.ts +2 -1
- package/dist/esm/query/ir.d.ts +21 -1
- package/dist/esm/query/ir.js +18 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
- package/dist/esm/query/live/collection-config-builder.js +503 -7
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/types.d.ts +3 -3
- package/dist/esm/query/live/utils.d.ts +1 -0
- package/dist/esm/query/live/utils.js +43 -3
- package/dist/esm/query/live/utils.js.map +1 -1
- package/dist/esm/query/live-query-collection.d.ts +9 -6
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/query-once.d.ts +7 -5
- package/dist/esm/query/query-once.js.map +1 -1
- package/dist/esm/query/subset-dedupe.js +9 -3
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/dist/esm/types.d.ts +42 -8
- package/dist/esm/utils/array-utils.d.ts +16 -0
- package/dist/esm/utils/array-utils.js +27 -0
- package/dist/esm/utils/array-utils.js.map +1 -0
- package/dist/esm/utils/comparison.js +11 -0
- package/dist/esm/utils/comparison.js.map +1 -1
- package/dist/esm/utils/index-optimization.js +4 -0
- package/dist/esm/utils/index-optimization.js.map +1 -1
- package/dist/esm/utils.d.ts +6 -1
- package/dist/esm/utils.js +7 -9
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/virtual-props.d.ts +196 -0
- package/dist/esm/virtual-props.js +33 -0
- package/dist/esm/virtual-props.js.map +1 -0
- package/package.json +4 -3
- package/skills/db-core/SKILL.md +4 -2
- package/skills/db-core/collection-setup/SKILL.md +30 -11
- package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
- package/skills/db-core/collection-setup/references/powersync-adapter.md +4 -0
- package/skills/db-core/collection-setup/references/query-adapter.md +32 -0
- package/skills/db-core/custom-adapter/SKILL.md +58 -9
- package/skills/db-core/live-queries/SKILL.md +162 -2
- package/skills/db-core/mutations-optimistic/SKILL.md +1 -1
- package/skills/db-core/persistence/SKILL.md +241 -0
- package/skills/meta-framework/SKILL.md +1 -1
- package/src/collection/change-events.ts +13 -9
- package/src/collection/changes.ts +30 -7
- package/src/collection/cleanup-queue.ts +105 -0
- package/src/collection/events.ts +65 -0
- package/src/collection/index.ts +110 -45
- package/src/collection/indexes.ts +283 -76
- package/src/collection/lifecycle.ts +5 -26
- package/src/collection/mutations.ts +21 -0
- package/src/collection/state.ts +545 -71
- package/src/collection/subscription.ts +7 -0
- package/src/collection/sync.ts +137 -0
- package/src/collection/transaction-metadata.ts +1 -0
- package/src/errors.ts +9 -0
- package/src/index.ts +46 -3
- package/src/indexes/auto-index.ts +18 -8
- package/src/indexes/base-index.ts +2 -10
- package/src/indexes/basic-index.ts +507 -0
- package/src/indexes/btree-index.ts +1 -1
- package/src/indexes/index-options.ts +17 -37
- package/src/indexes/index-registry.ts +174 -0
- package/src/local-only.ts +7 -0
- package/src/query/builder/functions.ts +84 -7
- package/src/query/builder/index.ts +329 -9
- package/src/query/builder/ref-proxy.ts +22 -4
- package/src/query/builder/types.ts +257 -62
- package/src/query/compiler/evaluators.ts +57 -0
- package/src/query/compiler/group-by.ts +156 -35
- package/src/query/compiler/index.ts +445 -15
- package/src/query/compiler/order-by.ts +51 -12
- package/src/query/compiler/select.ts +9 -0
- package/src/query/index.ts +7 -0
- package/src/query/ir.ts +23 -2
- package/src/query/live/collection-config-builder.ts +809 -9
- package/src/query/live/types.ts +10 -4
- package/src/query/live/utils.ts +64 -3
- package/src/query/live-query-collection.ts +43 -18
- package/src/query/query-once.ts +31 -12
- package/src/query/subset-dedupe.ts +11 -7
- package/src/types.ts +49 -9
- package/src/utils/array-utils.ts +49 -0
- package/src/utils/comparison.ts +14 -0
- package/src/utils/index-optimization.ts +4 -0
- package/src/utils.ts +12 -9
- package/src/virtual-props.ts +282 -0
- package/dist/cjs/indexes/lazy-index.cjs +0 -190
- package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
- package/dist/cjs/indexes/lazy-index.d.cts +0 -96
- package/dist/esm/indexes/lazy-index.d.ts +0 -96
- package/dist/esm/indexes/lazy-index.js +0 -190
- package/dist/esm/indexes/lazy-index.js.map +0 -1
- package/src/indexes/lazy-index.ts +0 -251
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
Aggregate as AggregateExpr,
|
|
4
4
|
CollectionRef,
|
|
5
5
|
Func as FuncExpr,
|
|
6
|
+
INCLUDES_SCALAR_FIELD,
|
|
7
|
+
IncludesSubquery,
|
|
6
8
|
PropRef,
|
|
7
9
|
QueryRef,
|
|
8
10
|
Value as ValueExpr,
|
|
@@ -23,14 +25,17 @@ import {
|
|
|
23
25
|
isRefProxy,
|
|
24
26
|
toExpression,
|
|
25
27
|
} from './ref-proxy.js'
|
|
28
|
+
import { ConcatToArrayWrapper, ToArrayWrapper } from './functions.js'
|
|
26
29
|
import type { NamespacedRow, SingleResult } from '../../types.js'
|
|
27
30
|
import type {
|
|
28
31
|
Aggregate,
|
|
29
32
|
BasicExpression,
|
|
33
|
+
IncludesMaterialization,
|
|
30
34
|
JoinClause,
|
|
31
35
|
OrderBy,
|
|
32
36
|
OrderByDirection,
|
|
33
37
|
QueryIR,
|
|
38
|
+
Where,
|
|
34
39
|
} from '../ir.js'
|
|
35
40
|
import type {
|
|
36
41
|
CompareOptions,
|
|
@@ -41,10 +46,13 @@ import type {
|
|
|
41
46
|
JoinOnCallback,
|
|
42
47
|
MergeContextForJoinCallback,
|
|
43
48
|
MergeContextWithJoinType,
|
|
49
|
+
NonScalarSelectObject,
|
|
44
50
|
OrderByCallback,
|
|
45
51
|
OrderByOptions,
|
|
46
52
|
RefsForContext,
|
|
47
53
|
ResultTypeFromSelect,
|
|
54
|
+
ResultTypeFromSelectValue,
|
|
55
|
+
ScalarSelectValue,
|
|
48
56
|
SchemaFromSource,
|
|
49
57
|
SelectObject,
|
|
50
58
|
Source,
|
|
@@ -486,12 +494,30 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
486
494
|
* ```
|
|
487
495
|
*/
|
|
488
496
|
select<TSelectObject extends SelectObject>(
|
|
489
|
-
callback: (
|
|
490
|
-
|
|
497
|
+
callback: (
|
|
498
|
+
refs: RefsForContext<TContext>,
|
|
499
|
+
) => NonScalarSelectObject<TSelectObject>,
|
|
500
|
+
): QueryBuilder<WithResult<TContext, ResultTypeFromSelect<TSelectObject>>>
|
|
501
|
+
select<TSelectValue extends ScalarSelectValue>(
|
|
502
|
+
callback: (refs: RefsForContext<TContext>) => TSelectValue,
|
|
503
|
+
): QueryBuilder<WithResult<TContext, ResultTypeFromSelectValue<TSelectValue>>>
|
|
504
|
+
select(
|
|
505
|
+
callback: (
|
|
506
|
+
refs: RefsForContext<TContext>,
|
|
507
|
+
) => SelectObject | ScalarSelectValue,
|
|
508
|
+
) {
|
|
491
509
|
const aliases = this._getCurrentAliases()
|
|
492
510
|
const refProxy = createRefProxy(aliases) as RefsForContext<TContext>
|
|
493
|
-
|
|
494
|
-
|
|
511
|
+
let selectObject = callback(refProxy)
|
|
512
|
+
|
|
513
|
+
// Returning a top-level alias directly is equivalent to spreading it.
|
|
514
|
+
// Leaf refs like `row.name` must remain scalar selections.
|
|
515
|
+
if (isRefProxy(selectObject) && selectObject.__path.length === 1) {
|
|
516
|
+
const sentinelKey = `__SPREAD_SENTINEL__${selectObject.__path[0]}__0`
|
|
517
|
+
selectObject = { [sentinelKey]: true }
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const select = buildNestedSelect(selectObject, aliases)
|
|
495
521
|
|
|
496
522
|
return new BaseQueryBuilder({
|
|
497
523
|
...this.query,
|
|
@@ -676,7 +702,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
676
702
|
* // Get countries our users are from
|
|
677
703
|
* query
|
|
678
704
|
* .from({ users: usersCollection })
|
|
679
|
-
* .select(({users}) => users.country)
|
|
705
|
+
* .select(({users}) => ({ country: users.country }))
|
|
680
706
|
* .distinct()
|
|
681
707
|
* ```
|
|
682
708
|
*/
|
|
@@ -706,7 +732,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
706
732
|
// TODO: enforcing return only one result with also a default orderBy if none is specified
|
|
707
733
|
// limit: 1,
|
|
708
734
|
singleResult: true,
|
|
709
|
-
})
|
|
735
|
+
}) as any
|
|
710
736
|
}
|
|
711
737
|
|
|
712
738
|
// Helper methods
|
|
@@ -769,7 +795,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
769
795
|
...builder.query,
|
|
770
796
|
select: undefined, // remove the select clause if it exists
|
|
771
797
|
fnSelect: callback,
|
|
772
|
-
})
|
|
798
|
+
}) as any
|
|
773
799
|
},
|
|
774
800
|
/**
|
|
775
801
|
* Filter rows using a function that operates on each row
|
|
@@ -867,7 +893,7 @@ function isPlainObject(value: any): value is Record<string, any> {
|
|
|
867
893
|
)
|
|
868
894
|
}
|
|
869
895
|
|
|
870
|
-
function buildNestedSelect(obj: any): any {
|
|
896
|
+
function buildNestedSelect(obj: any, parentAliases: Array<string> = []): any {
|
|
871
897
|
if (!isPlainObject(obj)) return toExpr(obj)
|
|
872
898
|
const out: Record<string, any> = {}
|
|
873
899
|
for (const [k, v] of Object.entries(obj)) {
|
|
@@ -876,11 +902,305 @@ function buildNestedSelect(obj: any): any {
|
|
|
876
902
|
out[k] = v
|
|
877
903
|
continue
|
|
878
904
|
}
|
|
879
|
-
|
|
905
|
+
if (v instanceof BaseQueryBuilder) {
|
|
906
|
+
out[k] = buildIncludesSubquery(v, k, parentAliases, `collection`)
|
|
907
|
+
continue
|
|
908
|
+
}
|
|
909
|
+
if (v instanceof ToArrayWrapper) {
|
|
910
|
+
if (!(v.query instanceof BaseQueryBuilder)) {
|
|
911
|
+
throw new Error(`toArray() must wrap a subquery builder`)
|
|
912
|
+
}
|
|
913
|
+
out[k] = buildIncludesSubquery(v.query, k, parentAliases, `array`)
|
|
914
|
+
continue
|
|
915
|
+
}
|
|
916
|
+
if (v instanceof ConcatToArrayWrapper) {
|
|
917
|
+
if (!(v.query instanceof BaseQueryBuilder)) {
|
|
918
|
+
throw new Error(`concat(toArray(...)) must wrap a subquery builder`)
|
|
919
|
+
}
|
|
920
|
+
out[k] = buildIncludesSubquery(v.query, k, parentAliases, `concat`)
|
|
921
|
+
continue
|
|
922
|
+
}
|
|
923
|
+
out[k] = buildNestedSelect(v, parentAliases)
|
|
880
924
|
}
|
|
881
925
|
return out
|
|
882
926
|
}
|
|
883
927
|
|
|
928
|
+
/**
|
|
929
|
+
* Recursively collects all PropRef nodes from an expression tree.
|
|
930
|
+
*/
|
|
931
|
+
function collectRefsFromExpression(expr: BasicExpression): Array<PropRef> {
|
|
932
|
+
const refs: Array<PropRef> = []
|
|
933
|
+
switch (expr.type) {
|
|
934
|
+
case `ref`:
|
|
935
|
+
refs.push(expr)
|
|
936
|
+
break
|
|
937
|
+
case `func`:
|
|
938
|
+
for (const arg of (expr as any).args ?? []) {
|
|
939
|
+
refs.push(...collectRefsFromExpression(arg))
|
|
940
|
+
}
|
|
941
|
+
break
|
|
942
|
+
default:
|
|
943
|
+
break
|
|
944
|
+
}
|
|
945
|
+
return refs
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Checks whether a WHERE clause references any parent alias.
|
|
950
|
+
*/
|
|
951
|
+
function referencesParent(where: Where, parentAliases: Array<string>): boolean {
|
|
952
|
+
const expr =
|
|
953
|
+
typeof where === `object` && `expression` in where
|
|
954
|
+
? where.expression
|
|
955
|
+
: where
|
|
956
|
+
return collectRefsFromExpression(expr).some(
|
|
957
|
+
(ref) => ref.path[0] != null && parentAliases.includes(ref.path[0]),
|
|
958
|
+
)
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Builds an IncludesSubquery IR node from a child query builder.
|
|
963
|
+
* Extracts the correlation condition from the child's WHERE clauses by finding
|
|
964
|
+
* an eq() predicate that references both a parent alias and a child alias.
|
|
965
|
+
*/
|
|
966
|
+
function buildIncludesSubquery(
|
|
967
|
+
childBuilder: BaseQueryBuilder,
|
|
968
|
+
fieldName: string,
|
|
969
|
+
parentAliases: Array<string>,
|
|
970
|
+
materialization: IncludesMaterialization,
|
|
971
|
+
): IncludesSubquery {
|
|
972
|
+
const childQuery = childBuilder._getQuery()
|
|
973
|
+
|
|
974
|
+
// Collect child's own aliases
|
|
975
|
+
const childAliases: Array<string> = [childQuery.from.alias]
|
|
976
|
+
if (childQuery.join) {
|
|
977
|
+
for (const j of childQuery.join) {
|
|
978
|
+
childAliases.push(j.from.alias)
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// Walk child's WHERE clauses to find the correlation condition.
|
|
983
|
+
// The correlation eq() may be a standalone WHERE or nested inside a top-level and().
|
|
984
|
+
let parentRef: PropRef | undefined
|
|
985
|
+
let childRef: PropRef | undefined
|
|
986
|
+
let correlationWhereIndex = -1
|
|
987
|
+
let correlationAndArgIndex = -1 // >= 0 when found inside an and()
|
|
988
|
+
|
|
989
|
+
if (childQuery.where) {
|
|
990
|
+
for (let i = 0; i < childQuery.where.length; i++) {
|
|
991
|
+
const where = childQuery.where[i]!
|
|
992
|
+
const expr =
|
|
993
|
+
typeof where === `object` && `expression` in where
|
|
994
|
+
? where.expression
|
|
995
|
+
: where
|
|
996
|
+
|
|
997
|
+
// Try standalone eq()
|
|
998
|
+
if (
|
|
999
|
+
expr.type === `func` &&
|
|
1000
|
+
expr.name === `eq` &&
|
|
1001
|
+
expr.args.length === 2
|
|
1002
|
+
) {
|
|
1003
|
+
const result = extractCorrelation(
|
|
1004
|
+
expr.args[0]!,
|
|
1005
|
+
expr.args[1]!,
|
|
1006
|
+
parentAliases,
|
|
1007
|
+
childAliases,
|
|
1008
|
+
)
|
|
1009
|
+
if (result) {
|
|
1010
|
+
parentRef = result.parentRef
|
|
1011
|
+
childRef = result.childRef
|
|
1012
|
+
correlationWhereIndex = i
|
|
1013
|
+
break
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// Try inside top-level and()
|
|
1018
|
+
if (
|
|
1019
|
+
expr.type === `func` &&
|
|
1020
|
+
expr.name === `and` &&
|
|
1021
|
+
expr.args.length >= 2
|
|
1022
|
+
) {
|
|
1023
|
+
for (let j = 0; j < expr.args.length; j++) {
|
|
1024
|
+
const arg = expr.args[j]!
|
|
1025
|
+
if (
|
|
1026
|
+
arg.type === `func` &&
|
|
1027
|
+
arg.name === `eq` &&
|
|
1028
|
+
arg.args.length === 2
|
|
1029
|
+
) {
|
|
1030
|
+
const result = extractCorrelation(
|
|
1031
|
+
arg.args[0]!,
|
|
1032
|
+
arg.args[1]!,
|
|
1033
|
+
parentAliases,
|
|
1034
|
+
childAliases,
|
|
1035
|
+
)
|
|
1036
|
+
if (result) {
|
|
1037
|
+
parentRef = result.parentRef
|
|
1038
|
+
childRef = result.childRef
|
|
1039
|
+
correlationWhereIndex = i
|
|
1040
|
+
correlationAndArgIndex = j
|
|
1041
|
+
break
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
if (parentRef) break
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
if (!parentRef || !childRef || correlationWhereIndex === -1) {
|
|
1051
|
+
throw new Error(
|
|
1052
|
+
`Includes subquery for "${fieldName}" must have a WHERE clause with an eq() condition ` +
|
|
1053
|
+
`that correlates a parent field with a child field. ` +
|
|
1054
|
+
`Example: .where(({child}) => eq(child.parentId, parent.id))`,
|
|
1055
|
+
)
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// Remove the correlation eq() from the child query's WHERE clauses.
|
|
1059
|
+
// If it was inside an and(), remove just that arg (collapsing the and() if needed).
|
|
1060
|
+
const modifiedWhere = [...childQuery.where!]
|
|
1061
|
+
if (correlationAndArgIndex >= 0) {
|
|
1062
|
+
const where = modifiedWhere[correlationWhereIndex]!
|
|
1063
|
+
const expr =
|
|
1064
|
+
typeof where === `object` && `expression` in where
|
|
1065
|
+
? where.expression
|
|
1066
|
+
: where
|
|
1067
|
+
const remainingArgs = (expr as any).args.filter(
|
|
1068
|
+
(_: any, idx: number) => idx !== correlationAndArgIndex,
|
|
1069
|
+
)
|
|
1070
|
+
if (remainingArgs.length === 1) {
|
|
1071
|
+
// Collapse and() with single remaining arg to just that expression
|
|
1072
|
+
const isResidual =
|
|
1073
|
+
typeof where === `object` && `expression` in where && where.residual
|
|
1074
|
+
modifiedWhere[correlationWhereIndex] = isResidual
|
|
1075
|
+
? { expression: remainingArgs[0], residual: true }
|
|
1076
|
+
: remainingArgs[0]
|
|
1077
|
+
} else {
|
|
1078
|
+
// Rebuild and() without the extracted arg
|
|
1079
|
+
const newAnd = new FuncExpr(`and`, remainingArgs)
|
|
1080
|
+
const isResidual =
|
|
1081
|
+
typeof where === `object` && `expression` in where && where.residual
|
|
1082
|
+
modifiedWhere[correlationWhereIndex] = isResidual
|
|
1083
|
+
? { expression: newAnd, residual: true }
|
|
1084
|
+
: newAnd
|
|
1085
|
+
}
|
|
1086
|
+
} else {
|
|
1087
|
+
modifiedWhere.splice(correlationWhereIndex, 1)
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
// Separate remaining WHEREs into pure-child vs parent-referencing
|
|
1091
|
+
const pureChildWhere: Array<Where> = []
|
|
1092
|
+
const parentFilters: Array<Where> = []
|
|
1093
|
+
for (const w of modifiedWhere) {
|
|
1094
|
+
if (referencesParent(w, parentAliases)) {
|
|
1095
|
+
parentFilters.push(w)
|
|
1096
|
+
} else {
|
|
1097
|
+
pureChildWhere.push(w)
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// Collect distinct parent PropRefs from parent-referencing filters
|
|
1102
|
+
let parentProjection: Array<PropRef> | undefined
|
|
1103
|
+
if (parentFilters.length > 0) {
|
|
1104
|
+
const seen = new Set<string>()
|
|
1105
|
+
parentProjection = []
|
|
1106
|
+
for (const w of parentFilters) {
|
|
1107
|
+
const expr = typeof w === `object` && `expression` in w ? w.expression : w
|
|
1108
|
+
for (const ref of collectRefsFromExpression(expr)) {
|
|
1109
|
+
if (
|
|
1110
|
+
ref.path[0] != null &&
|
|
1111
|
+
parentAliases.includes(ref.path[0]) &&
|
|
1112
|
+
!seen.has(ref.path.join(`.`))
|
|
1113
|
+
) {
|
|
1114
|
+
seen.add(ref.path.join(`.`))
|
|
1115
|
+
parentProjection.push(ref)
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
const modifiedQuery: QueryIR = {
|
|
1122
|
+
...childQuery,
|
|
1123
|
+
where: pureChildWhere.length > 0 ? pureChildWhere : undefined,
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
const rawChildSelect = modifiedQuery.select as any
|
|
1127
|
+
const hasObjectSelect =
|
|
1128
|
+
rawChildSelect === undefined || isPlainObject(rawChildSelect)
|
|
1129
|
+
let includesQuery = modifiedQuery
|
|
1130
|
+
let scalarField: string | undefined
|
|
1131
|
+
|
|
1132
|
+
if (materialization === `concat`) {
|
|
1133
|
+
if (rawChildSelect === undefined || hasObjectSelect) {
|
|
1134
|
+
throw new Error(
|
|
1135
|
+
`concat(toArray(...)) for "${fieldName}" requires the subquery to select a scalar value`,
|
|
1136
|
+
)
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
if (!hasObjectSelect) {
|
|
1141
|
+
if (materialization === `collection`) {
|
|
1142
|
+
throw new Error(
|
|
1143
|
+
`Includes subquery for "${fieldName}" must select an object when materializing as a Collection`,
|
|
1144
|
+
)
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
scalarField = INCLUDES_SCALAR_FIELD
|
|
1148
|
+
includesQuery = {
|
|
1149
|
+
...modifiedQuery,
|
|
1150
|
+
select: {
|
|
1151
|
+
[scalarField]: rawChildSelect,
|
|
1152
|
+
},
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
return new IncludesSubquery(
|
|
1157
|
+
includesQuery,
|
|
1158
|
+
parentRef,
|
|
1159
|
+
childRef,
|
|
1160
|
+
fieldName,
|
|
1161
|
+
parentFilters.length > 0 ? parentFilters : undefined,
|
|
1162
|
+
parentProjection,
|
|
1163
|
+
materialization,
|
|
1164
|
+
scalarField,
|
|
1165
|
+
)
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Checks if two eq() arguments form a parent-child correlation.
|
|
1170
|
+
* Returns the parent and child PropRefs if found, undefined otherwise.
|
|
1171
|
+
*/
|
|
1172
|
+
function extractCorrelation(
|
|
1173
|
+
argA: BasicExpression,
|
|
1174
|
+
argB: BasicExpression,
|
|
1175
|
+
parentAliases: Array<string>,
|
|
1176
|
+
childAliases: Array<string>,
|
|
1177
|
+
): { parentRef: PropRef; childRef: PropRef } | undefined {
|
|
1178
|
+
if (argA.type === `ref` && argB.type === `ref`) {
|
|
1179
|
+
const aAlias = argA.path[0]
|
|
1180
|
+
const bAlias = argB.path[0]
|
|
1181
|
+
|
|
1182
|
+
if (
|
|
1183
|
+
aAlias &&
|
|
1184
|
+
bAlias &&
|
|
1185
|
+
parentAliases.includes(aAlias) &&
|
|
1186
|
+
childAliases.includes(bAlias)
|
|
1187
|
+
) {
|
|
1188
|
+
return { parentRef: argA, childRef: argB }
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
if (
|
|
1192
|
+
aAlias &&
|
|
1193
|
+
bAlias &&
|
|
1194
|
+
parentAliases.includes(bAlias) &&
|
|
1195
|
+
childAliases.includes(aAlias)
|
|
1196
|
+
) {
|
|
1197
|
+
return { parentRef: argB, childRef: argA }
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
return undefined
|
|
1202
|
+
}
|
|
1203
|
+
|
|
884
1204
|
// Internal function to build a query from a callback
|
|
885
1205
|
// used by liveQueryCollectionOptions.query
|
|
886
1206
|
export function buildQuery<TContext extends Context>(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PropRef, Value } from '../ir.js'
|
|
2
2
|
import type { BasicExpression } from '../ir.js'
|
|
3
3
|
import type { RefLeaf } from './types.js'
|
|
4
|
+
import type { VirtualRowProps } from '../../virtual-props.js'
|
|
4
5
|
|
|
5
6
|
export interface RefProxy<T = any> {
|
|
6
7
|
/** @internal */
|
|
@@ -11,18 +12,35 @@ export interface RefProxy<T = any> {
|
|
|
11
12
|
readonly __type: T
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Virtual properties available on all row ref proxies.
|
|
17
|
+
* These allow querying on sync status, origin, key, and collection ID.
|
|
18
|
+
*/
|
|
19
|
+
export type VirtualPropsRefProxy<
|
|
20
|
+
TKey extends string | number = string | number,
|
|
21
|
+
> = {
|
|
22
|
+
readonly [K in keyof VirtualRowProps<TKey>]: RefLeaf<VirtualRowProps<TKey>[K]>
|
|
23
|
+
}
|
|
24
|
+
|
|
14
25
|
/**
|
|
15
26
|
* Type for creating a RefProxy for a single row/type without namespacing
|
|
16
27
|
* Used in collection indexes and where clauses
|
|
28
|
+
*
|
|
29
|
+
* Includes virtual properties ($synced, $origin, $key, $collectionId) for
|
|
30
|
+
* querying on sync status and row metadata.
|
|
17
31
|
*/
|
|
18
|
-
export type SingleRowRefProxy<
|
|
32
|
+
export type SingleRowRefProxy<
|
|
33
|
+
T,
|
|
34
|
+
TKey extends string | number = string | number,
|
|
35
|
+
> =
|
|
19
36
|
T extends Record<string, any>
|
|
20
37
|
? {
|
|
21
38
|
[K in keyof T]: T[K] extends Record<string, any>
|
|
22
|
-
? SingleRowRefProxy<T[K]> & RefProxy<T[K]>
|
|
39
|
+
? SingleRowRefProxy<T[K], TKey> & RefProxy<T[K]>
|
|
23
40
|
: RefLeaf<T[K]>
|
|
24
|
-
} & RefProxy<T>
|
|
25
|
-
|
|
41
|
+
} & RefProxy<T> &
|
|
42
|
+
VirtualPropsRefProxy<TKey>
|
|
43
|
+
: RefProxy<T> & VirtualPropsRefProxy<TKey>
|
|
26
44
|
|
|
27
45
|
/**
|
|
28
46
|
* Creates a proxy object that records property access paths for a single row
|