@codeleap/query 5.8.2 → 5.8.4

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.
@@ -0,0 +1,351 @@
1
+ import { useMutation, useQuery, UseQueryOptions, UseMutationOptions, QueryKey, FetchQueryOptions } from '@tanstack/react-query'
2
+ import { QueryOperationsOptions, MutationFn, QueryFn, InferMutationParams, InferMutationReturn, InferQueryParams, InferQueryReturn } from './types'
3
+
4
+ /**
5
+ * Builder class for creating type-safe query and mutation operations
6
+ * @template TMutations - Record type containing all registered mutation functions
7
+ * @template TQueries - Record type containing all registered query functions
8
+ *
9
+ * @description
10
+ * QueryOperations provides a fluent interface for building collections of queries and mutations
11
+ * with full type safety. It acts as a centralized registry for all data operations and provides
12
+ * corresponding React hooks that are automatically typed based on the registered functions.
13
+ *
14
+ * Key features:
15
+ * - Fluent builder pattern for registering operations
16
+ * - Automatic type inference for parameters and return types
17
+ * - Type-safe React hooks generation
18
+ * - Immutable operation registration (returns new instances)
19
+ */
20
+ export class QueryOperations<
21
+ TMutations,
22
+ TQueries,
23
+ > {
24
+ /**
25
+ * Creates a new QueryOperations instance
26
+ * @param _options - Configuration options including QueryClient
27
+ * @param _mutations - Record of registered mutation functions (internal)
28
+ * @param _queries - Record of registered query functions (internal)
29
+ */
30
+ constructor(
31
+ private _options: QueryOperationsOptions,
32
+ private _mutations: TMutations = {} as TMutations,
33
+ private _queries: TQueries = {} as TQueries
34
+ ) { }
35
+
36
+ /**
37
+ * Gets all registered mutation functions
38
+ * @returns Readonly record of mutation functions
39
+ */
40
+ get mutations(): Readonly<TMutations> {
41
+ return this._mutations
42
+ }
43
+
44
+ /**
45
+ * Gets all registered query functions
46
+ * @returns Readonly record of query functions
47
+ */
48
+ get queries(): Readonly<TQueries> {
49
+ return this._queries
50
+ }
51
+
52
+ /**
53
+ * Registers a new mutation function
54
+ * @template K - The name/key for the mutation
55
+ * @template T - The input data type for the mutation
56
+ * @template R - The return data type for the mutation
57
+ * @param name - Unique name identifier for the mutation
58
+ * @param fn - The mutation function that performs the operation
59
+ * @returns New QueryOperations instance with the mutation added
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const operations = createQueryOperations({ queryClient })
64
+ * .mutation('createUser', async (userData: CreateUserData) => {
65
+ * return api.post('/users', userData)
66
+ * })
67
+ * .mutation('updateUser', async (userData: UpdateUserData) => {
68
+ * return api.put(`/users/${userData.id}`, userData)
69
+ * })
70
+ * ```
71
+ */
72
+ mutation<K extends string, T = any, R = any>(
73
+ name: K,
74
+ fn: MutationFn<T, R>
75
+ ): QueryOperations<TMutations & Record<K, MutationFn<T, R>>, TQueries> {
76
+ return new QueryOperations(
77
+ this._options,
78
+ { ...this._mutations, [name]: fn } as TMutations & Record<K, MutationFn<T, R>>,
79
+ this._queries
80
+ )
81
+ }
82
+
83
+ /**
84
+ * Registers a new query function
85
+ * @template K - The name/key for the query
86
+ * @template T - The parameters type for the query
87
+ * @template R - The return data type for the query
88
+ * @param name - Unique name identifier for the query
89
+ * @param fn - The query function that fetches the data
90
+ * @returns New QueryOperations instance with the query added
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * const operations = createQueryOperations({ queryClient })
95
+ * .query('getUser', async (userId: string) => {
96
+ * return api.get(`/users/${userId}`)
97
+ * })
98
+ * .query('getUsers', async (filters?: UserFilters) => {
99
+ * return api.get('/users', { params: filters })
100
+ * })
101
+ * ```
102
+ */
103
+ query<K extends string, T = any, R = any>(
104
+ name: K,
105
+ fn: QueryFn<T, R>
106
+ ): QueryOperations<TMutations, TQueries & Record<K, QueryFn<T, R>>> {
107
+ return new QueryOperations(
108
+ this._options,
109
+ this._mutations,
110
+ { ...this._queries, [name]: fn } as TQueries & Record<K, QueryFn<T, R>>
111
+ )
112
+ }
113
+
114
+ /**
115
+ * React hook for executing mutations with full type safety
116
+ * @template K - The mutation key type
117
+ * @param mutationKey - The name of the registered mutation to use
118
+ * @param options - React Query mutation options (excluding mutationFn and mutationKey)
119
+ * @returns React Query mutation object with inferred types
120
+ *
121
+ * @description
122
+ * This hook automatically provides type-safe parameters and return types based on the
123
+ * registered mutation function. It handles error cases and provides proper TypeScript
124
+ * inference for the mutation data and variables.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * const createUserMutation = operations.useMutation('createUser', {
129
+ * onSuccess: (user) => {
130
+ * // 'user' is automatically typed as the return type of createUser
131
+ * console.log('Created user:', user.id)
132
+ * }
133
+ * })
134
+ *
135
+ * // Usage - parameters are type-checked
136
+ * createUserMutation.mutate({ name: 'John', email: 'john@example.com' })
137
+ * ```
138
+ */
139
+ useMutation<K extends keyof TMutations>(
140
+ mutationKey: K,
141
+ options?: Omit<
142
+ UseMutationOptions<
143
+ InferMutationReturn<TMutations[K]>,
144
+ Error,
145
+ InferMutationParams<TMutations[K]>
146
+ >,
147
+ 'mutationFn'
148
+ >
149
+ ) {
150
+ const mutationFn = this._mutations[mutationKey] as MutationFn
151
+
152
+ type TData = InferMutationReturn<TMutations[K]>
153
+ type TVariables = InferMutationParams<TMutations[K]>
154
+
155
+ return useMutation<TData, Error, TVariables>({
156
+ mutationKey: this.getMutationKey(mutationKey),
157
+ mutationFn: async (data: TVariables): Promise<TData> => {
158
+ if (!mutationFn) {
159
+ throw new Error(`Mutation "${String(mutationKey)}" not found`)
160
+ }
161
+ return mutationFn(data) as Promise<TData>
162
+ },
163
+ ...options
164
+ })
165
+ }
166
+
167
+ /**
168
+ * React hook for executing queries with full type safety
169
+ * @template K - The query key type
170
+ * @param queryKey - The name of the registered query to use
171
+ * @param params - Parameters to pass to the query function (optional if query doesn't require params)
172
+ * @param options - React Query options (excluding queryKey and queryFn)
173
+ * @returns React Query query object with inferred types
174
+ *
175
+ * @description
176
+ * This hook automatically provides type-safe parameters and return types based on the
177
+ * registered query function. It generates appropriate query keys and handles parameter
178
+ * validation.
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * // Query with parameters
183
+ * const userQuery = operations.useQuery('getUser', 'user-123', {
184
+ * enabled: !!userId
185
+ * })
186
+ *
187
+ * // Query without parameters
188
+ * const usersQuery = operations.useQuery('getUsers', undefined, {
189
+ * refetchInterval: 30000
190
+ * })
191
+ *
192
+ * // Query with optional parameters
193
+ * const filteredUsersQuery = operations.useQuery('getUsers', { status: 'active' })
194
+ * ```
195
+ */
196
+ useQuery<K extends keyof TQueries, T = InferQueryReturn<TQueries[K]>>(
197
+ queryKey: K,
198
+ params?: InferQueryParams<TQueries[K]>,
199
+ options?: Omit<
200
+ Partial<UseQueryOptions<
201
+ InferQueryReturn<TQueries[K]>,
202
+ Error,
203
+ T,
204
+ QueryKey
205
+ >>,
206
+ 'queryFn'
207
+ >
208
+ ) {
209
+ const queryFn = this._queries[queryKey] as QueryFn
210
+
211
+ type TData = InferQueryReturn<TQueries[K]>
212
+
213
+ return useQuery<TData, Error, T, QueryKey>({
214
+ queryKey: this.getQueryKey(queryKey, params),
215
+ queryFn: async (): Promise<TData> => {
216
+ if (!queryFn) {
217
+ throw new Error(`Query "${String(queryKey)}" not found`)
218
+ }
219
+ return queryFn(params as any) as Promise<TData>
220
+ },
221
+ ...options
222
+ } as any)
223
+ }
224
+
225
+ /**
226
+ * Generates a properly typed query key for React Query
227
+ * @template K - The query key type
228
+ * @param queryKey - The name of the query
229
+ * @param params - Optional parameters for the query
230
+ * @returns Query key array, with params included only when necessary
231
+ *
232
+ * @description
233
+ * This method creates React Query compatible keys that include parameters when present.
234
+ * The return type is conditionally typed based on whether the query requires parameters.
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * // Returns ['getUser', 'user-123']
239
+ * const keyWithParams = operations.getQueryKey('getUser', 'user-123')
240
+ *
241
+ * // Returns ['getUsers']
242
+ * const keyWithoutParams = operations.getQueryKey('getUsers')
243
+ * ```
244
+ */
245
+ getQueryKey<K extends keyof TQueries>(
246
+ queryKey: K,
247
+ params?: InferQueryParams<TQueries[K]>
248
+ ): QueryKey {
249
+ return (params !== undefined ? [queryKey, params] : [queryKey]) as QueryKey
250
+ }
251
+
252
+ /**
253
+ * Generates a mutation key for React Query
254
+ * @template K - The mutation key type
255
+ * @param mutationKey - The name of the mutation
256
+ * @returns Mutation key array containing only the mutation name
257
+ *
258
+ * @example
259
+ * ```typescript
260
+ * // Returns ['createUser']
261
+ * const mutationKey = operations.getMutationKey('createUser')
262
+ * ```
263
+ */
264
+ getMutationKey<K extends keyof TMutations>(mutationKey: K): QueryKey {
265
+ return [mutationKey] as QueryKey
266
+ }
267
+
268
+ /**
269
+ * Prefetches a query to populate the cache ahead of time
270
+ * @template K - The query key type
271
+ * @param queryKey - The name of the registered query to prefetch
272
+ * @param params - Parameters to pass to the query function (optional if query doesn't require params)
273
+ * @param options - React Query prefetch options
274
+ * @returns Promise that resolves when the prefetch is complete
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * // Prefetch user data when hovering over a user link
279
+ * const handleUserHover = async (userId: string) => {
280
+ * await operations.prefetchQuery('getUser', userId, {
281
+ * staleTime: 5 * 60 * 1000 // 5 minutes
282
+ * })
283
+ * }
284
+ *
285
+ * // Prefetch data on route change
286
+ * useEffect(() => {
287
+ * operations.prefetchQuery('getUsers', { status: 'active' })
288
+ * }, [])
289
+ * ```
290
+ */
291
+ prefetchQuery<K extends keyof TQueries, T = InferQueryReturn<TQueries[K]>>(
292
+ queryKey: K,
293
+ params?: InferQueryParams<TQueries[K]>,
294
+ options?: FetchQueryOptions<InferQueryReturn<TQueries[K]>, Error, T, QueryKey, never>
295
+ ) {
296
+ const prefetchQueryKey = this.getQueryKey(queryKey, params)
297
+
298
+ const queryFn = this._queries[queryKey] as QueryFn
299
+
300
+ return this._options.queryClient.prefetchQuery<InferQueryReturn<TQueries[K]>, Error, T, QueryKey>({
301
+ queryKey: prefetchQueryKey,
302
+ queryFn: queryFn,
303
+ ...options,
304
+ })
305
+ }
306
+
307
+ /**
308
+ * Retrieves cached query data if it exists
309
+ * @template K - The query key type
310
+ * @template T - The expected return type (defaults to inferred query return type)
311
+ * @param queryKey - The name of the registered query
312
+ * @param params - Parameters used when the query was cached (optional if query doesn't require params)
313
+ * @returns The cached data if it exists, undefined otherwise
314
+ *
315
+ * @example
316
+ * ```typescript
317
+ * // Get cached user data
318
+ * const cachedUser = operations.getQueryData('getUser', 'user-123')
319
+ * if (cachedUser) {
320
+ * console.log('User already in cache:', cachedUser.name)
321
+ * }
322
+ *
323
+ * // Check if users list is cached before showing loading state
324
+ * const cachedUsers = operations.getQueryData('getUsers')
325
+ * const showSkeleton = !cachedUsers
326
+ *
327
+ * // Access cached data in event handlers
328
+ * const handleUserAction = () => {
329
+ * const currentUser = operations.getQueryData('getCurrentUser')
330
+ * if (currentUser?.role === 'admin') {
331
+ * // Perform admin action
332
+ * }
333
+ * }
334
+ * ```
335
+ */
336
+ async getQueryData<K extends keyof TQueries, T = InferQueryReturn<TQueries[K]>>(
337
+ queryKey: K,
338
+ params?: InferQueryParams<TQueries[K]>,
339
+ options?: FetchQueryOptions<InferQueryReturn<TQueries[K]>, Error, T, QueryKey, never>,
340
+ ) {
341
+ const prefetchQueryKey = this.getQueryKey(queryKey, params)
342
+
343
+ const cachedData = this._options.queryClient.getQueryData<T, QueryKey>(prefetchQueryKey)
344
+
345
+ if (!cachedData) {
346
+ await this.prefetchQuery(queryKey, params, options)
347
+ }
348
+
349
+ return this._options.queryClient.getQueryData<T, QueryKey>(prefetchQueryKey)
350
+ }
351
+ }
@@ -0,0 +1,47 @@
1
+ import { QueryClient } from '../../types'
2
+
3
+ /**
4
+ * Configuration options for QueryOperations
5
+ */
6
+ export type QueryOperationsOptions = {
7
+ /** The React Query client instance */
8
+ queryClient: QueryClient
9
+ }
10
+
11
+ /**
12
+ * Generic mutation function type
13
+ * @template T - The input data type
14
+ * @template R - The return data type
15
+ */
16
+ export type MutationFn<T = any, R = any> = (data: T) => Promise<R> | R
17
+
18
+ /**
19
+ * Generic query function type
20
+ * @template T - The parameters type
21
+ * @template R - The return data type
22
+ */
23
+ export type QueryFn<T = any, R = any> = (params?: T) => Promise<R> | R
24
+
25
+ /**
26
+ * Utility type to infer mutation function parameters
27
+ * @template T - The mutation function type
28
+ */
29
+ export type InferMutationParams<T> = T extends MutationFn<infer P, any> ? P : never
30
+
31
+ /**
32
+ * Utility type to infer mutation function return type
33
+ * @template T - The mutation function type
34
+ */
35
+ export type InferMutationReturn<T> = T extends MutationFn<any, infer R> ? R : never
36
+
37
+ /**
38
+ * Utility type to infer query function parameters
39
+ * @template T - The query function type
40
+ */
41
+ export type InferQueryParams<T> = T extends QueryFn<infer P, any> ? P : never
42
+
43
+ /**
44
+ * Utility type to infer query function return type
45
+ * @template T - The query function type
46
+ */
47
+ export type InferQueryReturn<T> = T extends QueryFn<any, infer R> ? R : never
@@ -0,0 +1,5 @@
1
+ export * from './QueryClientEnhanced'
2
+ export * from './QueryKeys'
3
+ export * from './Mutations'
4
+ export * from './QueryManager'
5
+ export * from './QueryOperations'