@tanstack/db 0.4.7 → 0.4.9
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/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +2 -1
- package/dist/cjs/collection/lifecycle.cjs +2 -3
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/state.cjs +22 -33
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +6 -2
- package/dist/cjs/collection/sync.cjs +4 -3
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/errors.cjs +51 -17
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +38 -8
- package/dist/cjs/index.cjs +8 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/indexes/auto-index.cjs +0 -3
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/query/builder/types.d.cts +1 -1
- package/dist/cjs/query/compiler/index.cjs +42 -19
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +33 -8
- package/dist/cjs/query/compiler/joins.cjs +88 -66
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.d.cts +5 -2
- package/dist/cjs/query/compiler/order-by.cjs +2 -0
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +322 -46
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +98 -7
- package/dist/cjs/query/live/collection-registry.cjs +16 -0
- package/dist/cjs/query/live/collection-registry.cjs.map +1 -0
- package/dist/cjs/query/live/collection-registry.d.cts +26 -0
- package/dist/cjs/query/live/collection-subscriber.cjs +57 -58
- package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
- package/dist/cjs/query/live/collection-subscriber.d.cts +4 -7
- package/dist/cjs/query/live-query-collection.cjs +11 -5
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +10 -3
- package/dist/cjs/query/optimizer.cjs +44 -7
- package/dist/cjs/query/optimizer.cjs.map +1 -1
- package/dist/cjs/query/optimizer.d.cts +4 -4
- package/dist/cjs/scheduler.cjs +137 -0
- package/dist/cjs/scheduler.cjs.map +1 -0
- package/dist/cjs/scheduler.d.cts +56 -0
- package/dist/cjs/transactions.cjs +7 -1
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +3 -5
- package/dist/esm/collection/index.d.ts +2 -1
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/lifecycle.js +2 -3
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/state.d.ts +6 -2
- package/dist/esm/collection/state.js +22 -33
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/sync.js +4 -3
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/errors.d.ts +38 -8
- package/dist/esm/errors.js +52 -18
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.js +9 -5
- package/dist/esm/indexes/auto-index.js +0 -3
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +1 -1
- package/dist/esm/query/compiler/index.d.ts +33 -8
- package/dist/esm/query/compiler/index.js +42 -19
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.d.ts +5 -2
- package/dist/esm/query/compiler/joins.js +90 -68
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -0
- package/dist/esm/query/compiler/order-by.js +2 -0
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +98 -7
- package/dist/esm/query/live/collection-config-builder.js +322 -46
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/collection-registry.d.ts +26 -0
- package/dist/esm/query/live/collection-registry.js +16 -0
- package/dist/esm/query/live/collection-registry.js.map +1 -0
- package/dist/esm/query/live/collection-subscriber.d.ts +4 -7
- package/dist/esm/query/live/collection-subscriber.js +57 -58
- package/dist/esm/query/live/collection-subscriber.js.map +1 -1
- package/dist/esm/query/live-query-collection.d.ts +10 -3
- package/dist/esm/query/live-query-collection.js +11 -5
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/optimizer.d.ts +4 -4
- package/dist/esm/query/optimizer.js +44 -7
- package/dist/esm/query/optimizer.js.map +1 -1
- package/dist/esm/scheduler.d.ts +56 -0
- package/dist/esm/scheduler.js +137 -0
- package/dist/esm/scheduler.js.map +1 -0
- package/dist/esm/transactions.js +7 -1
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +3 -5
- package/package.json +2 -2
- package/src/collection/index.ts +1 -1
- package/src/collection/lifecycle.ts +3 -4
- package/src/collection/state.ts +52 -48
- package/src/collection/sync.ts +7 -6
- package/src/errors.ts +79 -13
- package/src/indexes/auto-index.ts +0 -8
- package/src/query/builder/types.ts +1 -1
- package/src/query/compiler/index.ts +115 -32
- package/src/query/compiler/joins.ts +180 -127
- package/src/query/compiler/order-by.ts +7 -0
- package/src/query/compiler/select.ts +2 -3
- package/src/query/live/collection-config-builder.ts +542 -71
- package/src/query/live/collection-registry.ts +47 -0
- package/src/query/live/collection-subscriber.ts +87 -105
- package/src/query/live-query-collection.ts +39 -14
- package/src/query/optimizer.ts +85 -15
- package/src/scheduler.ts +198 -0
- package/src/transactions.ts +12 -1
- package/src/types.ts +3 -5
|
@@ -31,24 +31,53 @@ import type {
|
|
|
31
31
|
import type { QueryCache, QueryMapping } from "./types.js"
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Result of query compilation including both the pipeline and
|
|
34
|
+
* Result of query compilation including both the pipeline and source-specific WHERE clauses
|
|
35
35
|
*/
|
|
36
36
|
export interface CompilationResult {
|
|
37
37
|
/** The ID of the main collection */
|
|
38
38
|
collectionId: string
|
|
39
|
-
|
|
39
|
+
|
|
40
|
+
/** The compiled query pipeline (D2 stream) */
|
|
40
41
|
pipeline: ResultStream
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
/** Map of source aliases to their WHERE clauses for index optimization */
|
|
44
|
+
sourceWhereClauses: Map<string, BasicExpression<boolean>>
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Maps each source alias to its collection ID. Enables per-alias subscriptions for self-joins.
|
|
48
|
+
* Example: `{ employee: 'employees-col-id', manager: 'employees-col-id' }`
|
|
49
|
+
*/
|
|
50
|
+
aliasToCollectionId: Record<string, string>
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Flattened mapping from outer alias to innermost alias for subqueries.
|
|
54
|
+
* Always provides one-hop lookups, never recursive chains.
|
|
55
|
+
*
|
|
56
|
+
* Example: `{ activeUser: 'user' }` when `.from({ activeUser: subquery })`
|
|
57
|
+
* where the subquery uses `.from({ user: collection })`.
|
|
58
|
+
*
|
|
59
|
+
* For deeply nested subqueries, the mapping goes directly to the innermost alias:
|
|
60
|
+
* `{ author: 'user' }` (not `{ author: 'activeUser' }`), so `aliasRemapping[alias]`
|
|
61
|
+
* always resolves in a single lookup.
|
|
62
|
+
*
|
|
63
|
+
* Used to resolve subscriptions during lazy loading when join aliases differ from
|
|
64
|
+
* the inner aliases where collection subscriptions were created.
|
|
65
|
+
*/
|
|
66
|
+
aliasRemapping: Record<string, string>
|
|
43
67
|
}
|
|
44
68
|
|
|
45
69
|
/**
|
|
46
|
-
* Compiles a
|
|
70
|
+
* Compiles a query IR into a D2 pipeline
|
|
47
71
|
* @param rawQuery The query IR to compile
|
|
48
|
-
* @param inputs Mapping of
|
|
72
|
+
* @param inputs Mapping of source aliases to input streams (e.g., `{ employee: input1, manager: input2 }`)
|
|
73
|
+
* @param collections Mapping of collection IDs to Collection instances
|
|
74
|
+
* @param subscriptions Mapping of source aliases to CollectionSubscription instances
|
|
75
|
+
* @param callbacks Mapping of source aliases to lazy loading callbacks
|
|
76
|
+
* @param lazySources Set of source aliases that should load data lazily
|
|
77
|
+
* @param optimizableOrderByCollections Map of collection IDs to order-by optimization info
|
|
49
78
|
* @param cache Optional cache for compiled subqueries (used internally for recursion)
|
|
50
79
|
* @param queryMapping Optional mapping from optimized queries to original queries
|
|
51
|
-
* @returns A CompilationResult with the pipeline
|
|
80
|
+
* @returns A CompilationResult with the pipeline, source WHERE clauses, and alias metadata
|
|
52
81
|
*/
|
|
53
82
|
export function compileQuery(
|
|
54
83
|
rawQuery: QueryIR,
|
|
@@ -56,7 +85,7 @@ export function compileQuery(
|
|
|
56
85
|
collections: Record<string, Collection<any, any, any, any, any>>,
|
|
57
86
|
subscriptions: Record<string, CollectionSubscription>,
|
|
58
87
|
callbacks: Record<string, LazyCollectionCallbacks>,
|
|
59
|
-
|
|
88
|
+
lazySources: Set<string>,
|
|
60
89
|
optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,
|
|
61
90
|
cache: QueryCache = new WeakMap(),
|
|
62
91
|
queryMapping: QueryMapping = new WeakMap()
|
|
@@ -68,8 +97,7 @@ export function compileQuery(
|
|
|
68
97
|
}
|
|
69
98
|
|
|
70
99
|
// Optimize the query before compilation
|
|
71
|
-
const { optimizedQuery: query,
|
|
72
|
-
optimizeQuery(rawQuery)
|
|
100
|
+
const { optimizedQuery: query, sourceWhereClauses } = optimizeQuery(rawQuery)
|
|
73
101
|
|
|
74
102
|
// Create mapping from optimized query to original for caching
|
|
75
103
|
queryMapping.set(query, rawQuery)
|
|
@@ -78,12 +106,24 @@ export function compileQuery(
|
|
|
78
106
|
// Create a copy of the inputs map to avoid modifying the original
|
|
79
107
|
const allInputs = { ...inputs }
|
|
80
108
|
|
|
81
|
-
//
|
|
82
|
-
|
|
109
|
+
// Track alias to collection id relationships discovered during compilation.
|
|
110
|
+
// This includes all user-declared aliases plus inner aliases from subqueries.
|
|
111
|
+
const aliasToCollectionId: Record<string, string> = {}
|
|
112
|
+
|
|
113
|
+
// Track alias remapping for subqueries (outer alias → inner alias)
|
|
114
|
+
// e.g., when .join({ activeUser: subquery }) where subquery uses .from({ user: collection })
|
|
115
|
+
// we store: aliasRemapping['activeUser'] = 'user'
|
|
116
|
+
const aliasRemapping: Record<string, string> = {}
|
|
83
117
|
|
|
84
|
-
//
|
|
118
|
+
// Create a map of source aliases to input streams.
|
|
119
|
+
// Inputs MUST be keyed by alias (e.g., `{ employee: input1, manager: input2 }`),
|
|
120
|
+
// not by collection ID. This enables per-alias subscriptions where different aliases
|
|
121
|
+
// of the same collection (e.g., self-joins) maintain independent filtered streams.
|
|
122
|
+
const sources: Record<string, KeyedStream> = {}
|
|
123
|
+
|
|
124
|
+
// Process the FROM clause to get the main source
|
|
85
125
|
const {
|
|
86
|
-
alias:
|
|
126
|
+
alias: mainSource,
|
|
87
127
|
input: mainInput,
|
|
88
128
|
collectionId: mainCollectionId,
|
|
89
129
|
} = processFrom(
|
|
@@ -92,18 +132,20 @@ export function compileQuery(
|
|
|
92
132
|
collections,
|
|
93
133
|
subscriptions,
|
|
94
134
|
callbacks,
|
|
95
|
-
|
|
135
|
+
lazySources,
|
|
96
136
|
optimizableOrderByCollections,
|
|
97
137
|
cache,
|
|
98
|
-
queryMapping
|
|
138
|
+
queryMapping,
|
|
139
|
+
aliasToCollectionId,
|
|
140
|
+
aliasRemapping
|
|
99
141
|
)
|
|
100
|
-
|
|
142
|
+
sources[mainSource] = mainInput
|
|
101
143
|
|
|
102
|
-
// Prepare the initial pipeline with the main
|
|
144
|
+
// Prepare the initial pipeline with the main source wrapped in its alias
|
|
103
145
|
let pipeline: NamespacedAndKeyedStream = mainInput.pipe(
|
|
104
146
|
map(([key, row]) => {
|
|
105
147
|
// Initialize the record with a nested structure
|
|
106
|
-
const ret = [key, { [
|
|
148
|
+
const ret = [key, { [mainSource]: row }] as [
|
|
107
149
|
string,
|
|
108
150
|
Record<string, typeof row>,
|
|
109
151
|
]
|
|
@@ -116,19 +158,21 @@ export function compileQuery(
|
|
|
116
158
|
pipeline = processJoins(
|
|
117
159
|
pipeline,
|
|
118
160
|
query.join,
|
|
119
|
-
|
|
161
|
+
sources,
|
|
120
162
|
mainCollectionId,
|
|
121
|
-
|
|
163
|
+
mainSource,
|
|
122
164
|
allInputs,
|
|
123
165
|
cache,
|
|
124
166
|
queryMapping,
|
|
125
167
|
collections,
|
|
126
168
|
subscriptions,
|
|
127
169
|
callbacks,
|
|
128
|
-
|
|
170
|
+
lazySources,
|
|
129
171
|
optimizableOrderByCollections,
|
|
130
172
|
rawQuery,
|
|
131
|
-
compileQuery
|
|
173
|
+
compileQuery,
|
|
174
|
+
aliasToCollectionId,
|
|
175
|
+
aliasRemapping
|
|
132
176
|
)
|
|
133
177
|
}
|
|
134
178
|
|
|
@@ -185,7 +229,7 @@ export function compileQuery(
|
|
|
185
229
|
map(([key, namespacedRow]) => {
|
|
186
230
|
const selectResults =
|
|
187
231
|
!query.join && !query.groupBy
|
|
188
|
-
? namespacedRow[
|
|
232
|
+
? namespacedRow[mainSource]
|
|
189
233
|
: namespacedRow
|
|
190
234
|
|
|
191
235
|
return [
|
|
@@ -286,7 +330,9 @@ export function compileQuery(
|
|
|
286
330
|
const compilationResult = {
|
|
287
331
|
collectionId: mainCollectionId,
|
|
288
332
|
pipeline: result,
|
|
289
|
-
|
|
333
|
+
sourceWhereClauses,
|
|
334
|
+
aliasToCollectionId,
|
|
335
|
+
aliasRemapping,
|
|
290
336
|
}
|
|
291
337
|
cache.set(rawQuery, compilationResult)
|
|
292
338
|
|
|
@@ -314,7 +360,9 @@ export function compileQuery(
|
|
|
314
360
|
const compilationResult = {
|
|
315
361
|
collectionId: mainCollectionId,
|
|
316
362
|
pipeline: result,
|
|
317
|
-
|
|
363
|
+
sourceWhereClauses,
|
|
364
|
+
aliasToCollectionId,
|
|
365
|
+
aliasRemapping,
|
|
318
366
|
}
|
|
319
367
|
cache.set(rawQuery, compilationResult)
|
|
320
368
|
|
|
@@ -322,7 +370,8 @@ export function compileQuery(
|
|
|
322
370
|
}
|
|
323
371
|
|
|
324
372
|
/**
|
|
325
|
-
* Processes the FROM clause
|
|
373
|
+
* Processes the FROM clause, handling direct collection references and subqueries.
|
|
374
|
+
* Populates `aliasToCollectionId` and `aliasRemapping` for per-alias subscription tracking.
|
|
326
375
|
*/
|
|
327
376
|
function processFrom(
|
|
328
377
|
from: CollectionRef | QueryRef,
|
|
@@ -330,17 +379,24 @@ function processFrom(
|
|
|
330
379
|
collections: Record<string, Collection>,
|
|
331
380
|
subscriptions: Record<string, CollectionSubscription>,
|
|
332
381
|
callbacks: Record<string, LazyCollectionCallbacks>,
|
|
333
|
-
|
|
382
|
+
lazySources: Set<string>,
|
|
334
383
|
optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,
|
|
335
384
|
cache: QueryCache,
|
|
336
|
-
queryMapping: QueryMapping
|
|
385
|
+
queryMapping: QueryMapping,
|
|
386
|
+
aliasToCollectionId: Record<string, string>,
|
|
387
|
+
aliasRemapping: Record<string, string>
|
|
337
388
|
): { alias: string; input: KeyedStream; collectionId: string } {
|
|
338
389
|
switch (from.type) {
|
|
339
390
|
case `collectionRef`: {
|
|
340
|
-
const input = allInputs[from.
|
|
391
|
+
const input = allInputs[from.alias]
|
|
341
392
|
if (!input) {
|
|
342
|
-
throw new CollectionInputNotFoundError(
|
|
393
|
+
throw new CollectionInputNotFoundError(
|
|
394
|
+
from.alias,
|
|
395
|
+
from.collection.id,
|
|
396
|
+
Object.keys(allInputs)
|
|
397
|
+
)
|
|
343
398
|
}
|
|
399
|
+
aliasToCollectionId[from.alias] = from.collection.id
|
|
344
400
|
return { alias: from.alias, input, collectionId: from.collection.id }
|
|
345
401
|
}
|
|
346
402
|
case `queryRef`: {
|
|
@@ -354,12 +410,39 @@ function processFrom(
|
|
|
354
410
|
collections,
|
|
355
411
|
subscriptions,
|
|
356
412
|
callbacks,
|
|
357
|
-
|
|
413
|
+
lazySources,
|
|
358
414
|
optimizableOrderByCollections,
|
|
359
415
|
cache,
|
|
360
416
|
queryMapping
|
|
361
417
|
)
|
|
362
418
|
|
|
419
|
+
// Pull up alias mappings from subquery to parent scope.
|
|
420
|
+
// This includes both the innermost alias-to-collection mappings AND
|
|
421
|
+
// any existing remappings from nested subquery levels.
|
|
422
|
+
Object.assign(aliasToCollectionId, subQueryResult.aliasToCollectionId)
|
|
423
|
+
Object.assign(aliasRemapping, subQueryResult.aliasRemapping)
|
|
424
|
+
|
|
425
|
+
// Create a FLATTENED remapping from outer alias to innermost alias.
|
|
426
|
+
// For nested subqueries, this ensures one-hop lookups (not recursive chains).
|
|
427
|
+
//
|
|
428
|
+
// Example with 3-level nesting:
|
|
429
|
+
// Inner: .from({ user: usersCollection })
|
|
430
|
+
// Middle: .from({ activeUser: innerSubquery }) → creates: activeUser → user
|
|
431
|
+
// Outer: .from({ author: middleSubquery }) → creates: author → user (not author → activeUser)
|
|
432
|
+
//
|
|
433
|
+
// The key insight: We search through the PULLED-UP aliasToCollectionId (which contains
|
|
434
|
+
// the innermost 'user' alias), so we always map directly to the deepest level.
|
|
435
|
+
// This means aliasRemapping[alias] is always a single lookup, never recursive.
|
|
436
|
+
// Needed for subscription resolution during lazy loading.
|
|
437
|
+
const innerAlias = Object.keys(subQueryResult.aliasToCollectionId).find(
|
|
438
|
+
(alias) =>
|
|
439
|
+
subQueryResult.aliasToCollectionId[alias] ===
|
|
440
|
+
subQueryResult.collectionId
|
|
441
|
+
)
|
|
442
|
+
if (innerAlias && innerAlias !== from.alias) {
|
|
443
|
+
aliasRemapping[from.alias] = innerAlias
|
|
444
|
+
}
|
|
445
|
+
|
|
363
446
|
// Extract the pipeline from the compilation result
|
|
364
447
|
const subQueryInput = subQueryResult.pipeline
|
|
365
448
|
|