@tanstack/db 0.5.30 → 0.5.32
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/subscription.cjs +6 -6
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- 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 +13 -10
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/query/builder/types.d.cts +28 -31
- package/dist/cjs/query/compiler/index.cjs +3 -0
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/index.d.cts +1 -0
- package/dist/cjs/query/query-once.cjs +28 -0
- package/dist/cjs/query/query-once.cjs.map +1 -0
- package/dist/cjs/query/query-once.d.cts +57 -0
- package/dist/cjs/query/subset-dedupe.cjs +8 -7
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/esm/collection/subscription.js +6 -6
- package/dist/esm/collection/subscription.js.map +1 -1
- 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.js +6 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +28 -31
- package/dist/esm/query/compiler/index.js +4 -1
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/index.d.ts +1 -0
- package/dist/esm/query/query-once.d.ts +57 -0
- package/dist/esm/query/query-once.js +28 -0
- package/dist/esm/query/query-once.js.map +1 -0
- package/dist/esm/query/subset-dedupe.js +8 -7
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/package.json +3 -2
- package/skills/db-core/SKILL.md +61 -0
- package/skills/db-core/collection-setup/SKILL.md +427 -0
- package/skills/db-core/collection-setup/references/electric-adapter.md +238 -0
- package/skills/db-core/collection-setup/references/local-adapters.md +220 -0
- package/skills/db-core/collection-setup/references/powersync-adapter.md +241 -0
- package/skills/db-core/collection-setup/references/query-adapter.md +183 -0
- package/skills/db-core/collection-setup/references/rxdb-adapter.md +152 -0
- package/skills/db-core/collection-setup/references/schema-patterns.md +215 -0
- package/skills/db-core/collection-setup/references/trailbase-adapter.md +147 -0
- package/skills/db-core/custom-adapter/SKILL.md +285 -0
- package/skills/db-core/live-queries/SKILL.md +332 -0
- package/skills/db-core/live-queries/references/operators.md +302 -0
- package/skills/db-core/mutations-optimistic/SKILL.md +375 -0
- package/skills/db-core/mutations-optimistic/references/transaction-api.md +207 -0
- package/skills/meta-framework/SKILL.md +361 -0
- package/src/collection/subscription.ts +6 -6
- package/src/errors.ts +11 -0
- package/src/query/builder/types.ts +64 -50
- package/src/query/compiler/index.ts +5 -0
- package/src/query/index.ts +3 -0
- package/src/query/query-once.ts +115 -0
- package/src/query/subset-dedupe.ts +14 -15
|
@@ -227,18 +227,28 @@ export type ResultTypeFromSelect<TSelectObject> = WithoutRefBrand<
|
|
|
227
227
|
Prettify<{
|
|
228
228
|
[K in keyof TSelectObject]: NeedsExtraction<TSelectObject[K]> extends true
|
|
229
229
|
? ExtractExpressionType<TSelectObject[K]>
|
|
230
|
-
:
|
|
230
|
+
: // Ref (full object ref or spread with RefBrand) - recursively process properties
|
|
231
|
+
TSelectObject[K] extends Ref<infer _T>
|
|
231
232
|
? ExtractRef<TSelectObject[K]>
|
|
232
|
-
:
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
: // RefLeaf (simple property ref like user.name)
|
|
234
|
+
TSelectObject[K] extends RefLeaf<infer T>
|
|
235
|
+
? IsNullableRef<TSelectObject[K]> extends true
|
|
235
236
|
? T | undefined
|
|
236
|
-
:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
237
|
+
: T
|
|
238
|
+
: // RefLeaf | undefined (schema-optional field)
|
|
239
|
+
TSelectObject[K] extends RefLeaf<infer T> | undefined
|
|
240
|
+
? T | undefined
|
|
241
|
+
: // RefLeaf | null (schema-nullable field)
|
|
242
|
+
TSelectObject[K] extends RefLeaf<infer T> | null
|
|
243
|
+
? IsNullableRef<Exclude<TSelectObject[K], null>> extends true
|
|
244
|
+
? T | null | undefined
|
|
245
|
+
: T | null
|
|
246
|
+
: // Ref | undefined (optional object-type schema field)
|
|
247
|
+
TSelectObject[K] extends Ref<infer _T> | undefined
|
|
248
|
+
? ExtractRef<Exclude<TSelectObject[K], undefined>> | undefined
|
|
249
|
+
: // Ref | null (nullable object-type schema field)
|
|
250
|
+
TSelectObject[K] extends Ref<infer _T> | null
|
|
251
|
+
? ExtractRef<Exclude<TSelectObject[K], null>> | null
|
|
242
252
|
: TSelectObject[K] extends Aggregate<infer T>
|
|
243
253
|
? T
|
|
244
254
|
: TSelectObject[K] extends
|
|
@@ -366,24 +376,17 @@ export type FunctionalHavingRow<TContext extends Context> = TContext[`schema`] &
|
|
|
366
376
|
(TContext[`result`] extends object ? { $selected: TContext[`result`] } : {})
|
|
367
377
|
|
|
368
378
|
/**
|
|
369
|
-
*
|
|
379
|
+
* RefsForContext - Creates ref proxies for all tables/collections in a query context
|
|
370
380
|
*
|
|
371
381
|
* This is the main entry point for creating ref objects in query builder callbacks.
|
|
372
|
-
*
|
|
373
|
-
*
|
|
382
|
+
* For nullable join sides (left/right/full joins), it produces `Ref<T, true>` instead
|
|
383
|
+
* of `Ref<T> | undefined`. This accurately reflects that the proxy object is always
|
|
384
|
+
* present at build time (it's a truthy proxy that records property access paths),
|
|
385
|
+
* while the `Nullable` flag ensures the result type correctly includes `| undefined`.
|
|
374
386
|
*
|
|
375
387
|
* Examples:
|
|
376
|
-
* - Required field: `
|
|
377
|
-
* -
|
|
378
|
-
* - Nullable field: `RefProxy<User> | null` → user?.name works
|
|
379
|
-
* - Both optional and nullable: `RefProxy<User> | undefined` → user?.name works
|
|
380
|
-
*
|
|
381
|
-
* The key insight is that `RefProxy<User | undefined>` would NOT allow `user?.name`
|
|
382
|
-
* because the undefined is "inside" the proxy, but `RefProxy<User> | undefined`
|
|
383
|
-
* does allow it because the undefined is "outside" the proxy.
|
|
384
|
-
*
|
|
385
|
-
* The logic prioritizes optional chaining by always placing `undefined` outside when
|
|
386
|
-
* a type is both optional and nullable (e.g., `string | null | undefined`).
|
|
388
|
+
* - Required field: `Ref<User>` → user.name works, result is T
|
|
389
|
+
* - Nullable join side: `Ref<User, true>` → user.name works, result is T | undefined
|
|
387
390
|
*
|
|
388
391
|
* After `select()` is called, this type also includes `$selected` which provides access
|
|
389
392
|
* to the SELECT result fields via `$selected.fieldName` syntax.
|
|
@@ -394,17 +397,17 @@ export type RefsForContext<TContext extends Context> = {
|
|
|
394
397
|
> extends true
|
|
395
398
|
? IsNonExactNullable<TContext[`schema`][K]> extends true
|
|
396
399
|
? // T is both non-exact optional and non-exact nullable (e.g., string | null | undefined)
|
|
397
|
-
|
|
398
|
-
|
|
400
|
+
// Extract the non-undefined and non-null part, mark as nullable ref
|
|
401
|
+
Ref<NonNullable<TContext[`schema`][K]>, true>
|
|
399
402
|
: // T is optional (T | undefined) but not exactly undefined, and not nullable
|
|
400
|
-
|
|
401
|
-
|
|
403
|
+
// Extract the non-undefined part, mark as nullable ref
|
|
404
|
+
Ref<NonUndefined<TContext[`schema`][K]>, true>
|
|
402
405
|
: IsNonExactNullable<TContext[`schema`][K]> extends true
|
|
403
406
|
? // T is nullable (T | null) but not exactly null, and not optional
|
|
404
|
-
// Extract the non-null part
|
|
405
|
-
Ref<NonNull<TContext[`schema`][K]
|
|
407
|
+
// Extract the non-null part, mark as nullable ref
|
|
408
|
+
Ref<NonNull<TContext[`schema`][K]>, true>
|
|
406
409
|
: // T is exactly undefined, exactly null, or neither optional nor nullable
|
|
407
|
-
// Wrap in
|
|
410
|
+
// Wrap in Ref as-is (includes exact undefined, exact null, and normal types)
|
|
408
411
|
Ref<TContext[`schema`][K]>
|
|
409
412
|
} & (TContext[`result`] extends object
|
|
410
413
|
? { $selected: Ref<TContext[`result`]> }
|
|
@@ -479,41 +482,44 @@ type NonNull<T> = T extends null ? never : T
|
|
|
479
482
|
* It provides a recursive interface that allows nested property access while
|
|
480
483
|
* preserving optionality and nullability correctly.
|
|
481
484
|
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
485
|
+
* The `Nullable` parameter indicates whether this ref comes from a nullable
|
|
486
|
+
* join side (left/right/full). When `true`, the `Nullable` flag propagates
|
|
487
|
+
* through all nested property accesses, ensuring the result type includes
|
|
488
|
+
* `| undefined` for all fields accessed through this ref.
|
|
484
489
|
*
|
|
485
490
|
* Example usage:
|
|
486
491
|
* ```typescript
|
|
487
|
-
* //
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
492
|
+
* // Non-nullable ref (inner join or from table):
|
|
493
|
+
* select(({ user }) => ({ name: user.name })) // result: string
|
|
494
|
+
*
|
|
495
|
+
* // Nullable ref (left join right side):
|
|
496
|
+
* select(({ dept }) => ({ name: dept.name })) // result: string | undefined
|
|
491
497
|
*
|
|
492
498
|
* // Spread operations work cleanly:
|
|
493
499
|
* select(({ user }) => ({ ...user })) // Returns User type, not Ref types
|
|
494
500
|
* ```
|
|
495
501
|
*/
|
|
496
|
-
export type Ref<T = any> = {
|
|
502
|
+
export type Ref<T = any, Nullable extends boolean = false> = {
|
|
497
503
|
[K in keyof T]: IsNonExactOptional<T[K]> extends true
|
|
498
504
|
? IsNonExactNullable<T[K]> extends true
|
|
499
505
|
? // Both optional and nullable
|
|
500
506
|
IsPlainObject<NonNullable<T[K]>> extends true
|
|
501
|
-
? Ref<NonNullable<T[K]
|
|
502
|
-
: RefLeaf<NonNullable<T[K]
|
|
507
|
+
? Ref<NonNullable<T[K]>, Nullable> | undefined
|
|
508
|
+
: RefLeaf<NonNullable<T[K]>, Nullable> | undefined
|
|
503
509
|
: // Optional only
|
|
504
510
|
IsPlainObject<NonUndefined<T[K]>> extends true
|
|
505
|
-
? Ref<NonUndefined<T[K]
|
|
506
|
-
: RefLeaf<NonUndefined<T[K]
|
|
511
|
+
? Ref<NonUndefined<T[K]>, Nullable> | undefined
|
|
512
|
+
: RefLeaf<NonUndefined<T[K]>, Nullable> | undefined
|
|
507
513
|
: IsNonExactNullable<T[K]> extends true
|
|
508
514
|
? // Nullable only
|
|
509
515
|
IsPlainObject<NonNull<T[K]>> extends true
|
|
510
|
-
? Ref<NonNull<T[K]
|
|
511
|
-
: RefLeaf<NonNull<T[K]
|
|
516
|
+
? Ref<NonNull<T[K]>, Nullable> | null
|
|
517
|
+
: RefLeaf<NonNull<T[K]>, Nullable> | null
|
|
512
518
|
: // Required
|
|
513
519
|
IsPlainObject<T[K]> extends true
|
|
514
|
-
? Ref<T[K]>
|
|
515
|
-
: RefLeaf<T[K]>
|
|
516
|
-
} & RefLeaf<T>
|
|
520
|
+
? Ref<T[K], Nullable>
|
|
521
|
+
: RefLeaf<T[K], Nullable>
|
|
522
|
+
} & RefLeaf<T, Nullable>
|
|
517
523
|
|
|
518
524
|
/**
|
|
519
525
|
* Ref - The user-facing ref type with clean IDE display
|
|
@@ -527,11 +533,19 @@ export type Ref<T = any> = {
|
|
|
527
533
|
* - No internal properties like __refProxy, __path, __type are visible
|
|
528
534
|
*/
|
|
529
535
|
declare const RefBrand: unique symbol
|
|
530
|
-
|
|
536
|
+
declare const NullableBrand: unique symbol
|
|
537
|
+
export type RefLeaf<T = any, Nullable extends boolean = false> = {
|
|
538
|
+
readonly [RefBrand]?: T
|
|
539
|
+
} & ([Nullable] extends [true] ? { readonly [NullableBrand]?: true } : {})
|
|
540
|
+
|
|
541
|
+
// Detect NullableBrand by checking for the key's presence
|
|
542
|
+
type IsNullableRef<T> = typeof NullableBrand extends keyof T ? true : false
|
|
531
543
|
|
|
532
|
-
// Helper type to remove RefBrand from objects
|
|
544
|
+
// Helper type to remove RefBrand and NullableBrand from objects
|
|
533
545
|
type WithoutRefBrand<T> =
|
|
534
|
-
T extends Record<string, any>
|
|
546
|
+
T extends Record<string, any>
|
|
547
|
+
? Omit<T, typeof RefBrand | typeof NullableBrand>
|
|
548
|
+
: T
|
|
535
549
|
|
|
536
550
|
/**
|
|
537
551
|
* PreserveSingleResultFlag - Conditionally includes the singleResult flag
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
CollectionInputNotFoundError,
|
|
5
5
|
DistinctRequiresSelectError,
|
|
6
6
|
DuplicateAliasInSubqueryError,
|
|
7
|
+
FnSelectWithGroupByError,
|
|
7
8
|
HavingRequiresGroupByError,
|
|
8
9
|
LimitOffsetRequireOrderByError,
|
|
9
10
|
UnsupportedFromTypeError,
|
|
@@ -218,6 +219,10 @@ export function compileQuery(
|
|
|
218
219
|
throw new DistinctRequiresSelectError()
|
|
219
220
|
}
|
|
220
221
|
|
|
222
|
+
if (query.fnSelect && query.groupBy && query.groupBy.length > 0) {
|
|
223
|
+
throw new FnSelectWithGroupByError()
|
|
224
|
+
}
|
|
225
|
+
|
|
221
226
|
// Process the SELECT clause early - always create $selected
|
|
222
227
|
// This eliminates duplication and allows for DISTINCT implementation
|
|
223
228
|
if (query.fnSelect) {
|
package/src/query/index.ts
CHANGED
|
@@ -74,6 +74,9 @@ export {
|
|
|
74
74
|
liveQueryCollectionOptions,
|
|
75
75
|
} from './live-query-collection.js'
|
|
76
76
|
|
|
77
|
+
// One-shot query execution
|
|
78
|
+
export { queryOnce, type QueryOnceConfig } from './query-once.js'
|
|
79
|
+
|
|
77
80
|
export { type LiveQueryCollectionConfig } from './live/types.js'
|
|
78
81
|
export { type LiveQueryCollectionUtils } from './live/collection-config-builder.js'
|
|
79
82
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createLiveQueryCollection } from './live-query-collection.js'
|
|
2
|
+
import type { InitialQueryBuilder, QueryBuilder } from './builder/index.js'
|
|
3
|
+
import type { Context, InferResultType } from './builder/types.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration options for queryOnce
|
|
7
|
+
*/
|
|
8
|
+
export interface QueryOnceConfig<TContext extends Context> {
|
|
9
|
+
/**
|
|
10
|
+
* Query builder function that defines the query
|
|
11
|
+
*/
|
|
12
|
+
query:
|
|
13
|
+
| ((q: InitialQueryBuilder) => QueryBuilder<TContext>)
|
|
14
|
+
| QueryBuilder<TContext>
|
|
15
|
+
// Future: timeout, signal, etc.
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Overload 1: Simple query function returning array (non-single result)
|
|
19
|
+
/**
|
|
20
|
+
* Executes a one-shot query and returns the results as an array.
|
|
21
|
+
*
|
|
22
|
+
* This function creates a live query collection, preloads it, extracts the results,
|
|
23
|
+
* and automatically cleans up the collection. It's ideal for:
|
|
24
|
+
* - AI/LLM context building
|
|
25
|
+
* - Data export
|
|
26
|
+
* - Background processing
|
|
27
|
+
* - Testing
|
|
28
|
+
*
|
|
29
|
+
* @param queryFn - A function that receives the query builder and returns a query
|
|
30
|
+
* @returns A promise that resolves to an array of query results
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // Basic query
|
|
35
|
+
* const users = await queryOnce((q) =>
|
|
36
|
+
* q.from({ user: usersCollection })
|
|
37
|
+
* )
|
|
38
|
+
*
|
|
39
|
+
* // With filtering and projection
|
|
40
|
+
* const activeUserNames = await queryOnce((q) =>
|
|
41
|
+
* q.from({ user: usersCollection })
|
|
42
|
+
* .where(({ user }) => eq(user.active, true))
|
|
43
|
+
* .select(({ user }) => ({ name: user.name }))
|
|
44
|
+
* )
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function queryOnce<TContext extends Context>(
|
|
48
|
+
queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,
|
|
49
|
+
): Promise<InferResultType<TContext>>
|
|
50
|
+
|
|
51
|
+
// Overload 2: Config object form returning array (non-single result)
|
|
52
|
+
/**
|
|
53
|
+
* Executes a one-shot query using a configuration object.
|
|
54
|
+
*
|
|
55
|
+
* @param config - Configuration object with the query function
|
|
56
|
+
* @returns A promise that resolves to an array of query results
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const recentOrders = await queryOnce({
|
|
61
|
+
* query: (q) =>
|
|
62
|
+
* q.from({ order: ordersCollection })
|
|
63
|
+
* .orderBy(({ order }) => desc(order.createdAt))
|
|
64
|
+
* .limit(100),
|
|
65
|
+
* })
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export function queryOnce<TContext extends Context>(
|
|
69
|
+
config: QueryOnceConfig<TContext>,
|
|
70
|
+
): Promise<InferResultType<TContext>>
|
|
71
|
+
|
|
72
|
+
// Implementation
|
|
73
|
+
export async function queryOnce<TContext extends Context>(
|
|
74
|
+
configOrQuery:
|
|
75
|
+
| QueryOnceConfig<TContext>
|
|
76
|
+
| ((q: InitialQueryBuilder) => QueryBuilder<TContext>),
|
|
77
|
+
): Promise<InferResultType<TContext>> {
|
|
78
|
+
// Normalize input
|
|
79
|
+
const config: QueryOnceConfig<TContext> =
|
|
80
|
+
typeof configOrQuery === `function`
|
|
81
|
+
? { query: configOrQuery }
|
|
82
|
+
: configOrQuery
|
|
83
|
+
|
|
84
|
+
const query = (q: InitialQueryBuilder) => {
|
|
85
|
+
const queryConfig = config.query
|
|
86
|
+
return typeof queryConfig === `function` ? queryConfig(q) : queryConfig
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Create collection with minimal GC time; preload handles sync start
|
|
90
|
+
const collection = createLiveQueryCollection({
|
|
91
|
+
query,
|
|
92
|
+
gcTime: 1, // Cleanup in next tick when no subscribers (0 disables GC)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// Wait for initial data load
|
|
97
|
+
await collection.preload()
|
|
98
|
+
|
|
99
|
+
// Check if this is a single-result query (findOne was called)
|
|
100
|
+
const isSingleResult =
|
|
101
|
+
(collection.config as { singleResult?: boolean }).singleResult === true
|
|
102
|
+
|
|
103
|
+
// Extract and return results
|
|
104
|
+
if (isSingleResult) {
|
|
105
|
+
const first = collection.values().next().value as
|
|
106
|
+
| InferResultType<TContext>
|
|
107
|
+
| undefined
|
|
108
|
+
return first as InferResultType<TContext>
|
|
109
|
+
}
|
|
110
|
+
return collection.toArray as InferResultType<TContext>
|
|
111
|
+
} finally {
|
|
112
|
+
// Always cleanup, even on error
|
|
113
|
+
await collection.cleanup()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -126,28 +126,29 @@ export class DeduplicatedLoadSubset {
|
|
|
126
126
|
return prom
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
// Not fully covered by existing data
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
|
|
129
|
+
// Not fully covered by existing data — load the missing subset.
|
|
130
|
+
// We need two clones: trackingOptions preserves the original predicate for
|
|
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.
|
|
133
|
+
const trackingOptions = cloneOptions(options)
|
|
134
|
+
const loadOptions = cloneOptions(options)
|
|
133
135
|
if (this.unlimitedWhere !== undefined && options.limit === undefined) {
|
|
134
136
|
// Compute difference to get only the missing data
|
|
135
137
|
// We can only do this for unlimited queries
|
|
136
138
|
// and we can only remove data that was loaded from unlimited queries
|
|
137
139
|
// because with limited queries we have no way to express that we already loaded part of the matching data
|
|
138
|
-
|
|
139
|
-
minusWherePredicates(
|
|
140
|
-
|
|
140
|
+
loadOptions.where =
|
|
141
|
+
minusWherePredicates(loadOptions.where, this.unlimitedWhere) ??
|
|
142
|
+
loadOptions.where
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
// Call underlying loadSubset to load the missing data
|
|
144
|
-
const resultPromise = this._loadSubset(
|
|
146
|
+
const resultPromise = this._loadSubset(loadOptions)
|
|
145
147
|
|
|
146
148
|
// Handle both sync (true) and async (Promise<void>) return values
|
|
147
149
|
if (resultPromise === true) {
|
|
148
|
-
// Sync return - update tracking
|
|
149
|
-
|
|
150
|
-
this.updateTracking(clonedOptions)
|
|
150
|
+
// Sync return - update tracking with the original predicate
|
|
151
|
+
this.updateTracking(trackingOptions)
|
|
151
152
|
return true
|
|
152
153
|
} else {
|
|
153
154
|
// Async return - track the promise and update tracking after it resolves
|
|
@@ -158,16 +159,14 @@ export class DeduplicatedLoadSubset {
|
|
|
158
159
|
|
|
159
160
|
// We need to create a reference to the in-flight entry so we can remove it later
|
|
160
161
|
const inflightEntry = {
|
|
161
|
-
options:
|
|
162
|
+
options: loadOptions, // Store load options for subset matching of in-flight requests
|
|
162
163
|
promise: resultPromise
|
|
163
164
|
.then((result) => {
|
|
164
165
|
// Only update tracking if this request is still from the current generation
|
|
165
166
|
// If reset() was called, the generation will have incremented and we should
|
|
166
167
|
// not repopulate the state that was just cleared
|
|
167
168
|
if (capturedGeneration === this.generation) {
|
|
168
|
-
|
|
169
|
-
// This ensures we track exactly what was loaded, not what the caller changed
|
|
170
|
-
this.updateTracking(clonedOptions)
|
|
169
|
+
this.updateTracking(trackingOptions)
|
|
171
170
|
}
|
|
172
171
|
return result
|
|
173
172
|
})
|