@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
package/src/query/live/types.ts
CHANGED
|
@@ -5,7 +5,11 @@ import type {
|
|
|
5
5
|
StringCollationConfig,
|
|
6
6
|
} from '../../types.js'
|
|
7
7
|
import type { InitialQueryBuilder, QueryBuilder } from '../builder/index.js'
|
|
8
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
Context,
|
|
10
|
+
RootObjectResultConstraint,
|
|
11
|
+
RootQueryResult,
|
|
12
|
+
} from '../builder/types.js'
|
|
9
13
|
|
|
10
14
|
export type Changes<T> = {
|
|
11
15
|
deletes: number
|
|
@@ -54,7 +58,7 @@ export type FullSyncState = Required<Omit<SyncState, `flushPendingChanges`>> &
|
|
|
54
58
|
*/
|
|
55
59
|
export interface LiveQueryCollectionConfig<
|
|
56
60
|
TContext extends Context,
|
|
57
|
-
TResult extends object =
|
|
61
|
+
TResult extends object = RootQueryResult<TContext>,
|
|
58
62
|
> {
|
|
59
63
|
/**
|
|
60
64
|
* Unique identifier for the collection
|
|
@@ -66,8 +70,10 @@ export interface LiveQueryCollectionConfig<
|
|
|
66
70
|
* Query builder function that defines the live query
|
|
67
71
|
*/
|
|
68
72
|
query:
|
|
69
|
-
| ((
|
|
70
|
-
|
|
73
|
+
| ((
|
|
74
|
+
q: InitialQueryBuilder,
|
|
75
|
+
) => QueryBuilder<TContext> & RootObjectResultConstraint<TContext>)
|
|
76
|
+
| (QueryBuilder<TContext> & RootObjectResultConstraint<TContext>)
|
|
71
77
|
|
|
72
78
|
/**
|
|
73
79
|
* Function to extract the key from result items
|
package/src/query/live/utils.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { MultiSet, serializeValue } from '@tanstack/db-ivm'
|
|
2
|
+
import { UnsupportedRootScalarSelectError } from '../../errors.js'
|
|
2
3
|
import { normalizeOrderByPaths } from '../compiler/expressions.js'
|
|
3
4
|
import { buildQuery, getQueryIR } from '../builder/index.js'
|
|
5
|
+
import { IncludesSubquery } from '../ir.js'
|
|
4
6
|
import type { MultiSetArray, RootStreamBuilder } from '@tanstack/db-ivm'
|
|
5
7
|
import type { Collection } from '../../collection/index.js'
|
|
6
8
|
import type { ChangeMessage } from '../../types.js'
|
|
@@ -44,6 +46,24 @@ export function extractCollectionsFromQuery(
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
}
|
|
49
|
+
|
|
50
|
+
// Extract from SELECT (for IncludesSubquery)
|
|
51
|
+
if (q.select) {
|
|
52
|
+
extractFromSelect(q.select)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function extractFromSelect(select: any) {
|
|
57
|
+
for (const [key, value] of Object.entries(select)) {
|
|
58
|
+
if (typeof key === `string` && key.startsWith(`__SPREAD_SENTINEL__`)) {
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
if (value instanceof IncludesSubquery) {
|
|
62
|
+
extractFromQuery(value.query)
|
|
63
|
+
} else if (isNestedSelectObject(value)) {
|
|
64
|
+
extractFromSelect(value)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
47
67
|
}
|
|
48
68
|
|
|
49
69
|
// Start extraction from the root query
|
|
@@ -117,6 +137,19 @@ export function extractCollectionAliases(
|
|
|
117
137
|
}
|
|
118
138
|
}
|
|
119
139
|
|
|
140
|
+
function traverseSelect(select: any) {
|
|
141
|
+
for (const [key, value] of Object.entries(select)) {
|
|
142
|
+
if (typeof key === `string` && key.startsWith(`__SPREAD_SENTINEL__`)) {
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
if (value instanceof IncludesSubquery) {
|
|
146
|
+
traverse(value.query)
|
|
147
|
+
} else if (isNestedSelectObject(value)) {
|
|
148
|
+
traverseSelect(value)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
120
153
|
function traverse(q?: QueryIR) {
|
|
121
154
|
if (!q) return
|
|
122
155
|
|
|
@@ -127,6 +160,10 @@ export function extractCollectionAliases(
|
|
|
127
160
|
recordAlias(joinClause.from)
|
|
128
161
|
}
|
|
129
162
|
}
|
|
163
|
+
|
|
164
|
+
if (q.select) {
|
|
165
|
+
traverseSelect(q.select)
|
|
166
|
+
}
|
|
130
167
|
}
|
|
131
168
|
|
|
132
169
|
traverse(query)
|
|
@@ -134,6 +171,19 @@ export function extractCollectionAliases(
|
|
|
134
171
|
return aliasesById
|
|
135
172
|
}
|
|
136
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Check if a value is a nested select object (plain object, not an expression)
|
|
176
|
+
*/
|
|
177
|
+
function isNestedSelectObject(obj: any): boolean {
|
|
178
|
+
if (obj === null || typeof obj !== `object`) return false
|
|
179
|
+
if (obj instanceof IncludesSubquery) return false
|
|
180
|
+
// Expression-like objects have a .type property
|
|
181
|
+
if (`type` in obj && typeof obj.type === `string`) return false
|
|
182
|
+
// Ref proxies from spread operations
|
|
183
|
+
if (obj.__refProxy) return false
|
|
184
|
+
return true
|
|
185
|
+
}
|
|
186
|
+
|
|
137
187
|
/**
|
|
138
188
|
* Builds a query IR from a config object that contains either a query builder
|
|
139
189
|
* function or a QueryBuilder instance.
|
|
@@ -142,12 +192,23 @@ export function buildQueryFromConfig<TContext extends Context>(config: {
|
|
|
142
192
|
query:
|
|
143
193
|
| ((q: InitialQueryBuilder) => QueryBuilder<TContext>)
|
|
144
194
|
| QueryBuilder<TContext>
|
|
195
|
+
requireObjectResult?: boolean
|
|
145
196
|
}): QueryIR {
|
|
146
197
|
// Build the query using the provided query builder function or instance
|
|
147
|
-
|
|
148
|
-
|
|
198
|
+
const query =
|
|
199
|
+
typeof config.query === `function`
|
|
200
|
+
? buildQuery<TContext>(config.query)
|
|
201
|
+
: getQueryIR(config.query)
|
|
202
|
+
|
|
203
|
+
if (
|
|
204
|
+
config.requireObjectResult &&
|
|
205
|
+
query.select &&
|
|
206
|
+
!isNestedSelectObject(query.select)
|
|
207
|
+
) {
|
|
208
|
+
throw new UnsupportedRootScalarSelectError()
|
|
149
209
|
}
|
|
150
|
-
|
|
210
|
+
|
|
211
|
+
return query
|
|
151
212
|
}
|
|
152
213
|
|
|
153
214
|
/**
|
|
@@ -6,7 +6,11 @@ import {
|
|
|
6
6
|
} from './live/collection-registry.js'
|
|
7
7
|
import type { LiveQueryCollectionUtils } from './live/collection-config-builder.js'
|
|
8
8
|
import type { LiveQueryCollectionConfig } from './live/types.js'
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
ExtractContext,
|
|
11
|
+
InitialQueryBuilder,
|
|
12
|
+
QueryBuilder,
|
|
13
|
+
} from './builder/index.js'
|
|
10
14
|
import type { Collection } from '../collection/index.js'
|
|
11
15
|
import type {
|
|
12
16
|
CollectionConfig,
|
|
@@ -15,7 +19,13 @@ import type {
|
|
|
15
19
|
SingleResult,
|
|
16
20
|
UtilsRecord,
|
|
17
21
|
} from '../types.js'
|
|
18
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
Context,
|
|
24
|
+
RootObjectResultConstraint,
|
|
25
|
+
RootQueryBuilder,
|
|
26
|
+
RootQueryFn,
|
|
27
|
+
RootQueryResult,
|
|
28
|
+
} from './builder/types.js'
|
|
19
29
|
|
|
20
30
|
type CollectionConfigForContext<
|
|
21
31
|
TContext extends Context,
|
|
@@ -60,10 +70,13 @@ type CollectionForContext<
|
|
|
60
70
|
* @returns Collection options that can be passed to createCollection
|
|
61
71
|
*/
|
|
62
72
|
export function liveQueryCollectionOptions<
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
TQuery extends QueryBuilder<any>,
|
|
74
|
+
TContext extends Context = ExtractContext<TQuery>,
|
|
75
|
+
TResult extends object = RootQueryResult<TContext>,
|
|
65
76
|
>(
|
|
66
|
-
config: LiveQueryCollectionConfig<TContext, TResult
|
|
77
|
+
config: LiveQueryCollectionConfig<TContext, TResult> & {
|
|
78
|
+
query: RootQueryFn<TQuery> | RootQueryBuilder<TQuery>
|
|
79
|
+
},
|
|
67
80
|
): CollectionConfigForContext<TContext, TResult> & {
|
|
68
81
|
utils: LiveQueryCollectionUtils
|
|
69
82
|
} {
|
|
@@ -113,34 +126,42 @@ export function liveQueryCollectionOptions<
|
|
|
113
126
|
|
|
114
127
|
// Overload 1: Accept just the query function
|
|
115
128
|
export function createLiveQueryCollection<
|
|
116
|
-
|
|
117
|
-
|
|
129
|
+
TQueryFn extends (q: InitialQueryBuilder) => QueryBuilder<any>,
|
|
130
|
+
TQuery extends QueryBuilder<any> = ReturnType<TQueryFn>,
|
|
118
131
|
>(
|
|
119
|
-
query:
|
|
120
|
-
): CollectionForContext<
|
|
132
|
+
query: TQueryFn & RootQueryFn<TQuery>,
|
|
133
|
+
): CollectionForContext<
|
|
134
|
+
ExtractContext<TQuery>,
|
|
135
|
+
RootQueryResult<ExtractContext<TQuery>>
|
|
136
|
+
> & {
|
|
121
137
|
utils: LiveQueryCollectionUtils
|
|
122
138
|
}
|
|
123
139
|
|
|
124
140
|
// Overload 2: Accept full config object with optional utilities
|
|
125
141
|
export function createLiveQueryCollection<
|
|
126
|
-
|
|
127
|
-
|
|
142
|
+
TQuery extends QueryBuilder<any>,
|
|
143
|
+
TContext extends Context = ExtractContext<TQuery>,
|
|
128
144
|
TUtils extends UtilsRecord = {},
|
|
129
145
|
>(
|
|
130
|
-
config: LiveQueryCollectionConfig<TContext,
|
|
131
|
-
|
|
146
|
+
config: LiveQueryCollectionConfig<TContext, RootQueryResult<TContext>> & {
|
|
147
|
+
query: RootQueryFn<TQuery> | RootQueryBuilder<TQuery>
|
|
148
|
+
utils?: TUtils
|
|
149
|
+
},
|
|
150
|
+
): CollectionForContext<TContext, RootQueryResult<TContext>> & {
|
|
132
151
|
utils: LiveQueryCollectionUtils & TUtils
|
|
133
152
|
}
|
|
134
153
|
|
|
135
154
|
// Implementation
|
|
136
155
|
export function createLiveQueryCollection<
|
|
137
156
|
TContext extends Context,
|
|
138
|
-
TResult extends object =
|
|
157
|
+
TResult extends object = RootQueryResult<TContext>,
|
|
139
158
|
TUtils extends UtilsRecord = {},
|
|
140
159
|
>(
|
|
141
160
|
configOrQuery:
|
|
142
161
|
| (LiveQueryCollectionConfig<TContext, TResult> & { utils?: TUtils })
|
|
143
|
-
| ((
|
|
162
|
+
| ((
|
|
163
|
+
q: InitialQueryBuilder,
|
|
164
|
+
) => QueryBuilder<TContext> & RootObjectResultConstraint<TContext>),
|
|
144
165
|
): CollectionForContext<TContext, TResult> & {
|
|
145
166
|
utils: LiveQueryCollectionUtils & TUtils
|
|
146
167
|
} {
|
|
@@ -150,9 +171,11 @@ export function createLiveQueryCollection<
|
|
|
150
171
|
const config: LiveQueryCollectionConfig<TContext, TResult> = {
|
|
151
172
|
query: configOrQuery as (
|
|
152
173
|
q: InitialQueryBuilder,
|
|
153
|
-
) => QueryBuilder<TContext>,
|
|
174
|
+
) => QueryBuilder<TContext> & RootObjectResultConstraint<TContext>,
|
|
154
175
|
}
|
|
155
|
-
|
|
176
|
+
// The implementation accepts both overload shapes, but TypeScript cannot
|
|
177
|
+
// preserve the overload-specific query-builder inference through this branch.
|
|
178
|
+
const options = liveQueryCollectionOptions(config as any)
|
|
156
179
|
return bridgeToCreateCollection(options) as CollectionForContext<
|
|
157
180
|
TContext,
|
|
158
181
|
TResult
|
|
@@ -163,7 +186,9 @@ export function createLiveQueryCollection<
|
|
|
163
186
|
TContext,
|
|
164
187
|
TResult
|
|
165
188
|
> & { utils?: TUtils }
|
|
166
|
-
|
|
189
|
+
// Same overload implementation limitation as above: the config has already
|
|
190
|
+
// been validated by the public signatures, but the branch loses that precision.
|
|
191
|
+
const options = liveQueryCollectionOptions(config as any)
|
|
167
192
|
|
|
168
193
|
// Merge custom utils if provided, preserving the getBuilder() method for dependency tracking
|
|
169
194
|
if (config.utils) {
|
package/src/query/query-once.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { createLiveQueryCollection } from './live-query-collection.js'
|
|
2
|
-
import type {
|
|
3
|
-
|
|
2
|
+
import type {
|
|
3
|
+
ExtractContext,
|
|
4
|
+
InitialQueryBuilder,
|
|
5
|
+
QueryBuilder,
|
|
6
|
+
} from './builder/index.js'
|
|
7
|
+
import type {
|
|
8
|
+
Context,
|
|
9
|
+
InferResultType,
|
|
10
|
+
RootObjectResultConstraint,
|
|
11
|
+
RootQueryBuilder,
|
|
12
|
+
RootQueryFn,
|
|
13
|
+
} from './builder/types.js'
|
|
4
14
|
|
|
5
15
|
/**
|
|
6
16
|
* Configuration options for queryOnce
|
|
@@ -10,8 +20,10 @@ export interface QueryOnceConfig<TContext extends Context> {
|
|
|
10
20
|
* Query builder function that defines the query
|
|
11
21
|
*/
|
|
12
22
|
query:
|
|
13
|
-
| ((
|
|
14
|
-
|
|
23
|
+
| ((
|
|
24
|
+
q: InitialQueryBuilder,
|
|
25
|
+
) => QueryBuilder<TContext> & RootObjectResultConstraint<TContext>)
|
|
26
|
+
| (QueryBuilder<TContext> & RootObjectResultConstraint<TContext>)
|
|
15
27
|
// Future: timeout, signal, etc.
|
|
16
28
|
}
|
|
17
29
|
|
|
@@ -44,9 +56,12 @@ export interface QueryOnceConfig<TContext extends Context> {
|
|
|
44
56
|
* )
|
|
45
57
|
* ```
|
|
46
58
|
*/
|
|
47
|
-
export function queryOnce<
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
export function queryOnce<
|
|
60
|
+
TQueryFn extends (q: InitialQueryBuilder) => QueryBuilder<any>,
|
|
61
|
+
TQuery extends QueryBuilder<any> = ReturnType<TQueryFn>,
|
|
62
|
+
>(
|
|
63
|
+
queryFn: TQueryFn & RootQueryFn<TQuery>,
|
|
64
|
+
): Promise<InferResultType<ExtractContext<TQuery>>>
|
|
50
65
|
|
|
51
66
|
// Overload 2: Config object form returning array (non-single result)
|
|
52
67
|
/**
|
|
@@ -65,15 +80,19 @@ export function queryOnce<TContext extends Context>(
|
|
|
65
80
|
* })
|
|
66
81
|
* ```
|
|
67
82
|
*/
|
|
68
|
-
export function queryOnce<
|
|
69
|
-
config: QueryOnceConfig<
|
|
70
|
-
|
|
83
|
+
export function queryOnce<TQuery extends QueryBuilder<any>>(
|
|
84
|
+
config: QueryOnceConfig<ExtractContext<TQuery>> & {
|
|
85
|
+
query: RootQueryFn<TQuery> | RootQueryBuilder<TQuery>
|
|
86
|
+
},
|
|
87
|
+
): Promise<InferResultType<ExtractContext<TQuery>>>
|
|
71
88
|
|
|
72
89
|
// Implementation
|
|
73
90
|
export async function queryOnce<TContext extends Context>(
|
|
74
91
|
configOrQuery:
|
|
75
92
|
| QueryOnceConfig<TContext>
|
|
76
|
-
| ((
|
|
93
|
+
| ((
|
|
94
|
+
q: InitialQueryBuilder,
|
|
95
|
+
) => QueryBuilder<TContext> & RootObjectResultConstraint<TContext>),
|
|
77
96
|
): Promise<InferResultType<TContext>> {
|
|
78
97
|
// Normalize input
|
|
79
98
|
const config: QueryOnceConfig<TContext> =
|
|
@@ -107,7 +126,7 @@ export async function queryOnce<TContext extends Context>(
|
|
|
107
126
|
| undefined
|
|
108
127
|
return first as InferResultType<TContext>
|
|
109
128
|
}
|
|
110
|
-
return collection.toArray as InferResultType<TContext>
|
|
129
|
+
return collection.toArray as unknown as InferResultType<TContext>
|
|
111
130
|
} finally {
|
|
112
131
|
// Always cleanup, even on error
|
|
113
132
|
await collection.cleanup()
|
|
@@ -126,10 +126,8 @@ export class DeduplicatedLoadSubset {
|
|
|
126
126
|
return prom
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
// accurate tracking (e.g., where=undefined means "all data"), while loadOptions
|
|
132
|
-
// may be narrowed with a difference expression for the actual backend request.
|
|
129
|
+
// Preserve the original request for tracking and in-flight dedupe, but allow
|
|
130
|
+
// the backend request to be narrowed to only the missing subset.
|
|
133
131
|
const trackingOptions = cloneOptions(options)
|
|
134
132
|
const loadOptions = cloneOptions(options)
|
|
135
133
|
if (this.unlimitedWhere !== undefined && options.limit === undefined) {
|
|
@@ -147,7 +145,6 @@ export class DeduplicatedLoadSubset {
|
|
|
147
145
|
|
|
148
146
|
// Handle both sync (true) and async (Promise<void>) return values
|
|
149
147
|
if (resultPromise === true) {
|
|
150
|
-
// Sync return - update tracking with the original predicate
|
|
151
148
|
this.updateTracking(trackingOptions)
|
|
152
149
|
return true
|
|
153
150
|
} else {
|
|
@@ -159,7 +156,7 @@ export class DeduplicatedLoadSubset {
|
|
|
159
156
|
|
|
160
157
|
// We need to create a reference to the in-flight entry so we can remove it later
|
|
161
158
|
const inflightEntry = {
|
|
162
|
-
options:
|
|
159
|
+
options: trackingOptions,
|
|
163
160
|
promise: resultPromise
|
|
164
161
|
.then((result) => {
|
|
165
162
|
// Only update tracking if this request is still from the current generation
|
|
@@ -238,5 +235,12 @@ export class DeduplicatedLoadSubset {
|
|
|
238
235
|
* would reflect the mutated values rather than what was actually loaded.
|
|
239
236
|
*/
|
|
240
237
|
export function cloneOptions(options: LoadSubsetOptions): LoadSubsetOptions {
|
|
241
|
-
return {
|
|
238
|
+
return {
|
|
239
|
+
...options,
|
|
240
|
+
orderBy: options.orderBy?.map((clause) => ({
|
|
241
|
+
...clause,
|
|
242
|
+
compareOptions: { ...clause.compareOptions },
|
|
243
|
+
})),
|
|
244
|
+
cursor: options.cursor ? { ...options.cursor } : undefined,
|
|
245
|
+
}
|
|
242
246
|
}
|
package/src/types.ts
CHANGED
|
@@ -4,7 +4,9 @@ import type { StandardSchemaV1 } from '@standard-schema/spec'
|
|
|
4
4
|
import type { Transaction } from './transactions'
|
|
5
5
|
import type { BasicExpression, OrderBy } from './query/ir.js'
|
|
6
6
|
import type { EventEmitter } from './event-emitter.js'
|
|
7
|
+
import type { IndexConstructor } from './indexes/base-index.js'
|
|
7
8
|
import type { SingleRowRefProxy } from './query/builder/ref-proxy.js'
|
|
9
|
+
import type { WithVirtualProps } from './virtual-props.js'
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Interface for a collection-like object that provides the necessary methods
|
|
@@ -338,6 +340,7 @@ export interface SyncConfig<
|
|
|
338
340
|
commit: () => void
|
|
339
341
|
markReady: () => void
|
|
340
342
|
truncate: () => void
|
|
343
|
+
metadata?: SyncMetadataApi<TKey>
|
|
341
344
|
}) => void | CleanupFn | SyncConfigRes
|
|
342
345
|
|
|
343
346
|
/**
|
|
@@ -356,6 +359,25 @@ export interface SyncConfig<
|
|
|
356
359
|
rowUpdateMode?: `partial` | `full`
|
|
357
360
|
}
|
|
358
361
|
|
|
362
|
+
export interface SyncMetadataApi<
|
|
363
|
+
TKey extends string | number = string | number,
|
|
364
|
+
> {
|
|
365
|
+
row: {
|
|
366
|
+
get: (key: TKey) => unknown | undefined
|
|
367
|
+
set: (key: TKey, metadata: unknown) => void
|
|
368
|
+
delete: (key: TKey) => void
|
|
369
|
+
}
|
|
370
|
+
collection: {
|
|
371
|
+
get: (key: string) => unknown | undefined
|
|
372
|
+
set: (key: string, value: unknown) => void
|
|
373
|
+
delete: (key: string) => void
|
|
374
|
+
list: (prefix?: string) => ReadonlyArray<{
|
|
375
|
+
key: string
|
|
376
|
+
value: unknown
|
|
377
|
+
}>
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
359
381
|
export interface ChangeMessage<
|
|
360
382
|
T extends object = Record<string, unknown>,
|
|
361
383
|
TKey extends string | number = string | number,
|
|
@@ -540,12 +562,27 @@ export interface BaseCollectionConfig<
|
|
|
540
562
|
/**
|
|
541
563
|
* Auto-indexing mode for the collection.
|
|
542
564
|
* When enabled, indexes will be automatically created for simple where expressions.
|
|
543
|
-
* @default "
|
|
565
|
+
* @default "off"
|
|
544
566
|
* @description
|
|
545
|
-
* - "off": No automatic indexing
|
|
546
|
-
* - "eager": Automatically create indexes for simple where expressions in subscribeChanges
|
|
567
|
+
* - "off": No automatic indexing (default). Use explicit indexes for better bundle size.
|
|
568
|
+
* - "eager": Automatically create indexes for simple where expressions in subscribeChanges.
|
|
569
|
+
* Requires setting defaultIndexType.
|
|
547
570
|
*/
|
|
548
571
|
autoIndex?: `off` | `eager`
|
|
572
|
+
/**
|
|
573
|
+
* Default index type to use when creating indexes without an explicit type.
|
|
574
|
+
* Required for auto-indexing. Import from '@tanstack/db'.
|
|
575
|
+
* @example
|
|
576
|
+
* ```ts
|
|
577
|
+
* import { BasicIndex } from '@tanstack/db'
|
|
578
|
+
* const collection = createCollection({
|
|
579
|
+
* defaultIndexType: BasicIndex,
|
|
580
|
+
* autoIndex: 'eager',
|
|
581
|
+
* // ...
|
|
582
|
+
* })
|
|
583
|
+
* ```
|
|
584
|
+
*/
|
|
585
|
+
defaultIndexType?: IndexConstructor<TKey>
|
|
549
586
|
/**
|
|
550
587
|
* Optional function to compare two items.
|
|
551
588
|
* This is used to order the items in the collection.
|
|
@@ -739,9 +776,10 @@ export type CollectionConfigSingleRowOption<
|
|
|
739
776
|
TUtils extends UtilsRecord = {},
|
|
740
777
|
> = CollectionConfig<T, TKey, TSchema, TUtils> & MaybeSingleResult
|
|
741
778
|
|
|
742
|
-
export type ChangesPayload<
|
|
743
|
-
|
|
744
|
-
|
|
779
|
+
export type ChangesPayload<
|
|
780
|
+
T extends object = Record<string, unknown>,
|
|
781
|
+
TKey extends string | number = string | number,
|
|
782
|
+
> = Array<ChangeMessage<WithVirtualProps<T, TKey>, TKey>>
|
|
745
783
|
|
|
746
784
|
/**
|
|
747
785
|
* An input row from a collection
|
|
@@ -783,6 +821,7 @@ export type NamespacedAndKeyedStream = IStreamBuilder<KeyedNamespacedRow>
|
|
|
783
821
|
*/
|
|
784
822
|
export interface SubscribeChangesOptions<
|
|
785
823
|
T extends object = Record<string, unknown>,
|
|
824
|
+
TKey extends string | number = string | number,
|
|
786
825
|
> {
|
|
787
826
|
/** Whether to include the current state as initial changes */
|
|
788
827
|
includeInitialState?: boolean
|
|
@@ -800,7 +839,7 @@ export interface SubscribeChangesOptions<
|
|
|
800
839
|
* })
|
|
801
840
|
* ```
|
|
802
841
|
*/
|
|
803
|
-
where?: (row: SingleRowRefProxy<T
|
|
842
|
+
where?: (row: SingleRowRefProxy<WithVirtualProps<T, TKey>>) => any
|
|
804
843
|
/** Pre-compiled expression for filtering changes */
|
|
805
844
|
whereExpression?: BasicExpression<boolean>
|
|
806
845
|
/**
|
|
@@ -829,7 +868,8 @@ export interface SubscribeChangesOptions<
|
|
|
829
868
|
|
|
830
869
|
export interface SubscribeChangesSnapshotOptions<
|
|
831
870
|
T extends object = Record<string, unknown>,
|
|
832
|
-
|
|
871
|
+
TKey extends string | number = string | number,
|
|
872
|
+
> extends Omit<SubscribeChangesOptions<T, TKey>, `includeInitialState`> {
|
|
833
873
|
orderBy?: OrderBy
|
|
834
874
|
limit?: number
|
|
835
875
|
}
|
|
@@ -879,7 +919,7 @@ export interface CurrentStateAsChangesOptions {
|
|
|
879
919
|
export type ChangeListener<
|
|
880
920
|
T extends object = Record<string, unknown>,
|
|
881
921
|
TKey extends string | number = string | number,
|
|
882
|
-
> = (changes: Array<ChangeMessage<T, TKey>>) => void
|
|
922
|
+
> = (changes: Array<ChangeMessage<WithVirtualProps<T, TKey>, TKey>>) => void
|
|
883
923
|
|
|
884
924
|
// Adapted from https://github.com/sindresorhus/type-fest
|
|
885
925
|
// MIT License Copyright (c) Sindre Sorhus
|
package/src/utils/array-utils.ts
CHANGED
|
@@ -5,6 +5,35 @@
|
|
|
5
5
|
* @param compareFn Comparison function to use for ordering
|
|
6
6
|
* @returns The index where the value should be inserted to maintain order
|
|
7
7
|
*/
|
|
8
|
+
export function findInsertPositionInArray<T>(
|
|
9
|
+
sortedArray: Array<T>,
|
|
10
|
+
value: T,
|
|
11
|
+
compareFn: (a: T, b: T) => number,
|
|
12
|
+
): number {
|
|
13
|
+
let left = 0
|
|
14
|
+
let right = sortedArray.length
|
|
15
|
+
|
|
16
|
+
while (left < right) {
|
|
17
|
+
const mid = Math.floor((left + right) / 2)
|
|
18
|
+
const comparison = compareFn(sortedArray[mid]!, value)
|
|
19
|
+
|
|
20
|
+
if (comparison < 0) {
|
|
21
|
+
left = mid + 1
|
|
22
|
+
} else {
|
|
23
|
+
right = mid
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return left
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Finds the correct insert position for a value in a sorted tuple array using binary search
|
|
32
|
+
* @param sortedArray The sorted tuple array to search in
|
|
33
|
+
* @param value The value to find the position for
|
|
34
|
+
* @param compareFn Comparison function to use for ordering
|
|
35
|
+
* @returns The index where the value should be inserted to maintain order
|
|
36
|
+
*/
|
|
8
37
|
export function findInsertPosition<T>(
|
|
9
38
|
sortedArray: Array<[T, any]>,
|
|
10
39
|
value: T,
|
|
@@ -26,3 +55,23 @@ export function findInsertPosition<T>(
|
|
|
26
55
|
|
|
27
56
|
return left
|
|
28
57
|
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Deletes a value from a sorted array while maintaining sort order
|
|
61
|
+
* @param sortedArray The sorted array to delete from
|
|
62
|
+
* @param value The value to delete
|
|
63
|
+
* @param compareFn Comparison function to use for ordering
|
|
64
|
+
* @returns True if the value was found and deleted, false otherwise
|
|
65
|
+
*/
|
|
66
|
+
export function deleteInSortedArray<T>(
|
|
67
|
+
sortedArray: Array<T>,
|
|
68
|
+
value: T,
|
|
69
|
+
compareFn: (a: T, b: T) => number,
|
|
70
|
+
): boolean {
|
|
71
|
+
const idx = findInsertPositionInArray(sortedArray, value, compareFn)
|
|
72
|
+
if (idx < sortedArray.length && compareFn(sortedArray[idx]!, value) === 0) {
|
|
73
|
+
sortedArray.splice(idx, 1)
|
|
74
|
+
return true
|
|
75
|
+
}
|
|
76
|
+
return false
|
|
77
|
+
}
|
package/src/utils/comparison.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isTemporal } from '../utils'
|
|
1
2
|
import type { CompareOptions } from '../query/builder/types'
|
|
2
3
|
|
|
3
4
|
// WeakMap to store stable IDs for objects
|
|
@@ -54,6 +55,15 @@ export const ascComparator = (a: any, b: any, opts: CompareOptions): number => {
|
|
|
54
55
|
return a.getTime() - b.getTime()
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
// If both are Temporal objects of the same type, compare by string representation
|
|
59
|
+
if (isTemporal(a) && isTemporal(b)) {
|
|
60
|
+
const aStr = a.toString()
|
|
61
|
+
const bStr = b.toString()
|
|
62
|
+
if (aStr < bStr) return -1
|
|
63
|
+
if (aStr > bStr) return 1
|
|
64
|
+
return 0
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
// If at least one of the values is an object, use stable IDs for comparison
|
|
58
68
|
const aIsObject = typeof a === `object`
|
|
59
69
|
const bIsObject = typeof b === `object`
|
|
@@ -154,6 +164,10 @@ export function normalizeValue(value: any): any {
|
|
|
154
164
|
return value.getTime()
|
|
155
165
|
}
|
|
156
166
|
|
|
167
|
+
if (isTemporal(value)) {
|
|
168
|
+
return `__temporal__${value[Symbol.toStringTag]}__${value.toString()}`
|
|
169
|
+
}
|
|
170
|
+
|
|
157
171
|
// Normalize Uint8Arrays/Buffers to a string representation for Map key usage
|
|
158
172
|
// This enables content-based equality for binary data like ULIDs
|
|
159
173
|
const isUint8Array =
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { DEFAULT_COMPARE_OPTIONS } from '../utils.js'
|
|
19
19
|
import { ReverseIndex } from '../indexes/reverse-index.js'
|
|
20
|
+
import { hasVirtualPropPath } from '../virtual-props.js'
|
|
20
21
|
import type { CompareOptions } from '../query/builder/types.js'
|
|
21
22
|
import type { IndexInterface, IndexOperation } from '../indexes/base-index.js'
|
|
22
23
|
import type { BasicExpression } from '../query/ir.js'
|
|
@@ -38,6 +39,9 @@ export function findIndexForField<TKey extends string | number>(
|
|
|
38
39
|
fieldPath: Array<string>,
|
|
39
40
|
compareOptions?: CompareOptions,
|
|
40
41
|
): IndexInterface<TKey> | undefined {
|
|
42
|
+
if (hasVirtualPropPath(fieldPath)) {
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
41
45
|
const compareOpts = compareOptions ?? {
|
|
42
46
|
...DEFAULT_COMPARE_OPTIONS,
|
|
43
47
|
...collection.compareOptions,
|
package/src/utils.ts
CHANGED
|
@@ -144,8 +144,8 @@ function deepEqualsInternal(
|
|
|
144
144
|
// Handle Temporal objects
|
|
145
145
|
// Check if both are Temporal objects of the same type
|
|
146
146
|
if (isTemporal(a) && isTemporal(b)) {
|
|
147
|
-
const aTag =
|
|
148
|
-
const bTag =
|
|
147
|
+
const aTag = a[Symbol.toStringTag]
|
|
148
|
+
const bTag = b[Symbol.toStringTag]
|
|
149
149
|
|
|
150
150
|
// If they're different Temporal types, they're not equal
|
|
151
151
|
if (aTag !== bTag) return false
|
|
@@ -211,7 +211,7 @@ function deepEqualsInternal(
|
|
|
211
211
|
return false
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
const temporalTypes = [
|
|
214
|
+
const temporalTypes = new Set([
|
|
215
215
|
`Temporal.Duration`,
|
|
216
216
|
`Temporal.Instant`,
|
|
217
217
|
`Temporal.PlainDate`,
|
|
@@ -220,16 +220,19 @@ const temporalTypes = [
|
|
|
220
220
|
`Temporal.PlainTime`,
|
|
221
221
|
`Temporal.PlainYearMonth`,
|
|
222
222
|
`Temporal.ZonedDateTime`,
|
|
223
|
-
]
|
|
223
|
+
])
|
|
224
224
|
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
export interface TemporalLike {
|
|
226
|
+
[Symbol.toStringTag]: string
|
|
227
|
+
toString: () => string
|
|
228
|
+
equals?: (other: unknown) => boolean
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
/** Checks if the value is a Temporal object by checking for the Temporal brand */
|
|
230
|
-
export function isTemporal(a:
|
|
231
|
-
|
|
232
|
-
|
|
232
|
+
export function isTemporal(a: unknown): a is TemporalLike {
|
|
233
|
+
if (a == null || typeof a !== `object`) return false
|
|
234
|
+
const tag = (a as Record<symbol, unknown>)[Symbol.toStringTag]
|
|
235
|
+
return typeof tag === `string` && temporalTypes.has(tag)
|
|
233
236
|
}
|
|
234
237
|
|
|
235
238
|
export const DEFAULT_COMPARE_OPTIONS: CompareOptions = {
|