@codeleap/query 5.8.3 → 5.8.5
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/package.json +6 -10
- package/package.json.bak +3 -7
- package/src/factors/createQueryManager.ts +38 -0
- package/src/factors/createQueryOperations.ts +37 -0
- package/src/factors/index.ts +2 -0
- package/src/index.ts +2 -8
- package/src/lib/Mutations.ts +280 -0
- package/src/{queryClient.ts → lib/QueryClientEnhanced/index.ts} +24 -72
- package/src/lib/QueryClientEnhanced/types.ts +38 -0
- package/src/lib/QueryKeys.ts +319 -0
- package/src/lib/QueryManager.ts +488 -0
- package/src/lib/QueryOperations/index.ts +351 -0
- package/src/lib/QueryOperations/types.ts +47 -0
- package/src/lib/index.ts +5 -0
- package/src/tests/Mutations.spec.tsx +458 -0
- package/src/tests/QueryManager.spec.tsx +920 -0
- package/src/tests/QueryOperations.spec.tsx +109 -0
- package/src/tests/integration.spec.tsx +551 -0
- package/src/tests/setup.ts +119 -0
- package/src/types/core.ts +33 -0
- package/src/types/create.ts +16 -0
- package/src/types/delete.ts +15 -0
- package/src/types/index.ts +7 -0
- package/src/types/list.ts +24 -0
- package/src/types/retrieve.ts +7 -0
- package/src/types/update.ts +14 -0
- package/src/types/utility.ts +22 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/misc.ts +43 -0
- package/src/QueryManager.ts +0 -954
- package/src/types.ts +0 -199
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codeleap/query",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.5",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -9,23 +9,19 @@
|
|
|
9
9
|
"directory": "packages/query"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@codeleap/config": "5.8.
|
|
13
|
-
"@codeleap/types": "5.8.
|
|
14
|
-
"@codeleap/utils": "5.8.3",
|
|
15
|
-
"@codeleap/hooks": "5.8.3",
|
|
12
|
+
"@codeleap/config": "5.8.5",
|
|
13
|
+
"@codeleap/types": "5.8.5",
|
|
16
14
|
"ts-node-dev": "1.1.8"
|
|
17
15
|
},
|
|
18
16
|
"scripts": {
|
|
19
17
|
"build": "echo 'No build needed'"
|
|
20
18
|
},
|
|
21
19
|
"peerDependencies": {
|
|
22
|
-
"@codeleap/types": "5.8.
|
|
23
|
-
"@codeleap/utils": "5.8.3",
|
|
24
|
-
"@codeleap/hooks": "5.8.3",
|
|
20
|
+
"@codeleap/types": "5.8.5",
|
|
25
21
|
"typescript": "5.5.2",
|
|
26
|
-
"@tanstack/react-query": "5.
|
|
22
|
+
"@tanstack/react-query": "5.89.0"
|
|
27
23
|
},
|
|
28
24
|
"dependencies": {
|
|
29
|
-
"
|
|
25
|
+
"fast-deep-equal": "3.1.3"
|
|
30
26
|
}
|
|
31
27
|
}
|
package/package.json.bak
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codeleap/query",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.5",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@codeleap/config": "workspace:*",
|
|
13
13
|
"@codeleap/types": "workspace:*",
|
|
14
|
-
"@codeleap/utils": "workspace:*",
|
|
15
|
-
"@codeleap/hooks": "workspace:*",
|
|
16
14
|
"ts-node-dev": "1.1.8"
|
|
17
15
|
},
|
|
18
16
|
"scripts": {
|
|
@@ -20,12 +18,10 @@
|
|
|
20
18
|
},
|
|
21
19
|
"peerDependencies": {
|
|
22
20
|
"@codeleap/types": "workspace:*",
|
|
23
|
-
"@codeleap/utils": "workspace:*",
|
|
24
|
-
"@codeleap/hooks": "workspace:*",
|
|
25
21
|
"typescript": "5.5.2",
|
|
26
|
-
"@tanstack/react-query": "5.
|
|
22
|
+
"@tanstack/react-query": "5.89.0"
|
|
27
23
|
},
|
|
28
24
|
"dependencies": {
|
|
29
|
-
"
|
|
25
|
+
"fast-deep-equal": "3.1.3"
|
|
30
26
|
}
|
|
31
27
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { QueryManager } from '../lib'
|
|
2
|
+
import { QueryItem, QueryManagerOptions } from '../types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Factory function to create a new QueryManager instance
|
|
6
|
+
* @template T - The query item type that extends QueryItem
|
|
7
|
+
* @template F - The filter type used for list queries
|
|
8
|
+
* @param options - Configuration options for the query manager
|
|
9
|
+
* @returns New QueryManager instance
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* interface User extends QueryItem {
|
|
14
|
+
* name: string
|
|
15
|
+
* email: string
|
|
16
|
+
* status: 'active' | 'inactive'
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* interface UserFilters {
|
|
20
|
+
* status?: 'active' | 'inactive'
|
|
21
|
+
* search?: string
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* const userQueryManager = createQueryManager<User, UserFilters>({
|
|
25
|
+
* name: 'users',
|
|
26
|
+
* queryClient,
|
|
27
|
+
* listFn: (limit, offset, filters) => api.getUsers({ limit, offset, ...filters }),
|
|
28
|
+
* retrieveFn: (id) => api.getUser(id),
|
|
29
|
+
* createFn: (data) => api.createUser(data),
|
|
30
|
+
* updateFn: (data) => api.updateUser(data.id, data),
|
|
31
|
+
* deleteFn: (id) => api.deleteUser(id),
|
|
32
|
+
* listLimit: 20
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export const createQueryManager = <T extends QueryItem, F = {}>(options: QueryManagerOptions<T, F>) => {
|
|
37
|
+
return new QueryManager<T, F>(options)
|
|
38
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { QueryOperations } from '../lib'
|
|
2
|
+
import { QueryOperationsOptions } from '../lib/QueryOperations/types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Factory function to create a new QueryOperations builder instance
|
|
6
|
+
* @param options - Configuration options including the QueryClient
|
|
7
|
+
* @returns New QueryOperations instance ready for operation registration
|
|
8
|
+
*
|
|
9
|
+
* @description
|
|
10
|
+
* This is the entry point for creating a new QueryOperations instance. Use this
|
|
11
|
+
* function to start building your collection of queries and mutations.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { QueryClient } from '@tanstack/react-query'
|
|
16
|
+
*
|
|
17
|
+
* const queryClient = new QueryClient()
|
|
18
|
+
*
|
|
19
|
+
* const userOperations = createQueryOperations({ queryClient })
|
|
20
|
+
* .query('getUser', async (id: string) => fetchUser(id))
|
|
21
|
+
* .query('getUsers', async (filters?: UserFilters) => fetchUsers(filters))
|
|
22
|
+
* .mutation('createUser', async (data: CreateUserData) => createUser(data))
|
|
23
|
+
* .mutation('updateUser', async (data: UpdateUserData) => updateUser(data))
|
|
24
|
+
* .mutation('deleteUser', async (id: string) => deleteUser(id))
|
|
25
|
+
*
|
|
26
|
+
* // In components
|
|
27
|
+
* function UserList() {
|
|
28
|
+
* const usersQuery = userOperations.useQuery('getUsers', { active: true })
|
|
29
|
+
* const createMutation = userOperations.useMutation('createUser')
|
|
30
|
+
*
|
|
31
|
+
* // Both hooks are fully type-safe
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function createQueryOperations(options: QueryOperationsOptions) {
|
|
36
|
+
return new QueryOperations(options)
|
|
37
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { InfiniteData } from '@tanstack/query-core'
|
|
2
|
+
import { QueryKeys } from './QueryKeys'
|
|
3
|
+
import { ItemPosition, ListPaginationResponse, PageParam, QueryClient, QueryItem, RemovedItemMap, WithTempId } from '../types'
|
|
4
|
+
import deepEqual from 'fast-deep-equal'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Class for managing mutations and cache updates for React Query list data
|
|
8
|
+
* @template T - The query item type that extends QueryItem
|
|
9
|
+
* @template F - The filter type used for list queries
|
|
10
|
+
*/
|
|
11
|
+
export class Mutations<T extends QueryItem, F> {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new Mutations instance
|
|
14
|
+
* @param queryKeys - The QueryKeys instance for managing query keys
|
|
15
|
+
* @param queryClient - The React Query client instance
|
|
16
|
+
* @param queryName - The name of the query used for identification
|
|
17
|
+
*/
|
|
18
|
+
constructor(
|
|
19
|
+
private queryKeys: QueryKeys<T, F>,
|
|
20
|
+
private queryClient: QueryClient,
|
|
21
|
+
private queryName: string
|
|
22
|
+
) { }
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Adds a new item to the cached list data
|
|
26
|
+
* @param newItem - The new item to add to the list
|
|
27
|
+
* @param position - Where to add the item: 'start', 'end', or a RemovedItemMap for specific positions
|
|
28
|
+
* @param listFilters - Optional filters to target specific list queries
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // Add item to the beginning
|
|
33
|
+
* mutations.addItem(newUser, 'start')
|
|
34
|
+
*
|
|
35
|
+
* // Add item to the end with filters
|
|
36
|
+
* mutations.addItem(newUser, 'end', { status: 'active' })
|
|
37
|
+
*
|
|
38
|
+
* // Add item to specific positions (restore from removed item map)
|
|
39
|
+
* mutations.addItem(newUser, removedItemMap)
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
addItem(newItem: T, position: 'start' | 'end' | RemovedItemMap = 'start', listFilters?: F) {
|
|
43
|
+
const isMultiQueryKeys = Array.isArray(position) && position?.length >= 1
|
|
44
|
+
|
|
45
|
+
if (isMultiQueryKeys) {
|
|
46
|
+
for (const [queryKey, itemPosition] of position) {
|
|
47
|
+
const currentData = this.queryClient.getQueryData<InfiniteData<ListPaginationResponse<T>, PageParam>>(queryKey)
|
|
48
|
+
|
|
49
|
+
const updatedPages = [...(currentData?.pages || [])]
|
|
50
|
+
|
|
51
|
+
if (itemPosition.pageIndex < updatedPages.length) {
|
|
52
|
+
const targetPage = [...updatedPages[itemPosition.pageIndex]]
|
|
53
|
+
|
|
54
|
+
const insertIndex = Math.min(itemPosition.itemIndex, targetPage.length)
|
|
55
|
+
targetPage.splice(insertIndex, 0, newItem)
|
|
56
|
+
updatedPages[itemPosition.pageIndex] = targetPage
|
|
57
|
+
} else {
|
|
58
|
+
const lastPageIndex = updatedPages.length - 1
|
|
59
|
+
if (lastPageIndex >= 0) {
|
|
60
|
+
updatedPages[lastPageIndex] = [...updatedPages[lastPageIndex], newItem]
|
|
61
|
+
} else {
|
|
62
|
+
updatedPages.push([newItem])
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const newData = {
|
|
67
|
+
...currentData,
|
|
68
|
+
pages: updatedPages
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.queryClient.setQueryData(queryKey, newData)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const queryKey = this.queryKeys.listKeyWithFilters(listFilters)
|
|
78
|
+
|
|
79
|
+
const currentData = this.queryClient.getQueryData<InfiniteData<ListPaginationResponse<T>, PageParam>>(queryKey)
|
|
80
|
+
|
|
81
|
+
if (!currentData) {
|
|
82
|
+
const newData = {
|
|
83
|
+
pageParams: [0],
|
|
84
|
+
pages: [[newItem]]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.queryClient.setQueryData(queryKey, newData)
|
|
88
|
+
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const updatedPages = [...currentData.pages]
|
|
93
|
+
|
|
94
|
+
if (position === 'start') {
|
|
95
|
+
if (updatedPages.length > 0) {
|
|
96
|
+
updatedPages[0] = [newItem, ...updatedPages[0]]
|
|
97
|
+
} else {
|
|
98
|
+
updatedPages.push([newItem])
|
|
99
|
+
}
|
|
100
|
+
} else if (position === 'end') {
|
|
101
|
+
if (updatedPages.length > 0) {
|
|
102
|
+
const lastPageIndex = updatedPages.length - 1
|
|
103
|
+
updatedPages[lastPageIndex] = [...updatedPages[lastPageIndex], newItem]
|
|
104
|
+
} else {
|
|
105
|
+
updatedPages.push([newItem])
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const newData = {
|
|
110
|
+
...currentData,
|
|
111
|
+
pages: updatedPages
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.queryClient.setQueryData(queryKey, newData)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Removes an item from all cached list queries and returns the positions where it was found
|
|
119
|
+
* @param itemId - The ID of the item to remove
|
|
120
|
+
* @returns A RemovedItemMap containing the query keys and positions where the item was found, or null if not found
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const removedPositions = mutations.removeItem('user-123')
|
|
125
|
+
*
|
|
126
|
+
* // Later, restore the item to its original positions
|
|
127
|
+
* if (removedPositions) {
|
|
128
|
+
* mutations.addItem(restoredUser, removedPositions)
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
removeItem(itemId: QueryItem['id']): RemovedItemMap | null {
|
|
133
|
+
this.queryKeys.removeRetrieveQueryData(itemId)
|
|
134
|
+
|
|
135
|
+
const listQueries = this.queryKeys.getAllListQueries()
|
|
136
|
+
|
|
137
|
+
const removedItemMap: RemovedItemMap = []
|
|
138
|
+
|
|
139
|
+
for (const query of listQueries) {
|
|
140
|
+
const currentData = query.state?.data
|
|
141
|
+
const queryKey = query?.queryKey
|
|
142
|
+
|
|
143
|
+
if (!currentData) continue
|
|
144
|
+
|
|
145
|
+
let removedItemPosition: ItemPosition | null = null
|
|
146
|
+
|
|
147
|
+
for (let pageIndex = 0; pageIndex < currentData?.pages?.length; pageIndex++) {
|
|
148
|
+
const page = currentData.pages[pageIndex]
|
|
149
|
+
const itemIndex = page.findIndex((item: T) => item?.id === itemId)
|
|
150
|
+
|
|
151
|
+
if (itemIndex !== -1) {
|
|
152
|
+
removedItemPosition = {
|
|
153
|
+
pageIndex,
|
|
154
|
+
itemIndex,
|
|
155
|
+
}
|
|
156
|
+
break
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!removedItemPosition) continue
|
|
161
|
+
|
|
162
|
+
removedItemMap.push([queryKey, removedItemPosition])
|
|
163
|
+
|
|
164
|
+
const updatedPages = currentData.pages.map(page =>
|
|
165
|
+
page.filter((item: T) => item?.id !== itemId)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
const filteredPages = updatedPages.filter(page => page?.length > 0)
|
|
169
|
+
const finalPages = filteredPages?.length > 0 ? filteredPages : [[]]
|
|
170
|
+
const newPageParams = currentData?.pageParams?.slice(0, finalPages?.length)
|
|
171
|
+
|
|
172
|
+
const newData = {
|
|
173
|
+
...currentData,
|
|
174
|
+
pages: finalPages,
|
|
175
|
+
pageParams: newPageParams
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this.queryClient.setQueryData(queryKey, newData)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return removedItemMap
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Updates existing items in all cached list queries and individual retrieve queries
|
|
186
|
+
* @param data - Single item or array of items to update. Items can have tempId for temporary identification
|
|
187
|
+
*
|
|
188
|
+
* @description
|
|
189
|
+
* This method:
|
|
190
|
+
* - Finds items by their ID or tempId in all list queries
|
|
191
|
+
* - Updates the items only if the data has actually changed (uses deep equality check)
|
|
192
|
+
* - Updates both list cache and individual retrieve cache
|
|
193
|
+
* - Removes tempId from the final cached data
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* // Update single item
|
|
198
|
+
* mutations.updateItems({ id: 'user-123', name: 'Updated Name', tempId: 'temp-1' })
|
|
199
|
+
*
|
|
200
|
+
* // Update multiple items
|
|
201
|
+
* mutations.updateItems([
|
|
202
|
+
* { id: 'user-123', name: 'Updated Name' },
|
|
203
|
+
* { id: 'user-456', status: 'active' }
|
|
204
|
+
* ])
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
updateItems(data: WithTempId<T> | WithTempId<T>[]) {
|
|
208
|
+
const listQueries = this.queryKeys.getAllListQueries()
|
|
209
|
+
|
|
210
|
+
const dataArray = Array.isArray(data) ? data : [data]
|
|
211
|
+
|
|
212
|
+
for (const item of dataArray) {
|
|
213
|
+
const { tempId, ...updateData } = item
|
|
214
|
+
const cachedQueryKey = this.queryKeys.keys.retrieve(updateData.id)
|
|
215
|
+
const cachedItemData = this.queryKeys.getRetrieveData(updateData.id)
|
|
216
|
+
if (!deepEqual(cachedItemData, updateData)) {
|
|
217
|
+
this.queryClient.setQueryData(cachedQueryKey, updateData)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const dataMap = Object.fromEntries(dataArray.map(item => [item?.tempId ?? item?.id, item]))
|
|
222
|
+
|
|
223
|
+
for (const query of listQueries) {
|
|
224
|
+
const oldData = query.state?.data
|
|
225
|
+
const queryKey = query?.queryKey
|
|
226
|
+
|
|
227
|
+
if (!oldData?.pages || !Array.isArray(oldData?.pages)) continue
|
|
228
|
+
|
|
229
|
+
let hasChanges = false
|
|
230
|
+
|
|
231
|
+
const updatedPages = (oldData?.pages ?? [])?.filter(Array.isArray)?.map(page => {
|
|
232
|
+
let pageChanged = false
|
|
233
|
+
|
|
234
|
+
const updatedPage = page.map((item) => {
|
|
235
|
+
if (dataMap?.[item?.id]) {
|
|
236
|
+
const { tempId, ...updateData } = dataMap?.[item?.id]
|
|
237
|
+
|
|
238
|
+
const needsUpdate = !deepEqual(item, updateData)
|
|
239
|
+
|
|
240
|
+
if (needsUpdate) {
|
|
241
|
+
pageChanged = true
|
|
242
|
+
hasChanges = true
|
|
243
|
+
return updateData
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return item
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
return pageChanged ? updatedPage : page
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
if (hasChanges) {
|
|
254
|
+
this.queryClient.setQueryData(queryKey, {
|
|
255
|
+
...oldData,
|
|
256
|
+
pages: updatedPages
|
|
257
|
+
})
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Factory function to create a new Mutations instance
|
|
265
|
+
* @template T - The query item type that extends QueryItem
|
|
266
|
+
* @template F - The filter type used for list queries
|
|
267
|
+
* @param queryKeys - The QueryKeys instance for managing query keys
|
|
268
|
+
* @param queryClient - The React Query client instance
|
|
269
|
+
* @param queryName - The name of the query used for identification
|
|
270
|
+
* @returns New Mutations instance
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* const userQueryKeys = createQueryKeys<User, UserFilters>('users', queryClient)
|
|
275
|
+
* const userMutations = createMutations(userQueryKeys, queryClient, 'users')
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
export const createMutations = <T extends QueryItem, F>(queryKeys: QueryKeys<T, F>, queryClient: QueryClient, queryName: string) => {
|
|
279
|
+
return new Mutations<T, F>(queryKeys, queryClient, queryName)
|
|
280
|
+
}
|
|
@@ -1,45 +1,11 @@
|
|
|
1
|
-
import * as ReactQuery from '@tanstack/react-query'
|
|
2
1
|
import { waitFor } from '@codeleap/utils'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { QueryClient, QueryKey, Query, hashKey, QueryCacheNotifyEvent, matchQuery, QueryOptions } from '@tanstack/react-query'
|
|
3
|
+
import { DynamicEnhancedQuery, EnhancedQuery, PollQueryOptions, PollingResult, QueryKeyBuilder } from './types'
|
|
5
4
|
|
|
6
|
-
export
|
|
5
|
+
export class QueryClientEnhanced {
|
|
6
|
+
constructor(public client: QueryClient) { }
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
stop: boolean
|
|
10
|
-
data: T
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
type PollingCallback<T, R> = (query: ReactQuery.Query<T>, count: number, prev?: R) => Promise<PollingResult<R>>
|
|
14
|
-
|
|
15
|
-
type PollQueryOptions<T, R> = {
|
|
16
|
-
interval: number
|
|
17
|
-
callback: PollingCallback<T, R>
|
|
18
|
-
leading?: boolean
|
|
19
|
-
initialData?: R
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface EnhancedQuery<T> extends ReactQuery.Query<T> {
|
|
23
|
-
waitForRefresh(): Promise<ReactQuery.Query<T>>
|
|
24
|
-
listen(callback: (e: ReactQuery.QueryCacheNotifyEvent) => void): () => void
|
|
25
|
-
refresh(): Promise<T>
|
|
26
|
-
poll<R>(
|
|
27
|
-
options: PollQueryOptions<T, R>
|
|
28
|
-
): Promise<R>
|
|
29
|
-
getData(): T
|
|
30
|
-
ensureData(options?: Partial<ReactQuery.EnsureQueryDataOptions<T, Error, T, ReactQuery.QueryKey, never>>): Promise<T>
|
|
31
|
-
key: ReactQuery.QueryKey
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
type DynamicEnhancedQuery<T, BuilderArgs extends any[]> = {
|
|
35
|
-
[P in keyof EnhancedQuery<T>]: (...args: BuilderArgs) => EnhancedQuery<T>[P]
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export class CodeleapQueryClient {
|
|
39
|
-
constructor(public client: ReactQuery.QueryClient) {
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
listenToQuery(key: ReactQuery.QueryKey, callback: (e: ReactQuery.QueryCacheNotifyEvent) => void) {
|
|
8
|
+
listenToQuery(key: QueryKey, callback: (e: QueryCacheNotifyEvent) => void) {
|
|
43
9
|
const cache = this.client.getQueryCache()
|
|
44
10
|
|
|
45
11
|
const query = cache.find({ exact: true, queryKey: key })
|
|
@@ -49,7 +15,7 @@ export class CodeleapQueryClient {
|
|
|
49
15
|
}
|
|
50
16
|
|
|
51
17
|
const removeListener = cache.subscribe((e) => {
|
|
52
|
-
const matches =
|
|
18
|
+
const matches = matchQuery({ exact: true, queryKey: key }, e.query)
|
|
53
19
|
|
|
54
20
|
if (matches) {
|
|
55
21
|
callback(e)
|
|
@@ -61,7 +27,7 @@ export class CodeleapQueryClient {
|
|
|
61
27
|
}
|
|
62
28
|
|
|
63
29
|
async pollQuery<T, R>(
|
|
64
|
-
key:
|
|
30
|
+
key: QueryKey,
|
|
65
31
|
options: PollQueryOptions<T, R>,
|
|
66
32
|
) {
|
|
67
33
|
const { interval, callback, initialData, leading = false } = options
|
|
@@ -100,10 +66,9 @@ export class CodeleapQueryClient {
|
|
|
100
66
|
}
|
|
101
67
|
|
|
102
68
|
return result?.data
|
|
103
|
-
|
|
104
69
|
}
|
|
105
70
|
|
|
106
|
-
queryProxy<T>(key:
|
|
71
|
+
queryProxy<T>(key: QueryKey) {
|
|
107
72
|
const getClient = () => this
|
|
108
73
|
|
|
109
74
|
return new Proxy<EnhancedQuery<T>>({} as EnhancedQuery<T>, {
|
|
@@ -133,15 +98,16 @@ export class CodeleapQueryClient {
|
|
|
133
98
|
}
|
|
134
99
|
|
|
135
100
|
switch (p) {
|
|
136
|
-
|
|
137
101
|
case 'waitForRefresh':
|
|
138
102
|
return () => {
|
|
139
103
|
return client.waitForRefresh<T>(key)
|
|
140
104
|
}
|
|
105
|
+
|
|
141
106
|
case 'listen':
|
|
142
|
-
return (callback: (e:
|
|
107
|
+
return (callback: (e: QueryCacheNotifyEvent) => void) => {
|
|
143
108
|
return client.listenToQuery(key, callback)
|
|
144
109
|
}
|
|
110
|
+
|
|
145
111
|
case 'ensureData':
|
|
146
112
|
return (options) => {
|
|
147
113
|
return client.client.ensureQueryData<T>({
|
|
@@ -149,6 +115,7 @@ export class CodeleapQueryClient {
|
|
|
149
115
|
...options
|
|
150
116
|
})
|
|
151
117
|
}
|
|
118
|
+
|
|
152
119
|
case 'refresh':
|
|
153
120
|
return async () => {
|
|
154
121
|
client.client.refetchQueries({
|
|
@@ -158,19 +125,20 @@ export class CodeleapQueryClient {
|
|
|
158
125
|
const newQuery = await client.waitForRefresh<T>(key)
|
|
159
126
|
return newQuery.state.data
|
|
160
127
|
}
|
|
128
|
+
|
|
161
129
|
case 'poll':
|
|
162
130
|
return (options: PollQueryOptions<T, any>) => {
|
|
163
131
|
return client.pollQuery(key, options)
|
|
164
132
|
}
|
|
133
|
+
|
|
165
134
|
default:
|
|
166
135
|
return Reflect.get(query, p, receiver)
|
|
167
136
|
}
|
|
168
137
|
},
|
|
169
|
-
|
|
170
138
|
})
|
|
171
139
|
}
|
|
172
140
|
|
|
173
|
-
waitForRefresh<T>(key:
|
|
141
|
+
waitForRefresh<T>(key: QueryKey) {
|
|
174
142
|
const initialQuery = this.client.getQueryCache().find({ exact: true, queryKey: key })
|
|
175
143
|
|
|
176
144
|
if (!initialQuery) {
|
|
@@ -180,7 +148,7 @@ export class CodeleapQueryClient {
|
|
|
180
148
|
const updateTime = initialQuery.state.dataUpdatedAt
|
|
181
149
|
const errorTime = initialQuery.state.errorUpdatedAt
|
|
182
150
|
|
|
183
|
-
return new Promise<
|
|
151
|
+
return new Promise<Query<T>>((resolve, reject) => {
|
|
184
152
|
const removeListener = this.listenToQuery(key, (e) => {
|
|
185
153
|
const query = e.query
|
|
186
154
|
|
|
@@ -207,21 +175,19 @@ export class CodeleapQueryClient {
|
|
|
207
175
|
|
|
208
176
|
}
|
|
209
177
|
|
|
210
|
-
queryKey<Data>(k:
|
|
211
|
-
|
|
212
|
-
if(options){
|
|
213
|
-
|
|
178
|
+
queryKey<Data>(k: QueryKey, options?: QueryOptions<Data>) {
|
|
179
|
+
if (options) {
|
|
214
180
|
this.client.setQueryDefaults(k, options)
|
|
215
181
|
|
|
216
182
|
const cache = this.client.getQueryCache()
|
|
217
|
-
|
|
218
|
-
const q = new
|
|
219
|
-
|
|
183
|
+
|
|
184
|
+
const q = new Query({
|
|
185
|
+
client: this.client,
|
|
220
186
|
queryKey: k,
|
|
221
|
-
queryHash:
|
|
187
|
+
queryHash: hashKey(k),
|
|
222
188
|
...options,
|
|
223
189
|
})
|
|
224
|
-
|
|
190
|
+
|
|
225
191
|
cache.add(q)
|
|
226
192
|
}
|
|
227
193
|
|
|
@@ -229,12 +195,11 @@ export class CodeleapQueryClient {
|
|
|
229
195
|
}
|
|
230
196
|
|
|
231
197
|
dynamicQueryKey<Data, BuilderArgs extends any[] = any[]>(k: QueryKeyBuilder<BuilderArgs>) {
|
|
232
|
-
|
|
233
198
|
const getClient = () => this
|
|
234
199
|
|
|
235
200
|
return new Proxy<DynamicEnhancedQuery<Data, BuilderArgs>>({} as DynamicEnhancedQuery<Data, BuilderArgs>, {
|
|
236
201
|
get(target, p, receiver) {
|
|
237
|
-
return (...params:BuilderArgs) => {
|
|
202
|
+
return (...params: BuilderArgs) => {
|
|
238
203
|
const key = k(...params)
|
|
239
204
|
|
|
240
205
|
const proxy = getClient().queryProxy<Data>(key)
|
|
@@ -244,17 +209,4 @@ export class CodeleapQueryClient {
|
|
|
244
209
|
},
|
|
245
210
|
})
|
|
246
211
|
}
|
|
247
|
-
|
|
248
|
-
queryManager<T extends QueryManagerItem, Args>(name:string, options: Partial<QueryManagerOptions<T, Args>>) {
|
|
249
|
-
// @ts-expect-error
|
|
250
|
-
const m = new QueryManager<T, Args>({
|
|
251
|
-
name,
|
|
252
|
-
queryClient: this.client,
|
|
253
|
-
...options,
|
|
254
|
-
|
|
255
|
-
})
|
|
256
|
-
|
|
257
|
-
return m
|
|
258
|
-
}
|
|
259
|
-
|
|
260
212
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
QueryKey,
|
|
3
|
+
Query,
|
|
4
|
+
QueryCacheNotifyEvent,
|
|
5
|
+
EnsureQueryDataOptions,
|
|
6
|
+
} from '@tanstack/react-query'
|
|
7
|
+
|
|
8
|
+
export type QueryKeyBuilder<Args extends any[] = any[]> = (...args: Args) => QueryKey
|
|
9
|
+
|
|
10
|
+
export type PollingResult<T> = {
|
|
11
|
+
stop: boolean
|
|
12
|
+
data: T
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type PollingCallback<T, R> = (query: Query<T>, count: number, prev?: R) => Promise<PollingResult<R>>
|
|
16
|
+
|
|
17
|
+
export type PollQueryOptions<T, R> = {
|
|
18
|
+
interval: number
|
|
19
|
+
callback: PollingCallback<T, R>
|
|
20
|
+
leading?: boolean
|
|
21
|
+
initialData?: R
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface EnhancedQuery<T> extends Query<T> {
|
|
25
|
+
waitForRefresh(): Promise<Query<T>>
|
|
26
|
+
listen(callback: (e: QueryCacheNotifyEvent) => void): () => void
|
|
27
|
+
refresh(): Promise<T>
|
|
28
|
+
poll<R>(
|
|
29
|
+
options: PollQueryOptions<T, R>
|
|
30
|
+
): Promise<R>
|
|
31
|
+
getData(): T
|
|
32
|
+
ensureData(options?: Partial<EnsureQueryDataOptions<T, Error, T, QueryKey, never>>): Promise<T>
|
|
33
|
+
key: QueryKey
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type DynamicEnhancedQuery<T, BuilderArgs extends any[]> = {
|
|
37
|
+
[P in keyof EnhancedQuery<T>]: (...args: BuilderArgs) => EnhancedQuery<T>[P]
|
|
38
|
+
}
|