1- import type { Collection , CollectionDefaults , CustomHookMeta , FindOptions , HookMetaQueryTracking , HybridPromise , ResolvedCollection , StoreSchema , WrappedItemBase } from '@rstore/shared'
1+ import type { Collection , CollectionDefaults , CustomHookMeta , FindOptions , HookMetaQueryTracking , HybridPromise , StoreSchema } from '@rstore/shared'
22import type { MaybeRefOrGetter , Ref } from 'vue'
33import type { VueStore } from './store'
4- import { tryOnScopeDispose } from '@vueuse/core '
5- import { computed , nextTick , ref , shallowRef , toValue , watch } from 'vue '
4+ import { computed , ref , shallowRef , toValue , watch } from 'vue '
5+ import { useQueryTracking } from './tracking '
66
77export interface VueQueryReturn <
88 TCollection extends Collection ,
@@ -29,12 +29,10 @@ export interface VueCreateQueryOptions<
2929 TResult ,
3030> {
3131 store : VueStore < TSchema , TCollectionDefaults >
32- collection : ResolvedCollection < TCollection , TCollectionDefaults , TSchema >
3332 fetchMethod : ( options : TOptions | undefined , meta : CustomHookMeta ) => Promise < TResult >
3433 cacheMethod : ( options : TOptions | undefined , meta : CustomHookMeta ) => TResult
3534 defaultValue : MaybeRefOrGetter < TResult >
3635 options ?: MaybeRefOrGetter < TOptions | undefined | { enabled : boolean } >
37- name : MaybeRefOrGetter < string >
3836}
3937
4038/**
@@ -52,11 +50,7 @@ export function createQuery<
5250 cacheMethod,
5351 defaultValue,
5452 options,
55- collection,
56- name,
5753} : VueCreateQueryOptions < TCollection , TCollectionDefaults , TSchema , TOptions , TResult > ) : HybridPromise < VueQueryReturn < TCollection , TCollectionDefaults , TSchema , TResult > > {
58- const trackingQueryId = `${ collection . name } :${ toValue ( name ) } :${ crypto . randomUUID ( ) } `
59-
6054 function getOptions ( ) : TOptions | undefined {
6155 const result = toValue ( options )
6256 return typeof result === 'object' && 'enabled' in result && result . enabled === false ? undefined : result as TOptions
@@ -69,41 +63,34 @@ export function createQuery<
6963
7064 let fetchPolicy = store . $getFetchPolicy ( getOptions ( ) ?. fetchPolicy )
7165
72- let queryTracking : HookMetaQueryTracking | null = null
73- const queryTrackingEnabled = ! store . $isServer && ( getOptions ( ) ?. experimentalGarbageCollection ?? store . $experimentalGarbageCollection )
66+ const queryTrackingEnabled = ! store . $isServer && fetchPolicy !== 'no-cache' && ( getOptions ( ) ?. experimentalGarbageCollection ?? store . $experimentalGarbageCollection )
7467
7568 const result : Ref < TResult > = shallowRef ( toValue ( defaultValue ) )
7669 const meta = ref < CustomHookMeta > ( { } )
7770
78- const dataKey = ref ( 0 )
79-
8071 // @TODO include nested relations in no-cache results
81- const data = computed ( ( ) => {
82- // eslint-disable-next-line ts/no-unused-expressions
83- dataKey . value // track dataKey to force recompute
84-
72+ const cached = computed ( ( ) => {
8573 if ( fetchPolicy !== 'no-cache' ) {
8674 const options = getOptions ( )
87- const result = cacheMethod ( options , meta . value ) ?? null
88- if ( result && queryTrackingEnabled ) {
89- if ( Array . isArray ( result ) ) {
90- return result . filter ( ( item : WrappedItemBase < TCollection , TCollectionDefaults , TSchema > ) => ! item . $meta . dirtyQueries . has ( trackingQueryId ) )
91- }
92- else {
93- return ! ( result as unknown as WrappedItemBase < TCollection , TCollectionDefaults , TSchema > ) . $meta . dirtyQueries . has ( trackingQueryId ) ? result : null
94- }
95- }
96- return result
75+ return cacheMethod ( options , meta . value ) ?? null
9776 }
9877 return result . value
9978 } ) as Ref < TResult >
10079
80+ const queryTracking = queryTrackingEnabled
81+ ? useQueryTracking < TResult > ( {
82+ store,
83+ result,
84+ cached,
85+ } )
86+ : null
87+
10188 const loading = ref ( false )
10289
10390 const error = ref < Error | null > ( null )
10491
10592 const returnObject : VueQueryReturn < TCollection , TCollectionDefaults , TSchema , TResult > = {
106- data,
93+ data : queryTracking ?. filteredCached ?? cached ,
10794 loading,
10895 error,
10996 refresh,
@@ -133,9 +120,7 @@ export function createQuery<
133120 ...meta . value ,
134121 $queryTracking : queryTrackingEnabled ? newQueryTracking : undefined ,
135122 } ) . then ( ( ) => {
136- if ( queryTrackingEnabled ) {
137- handleQueryTracking ( newQueryTracking )
138- }
123+ queryTracking ?. handleQueryTracking ( newQueryTracking )
139124 } )
140125 }
141126
@@ -151,7 +136,7 @@ export function createQuery<
151136 : meta . value )
152137
153138 if ( queryTrackingEnabled && shouldHandleQueryTracking ) {
154- handleQueryTracking ( newQueryTracking )
139+ queryTracking ?. handleQueryTracking ( newQueryTracking )
155140 }
156141 }
157142 catch ( e : any ) {
@@ -166,134 +151,6 @@ export function createQuery<
166151 return returnObject
167152 }
168153
169- function handleQueryTracking ( newQueryTracking : HookMetaQueryTracking ) {
170- const isResultEmpty = result . value == null || ( Array . isArray ( result . value ) && result . value . length === 0 )
171-
172- // Init the query tracking object if the result is not empty and there is no previous tracking
173- if ( ! isResultEmpty && ! queryTracking ) {
174- const keys = Object . keys ( newQueryTracking )
175- const isNewQueryTrackingEmpty = keys . length === 0 || keys . every ( k => newQueryTracking [ k ] ! . size === 0 )
176-
177- if ( isNewQueryTrackingEmpty ) {
178- const list = ( Array . isArray ( result . value ) ? result . value : [ result . value ] )
179- for ( const item of list ) {
180- if ( item ) {
181- addToQueryTracking ( newQueryTracking , item )
182- }
183- }
184- }
185-
186- {
187- queryTracking = { }
188- const list = Array . isArray ( data . value ) ? data . value : ( data . value ? [ data . value ] : [ ] )
189- for ( const item of list ) {
190- if ( item ) {
191- addToQueryTracking ( queryTracking , item )
192- }
193- }
194- }
195-
196- function addToQueryTracking ( qt : HookMetaQueryTracking , item : WrappedItemBase < TCollection , TCollectionDefaults , TSchema > ) {
197- const collection = store . $collections . find ( c => c . name === item . $collection )
198- if ( ! collection ) {
199- throw new Error ( `Collection ${ item . $collection } not found in the store` )
200- }
201- const set = qt ! [ collection . name ] ??= new Set ( )
202- if ( set . has ( item . $getKey ( ) ) ) {
203- return
204- }
205- set . add ( item . $getKey ( ) )
206- for ( const relationName in collection . relations ) {
207- const value = item [ relationName as keyof typeof item ]
208- if ( Array . isArray ( value ) ) {
209- for ( const relatedItem of value ) {
210- if ( relatedItem ) {
211- addToQueryTracking ( qt , relatedItem as WrappedItemBase < TCollection , TCollectionDefaults , TSchema > )
212- }
213- }
214- }
215- }
216- item . $meta . queries . add ( trackingQueryId )
217- }
218- }
219-
220- // Mark new tracked items as fresh
221- for ( const collectionName in newQueryTracking ) {
222- const collection = store . $collections . find ( c => c . name === collectionName ) !
223- const oldKeys = queryTracking ?. [ collectionName ]
224- for ( const key of newQueryTracking [ collectionName ] ! ) {
225- const item = store . $cache . readItem ( {
226- collection,
227- key,
228- } ) as WrappedItemBase < TCollection , TCollectionDefaults , TSchema > | undefined
229- if ( item ) {
230- item . $meta . queries . add ( trackingQueryId )
231- item . $meta . dirtyQueries . delete ( trackingQueryId )
232- oldKeys ?. delete ( key )
233- }
234- }
235- }
236-
237- // Mark old tracked items as dirty if they are not tracked anymore
238- let hasAddedDirty = false
239- for ( const collectionName in queryTracking ) {
240- const collection = store . $collections . find ( c => c . name === collectionName ) !
241- for ( const key of queryTracking [ collectionName ] ! ) {
242- const item = store . $cache . readItem ( {
243- collection,
244- key,
245- } ) as WrappedItemBase < TCollection , TCollectionDefaults , TSchema > | undefined
246- if ( item ) {
247- item . $meta . queries . delete ( trackingQueryId )
248- item . $meta . dirtyQueries . add ( trackingQueryId )
249-
250- hasAddedDirty = true
251-
252- // Clean garbage after the dirty items have a change to be removed from other queries
253- // (e.g. after `dataKey.value++` updates the `data` computed property)
254- nextTick ( ( ) => {
255- store . $cache . garbageCollectItem ( {
256- collection,
257- item : item as any ,
258- } )
259- } )
260- }
261- }
262- }
263-
264- // Filter out dirty items from the results
265- if ( hasAddedDirty ) {
266- // Force refresh of the data computed
267- dataKey . value ++
268- }
269-
270- queryTracking = newQueryTracking
271- }
272-
273- // Mark tracked items as dirty on unmount
274- if ( queryTrackingEnabled ) {
275- tryOnScopeDispose ( ( ) => {
276- for ( const collectionName in queryTracking ) {
277- const collection = store . $collections . find ( c => c . name === collectionName ) !
278- for ( const key of queryTracking [ collectionName ] ! ) {
279- const item = store . $cache . readItem ( {
280- collection,
281- key,
282- } ) as WrappedItemBase < TCollection , TCollectionDefaults , TSchema > | undefined
283- if ( item ) {
284- item . $meta . queries . delete ( trackingQueryId )
285- item . $meta . dirtyQueries . add ( trackingQueryId )
286-
287- store . $cache . garbageCollectItem ( {
288- collection,
289- item : item as any ,
290- } )
291- }
292- }
293- }
294- } )
295- }
296-
297154 // Auto load on options change
298155 watch ( ( ) => toValue ( options ) , ( ) => {
299156 load ( )
0 commit comments