@codeleap/query 5.8.3 → 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,119 @@
1
+ import { afterEach, beforeEach } from 'bun:test'
2
+ import { QueryClient } from '@tanstack/react-query'
3
+ import { cleanup } from '@testing-library/react'
4
+
5
+ export interface TestUser {
6
+ id: string
7
+ name: string
8
+ email: string
9
+ status: 'active' | 'inactive'
10
+ createdAt: string
11
+ }
12
+
13
+ export interface TestUserFilters {
14
+ status?: 'active' | 'inactive'
15
+ search?: string
16
+ }
17
+
18
+ export const createTestQueryClient = () => {
19
+ return new QueryClient({
20
+ defaultOptions: {
21
+ queries: {
22
+ retry: false,
23
+ gcTime: 0,
24
+ },
25
+ mutations: {
26
+ retry: false,
27
+ },
28
+ },
29
+ })
30
+ }
31
+
32
+ export const createMockUser = (overrides: Partial<TestUser> = {}): TestUser => ({
33
+ id: `user-${Date.now()}-${Math.random()}`,
34
+ name: 'John Doe',
35
+ email: 'john@example.com',
36
+ status: 'active',
37
+ createdAt: new Date().toISOString(),
38
+ ...overrides,
39
+ })
40
+
41
+ export const createMockUsers = (count: number): TestUser[] => {
42
+ return Array.from({ length: count }, (_, i) => createMockUser({
43
+ id: `user-${i + 1}`,
44
+ name: `User ${i + 1}`,
45
+ email: `user${i + 1}@example.com`,
46
+ }))
47
+ }
48
+
49
+ export const createMockApiFunctions = () => {
50
+ const users: TestUser[] = []
51
+
52
+ return {
53
+ listFn: async (limit: number, offset: number, filters?: TestUserFilters) => {
54
+ let filteredUsers = [...users]
55
+
56
+ if (filters?.status) {
57
+ filteredUsers = filteredUsers.filter(user => user.status === filters.status)
58
+ }
59
+
60
+ if (filters?.search) {
61
+ filteredUsers = filteredUsers.filter(user =>
62
+ user.name.toLowerCase().includes(filters.search!.toLowerCase()) ||
63
+ user.email.toLowerCase().includes(filters.search!.toLowerCase())
64
+ )
65
+ }
66
+
67
+ return filteredUsers.slice(offset, offset + limit)
68
+ },
69
+
70
+ retrieveFn: async (id: string) => {
71
+ const user = users.find(u => u.id === id)
72
+ if (!user) throw new Error('User not found')
73
+ return user
74
+ },
75
+
76
+ createFn: async (data: Partial<TestUser>) => {
77
+ const newUser: TestUser = {
78
+ id: `user-${users?.length + 1}`,
79
+ name: data.name || 'New User',
80
+ email: data.email || 'new@example.com',
81
+ status: data.status || 'active',
82
+ createdAt: new Date().toISOString(),
83
+ }
84
+ users.push(newUser)
85
+ return newUser
86
+ },
87
+
88
+ updateFn: async (data: Partial<TestUser>) => {
89
+ const userIndex = users.findIndex(u => u.id === data.id)
90
+ if (userIndex === -1) throw new Error('User not found')
91
+
92
+ users[userIndex] = { ...users[userIndex], ...data }
93
+ return users[userIndex]
94
+ },
95
+
96
+ deleteFn: async (id: string) => {
97
+ const userIndex = users.findIndex(u => u.id === id)
98
+ if (userIndex === -1) throw new Error('User not found')
99
+
100
+ users.splice(userIndex, 1)
101
+ return id
102
+ },
103
+
104
+ getUsersArray: () => [...users],
105
+ addUser: (user: TestUser) => users.push(user),
106
+ clearUsers: () => users.splice(0, users.length),
107
+ setUsers: (newUsers: TestUser[]) => {
108
+ users.splice(0, users.length, ...newUsers)
109
+ }
110
+ }
111
+ }
112
+
113
+ beforeEach(() => { })
114
+
115
+ afterEach(() => {
116
+ cleanup()
117
+ })
118
+
119
+ export const waitFor = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
@@ -0,0 +1,33 @@
1
+ import { UseInfiniteQueryResult, useQueryClient } from '@tanstack/react-query'
2
+
3
+ export type QueryItem = {
4
+ id: string | number
5
+ }
6
+
7
+ export type PageParam = number
8
+
9
+ export type ListPaginationResponse<T extends QueryItem> = T[]
10
+
11
+ export type QueryManagerOptions<T extends QueryItem, F> = {
12
+ name: string
13
+
14
+ queryClient: ReturnType<typeof useQueryClient>
15
+
16
+ listFn?: (limit: number, offset: number, filters: F) => Promise<ListPaginationResponse<T>>
17
+
18
+ listLimit?: number
19
+
20
+ useListEffect?: (listQuery: UseInfiniteQueryResult<{
21
+ pageParams: number[]
22
+ pages: ListPaginationResponse<T>[]
23
+ allItems: T[]
24
+ }, Error>) => void
25
+
26
+ retrieveFn?: (id: T['id']) => Promise<T>
27
+
28
+ createFn?: (data: Partial<T>) => Promise<T>
29
+
30
+ updateFn?: (data: Partial<T>) => Promise<T>
31
+
32
+ deleteFn?: (id: T['id']) => Promise<T['id']>
33
+ }
@@ -0,0 +1,16 @@
1
+ import { UseMutationOptions } from '@tanstack/react-query'
2
+ import { QueryItem } from './core'
3
+ import { RemovedItemMap } from './utility'
4
+
5
+ export type CreateMutationCtx = {
6
+ tempId?: QueryItem['id']
7
+ }
8
+
9
+ export type CreateMutationOptions<T extends QueryItem, F> = Omit<
10
+ UseMutationOptions<T, Error, Partial<T>, CreateMutationCtx>,
11
+ 'mutationKey' | 'mutationFn'
12
+ > & {
13
+ optimistic?: boolean
14
+ listFilters?: F
15
+ appendTo?: RemovedItemMap | 'start' | 'end'
16
+ }
@@ -0,0 +1,15 @@
1
+ import { UseMutationOptions } from '@tanstack/react-query'
2
+ import { QueryItem } from './core'
3
+ import { RemovedItemMap } from './utility'
4
+
5
+ export type DeleteMutationCtx<T> = {
6
+ previousItem: T | undefined
7
+ removedAt: RemovedItemMap
8
+ }
9
+
10
+ export type DeleteMutationOptions<T extends QueryItem, F> = Omit<
11
+ UseMutationOptions<unknown, Error, T['id'], DeleteMutationCtx<T>>,
12
+ 'mutationKey' | 'mutationFn'
13
+ > & {
14
+ optimistic?: boolean
15
+ }
@@ -0,0 +1,7 @@
1
+ export * from './core'
2
+ export * from './list'
3
+ export * from './utility'
4
+ export * from './retrieve'
5
+ export * from './create'
6
+ export * from './update'
7
+ export * from './delete'
@@ -0,0 +1,24 @@
1
+ import { QueryKey, UseInfiniteQueryOptions } from '@tanstack/react-query'
2
+ import { ListPaginationResponse, PageParam, QueryItem } from './core'
3
+
4
+ export type ListSelector<T extends QueryItem> = {
5
+ pageParams: PageParam[]
6
+ pages: ListPaginationResponse<T>[]
7
+ allItems: T[]
8
+ }
9
+
10
+ type InfiniteQueryOptions<T extends QueryItem> = UseInfiniteQueryOptions<
11
+ ListPaginationResponse<T>,
12
+ Error,
13
+ ListSelector<T>,
14
+ QueryKey,
15
+ PageParam
16
+ >
17
+
18
+ export type ListQueryOptions<T extends QueryItem, F> = Omit<
19
+ InfiniteQueryOptions<T>,
20
+ 'queryKey' | 'queryFn' | 'initialPageParam' | 'getNextPageParam' | 'getPreviousPageParam' | 'select'
21
+ > & {
22
+ limit?: number
23
+ filters?: F
24
+ }
@@ -0,0 +1,7 @@
1
+ import { QueryKey, UndefinedInitialDataOptions } from '@tanstack/react-query'
2
+ import { QueryItem } from './core'
3
+
4
+ export type RetrieveQueryOptions<T extends QueryItem> = Omit<
5
+ UndefinedInitialDataOptions<T, Error, T, QueryKey>,
6
+ 'queryKey' | 'queryFn'
7
+ >
@@ -0,0 +1,14 @@
1
+ import { UseMutationOptions } from '@tanstack/react-query'
2
+ import { QueryItem } from './core'
3
+
4
+ export type UpdateMutationCtx<T> = {
5
+ previousItem: T | undefined
6
+ optimisticItem: T | undefined
7
+ }
8
+
9
+ export type UpdateMutationOptions<T extends QueryItem, F> = Omit<
10
+ UseMutationOptions<T, Error, Partial<T>, UpdateMutationCtx<T>>,
11
+ 'mutationKey' | 'mutationFn'
12
+ > & {
13
+ optimistic?: boolean
14
+ }
@@ -0,0 +1,22 @@
1
+ import { QueryItem } from './core';
2
+ import { QueryKey, useQueryClient } from '@tanstack/react-query'
3
+
4
+ export type QueryClient = ReturnType<typeof useQueryClient>
5
+
6
+ export type WithTempId<T extends QueryItem> = T & {
7
+ tempId?: QueryItem['id']
8
+ }
9
+
10
+ export type ItemPosition = {
11
+ pageIndex: number
12
+ itemIndex: number
13
+ }
14
+
15
+ export type RemovedItemMap = [QueryKey, ItemPosition][]
16
+
17
+ export type PaginationResponse<T extends QueryItem> = {
18
+ count: number
19
+ next: string
20
+ previous: string
21
+ results: T[]
22
+ }
@@ -0,0 +1 @@
1
+ export * from './misc'
@@ -0,0 +1,43 @@
1
+ function uuidV4() {
2
+ function randomHex() {
3
+ return Math.floor(Math.random() * 16).toString(16);
4
+ }
5
+
6
+ let uuid = ''
7
+
8
+ for (let i = 0; i < 8; i++) {
9
+ uuid += randomHex()
10
+ }
11
+
12
+ uuid += '-'
13
+
14
+ for (let i = 0; i < 4; i++) {
15
+ uuid += randomHex()
16
+ }
17
+
18
+ uuid += '-'
19
+
20
+ uuid += '4';
21
+ for (let i = 0; i < 3; i++) {
22
+ uuid += randomHex()
23
+ }
24
+ uuid += '-'
25
+
26
+ uuid += ['8', '9', 'a', 'b'][Math.floor(Math.random() * 4)];
27
+ for (let i = 0; i < 3; i++) {
28
+ uuid += randomHex()
29
+ }
30
+ uuid += '-'
31
+
32
+ for (let i = 0; i < 12; i++) {
33
+ uuid += randomHex()
34
+ }
35
+
36
+ return uuid
37
+ }
38
+
39
+ export const tempIdPrefix = 'temp-id'
40
+
41
+ export const generateTempId = () => {
42
+ return tempIdPrefix + '-' + uuidV4()
43
+ }