query-harbor 0.0.4 → 0.0.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/README.md +349 -349
- package/dist/query-harbor.es.js +165 -211
- package/dist/query-harbor.umd.js +1 -1
- package/package.json +47 -47
package/README.md
CHANGED
|
@@ -1,350 +1,350 @@
|
|
|
1
|
-
# Query Harbor
|
|
2
|
-
|
|
3
|
-
A collection of custom React hooks built on top of TanStack Query (formerly React Query) for handling API requests, mutations, and cookie management. These hooks provide a standardized way to handle data fetching, caching, and state management in React applications.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [Installation](#installation)
|
|
8
|
-
- [Hooks Overview](#hooks-overview)
|
|
9
|
-
- [useGlobalQuery](#useglobalquery)
|
|
10
|
-
- [useGlobalMutation](#useglobalmutation)
|
|
11
|
-
- [useGlobalInfiniteQuery](#useglobalinfinitequery)
|
|
12
|
-
- [useCookie](#usecookie)
|
|
13
|
-
- [Usage Examples](#usage-examples)
|
|
14
|
-
- [API Reference](#api-reference)
|
|
15
|
-
|
|
16
|
-
## Installation
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
# Install required dependencies
|
|
20
|
-
npm install @tanstack/react-query react-cookie axios
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Hooks Overview
|
|
24
|
-
|
|
25
|
-
### useGlobalQuery
|
|
26
|
-
|
|
27
|
-
A custom hook for making standard API requests with built-in caching and state management.
|
|
28
|
-
|
|
29
|
-
#### Features
|
|
30
|
-
- Automatic authentication header handling
|
|
31
|
-
- Configurable cache and stale times
|
|
32
|
-
- Built-in error handling
|
|
33
|
-
- Query invalidation support
|
|
34
|
-
|
|
35
|
-
#### Basic Usage
|
|
36
|
-
|
|
37
|
-
```javascript
|
|
38
|
-
const {
|
|
39
|
-
queryData,
|
|
40
|
-
isLoading,
|
|
41
|
-
isError,
|
|
42
|
-
error,
|
|
43
|
-
refetchQuery
|
|
44
|
-
} = useGlobalQuery({
|
|
45
|
-
url: '/api/users',
|
|
46
|
-
queryKey: ['users'],
|
|
47
|
-
methodType: 'GET'
|
|
48
|
-
});
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### useGlobalMutation
|
|
52
|
-
|
|
53
|
-
A custom hook for handling data mutations (create, update, delete operations) with support for FormData.
|
|
54
|
-
|
|
55
|
-
#### Features
|
|
56
|
-
- FormData support with nested object handling
|
|
57
|
-
- Automatic query invalidation after successful mutation
|
|
58
|
-
- Priority data support
|
|
59
|
-
- Built-in error handling
|
|
60
|
-
|
|
61
|
-
#### Basic Usage
|
|
62
|
-
|
|
63
|
-
```javascript
|
|
64
|
-
const {
|
|
65
|
-
runMutation,
|
|
66
|
-
mutationLoading,
|
|
67
|
-
mutationData,
|
|
68
|
-
mutationError,
|
|
69
|
-
isMutationSucceeded
|
|
70
|
-
} = useGlobalMutation({
|
|
71
|
-
url: '/api/users',
|
|
72
|
-
queriesToInvalidate: ['users'],
|
|
73
|
-
methodType: 'POST',
|
|
74
|
-
data: userData
|
|
75
|
-
});
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
#### FormData Upload Examples
|
|
79
|
-
|
|
80
|
-
##### Basic FormData Upload
|
|
81
|
-
|
|
82
|
-
```javascript
|
|
83
|
-
const {
|
|
84
|
-
runMutation,
|
|
85
|
-
mutationLoading
|
|
86
|
-
} = useGlobalMutation({
|
|
87
|
-
url: '/api/upload',
|
|
88
|
-
queriesToInvalidate: ['files'],
|
|
89
|
-
methodType: 'POST',
|
|
90
|
-
isFormData: true,
|
|
91
|
-
data: {
|
|
92
|
-
file: fileObject,
|
|
93
|
-
title: 'My Document'
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
##### Complex FormData with Arrays
|
|
99
|
-
|
|
100
|
-
```javascript
|
|
101
|
-
// Without excludedIndexKeys
|
|
102
|
-
const data = {
|
|
103
|
-
files: [file1, file2],
|
|
104
|
-
metadata: {
|
|
105
|
-
titles: ['Doc 1', 'Doc 2']
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
// This will generate FormData with structure:
|
|
110
|
-
// files[0] = file1
|
|
111
|
-
// files[1] = file2
|
|
112
|
-
// metadata[titles][0] = Doc 1
|
|
113
|
-
// metadata[titles][1] = Doc 2
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
##### Using excludedIndexKeys
|
|
117
|
-
|
|
118
|
-
```javascript
|
|
119
|
-
const MultipleFileUpload = () => {
|
|
120
|
-
const {
|
|
121
|
-
runMutation,
|
|
122
|
-
mutationLoading
|
|
123
|
-
} = useGlobalMutation({
|
|
124
|
-
url: '/api/upload-multiple',
|
|
125
|
-
queriesToInvalidate: ['files'],
|
|
126
|
-
methodType: 'POST',
|
|
127
|
-
isFormData: true,
|
|
128
|
-
// Specify which keys should not include array indices
|
|
129
|
-
excludedIndexKeys: ['files', 'documents'],
|
|
130
|
-
data: {
|
|
131
|
-
files: [file1, file2, file3],
|
|
132
|
-
documents: [docFile1, docFile2],
|
|
133
|
-
metadata: {
|
|
134
|
-
titles: ['Doc 1', 'Doc 2', 'Doc 3'],
|
|
135
|
-
categories: ['Cat 1', 'Cat 2', 'Cat 3']
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
return (
|
|
141
|
-
<button onClick={() => runMutation()}>
|
|
142
|
-
Upload Files
|
|
143
|
-
</button>
|
|
144
|
-
);
|
|
145
|
-
};
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
The above example will generate FormData with the following structure:
|
|
149
|
-
```plaintext
|
|
150
|
-
// Keys with excludedIndexKeys:
|
|
151
|
-
files = file1
|
|
152
|
-
files = file2
|
|
153
|
-
files = file3
|
|
154
|
-
documents = docFile1
|
|
155
|
-
documents = docFile2
|
|
156
|
-
|
|
157
|
-
// Regular array keys (maintain indices):
|
|
158
|
-
metadata[titles][0] = Doc 1
|
|
159
|
-
metadata[titles][1] = Doc 2
|
|
160
|
-
metadata[titles][2] = Doc 3
|
|
161
|
-
metadata[categories][0] = Cat 1
|
|
162
|
-
metadata[categories][1] = Cat 2
|
|
163
|
-
metadata[categories][2] = Cat 3
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
##### Real-World Example: Multiple File Upload with Metadata
|
|
167
|
-
|
|
168
|
-
```javascript
|
|
169
|
-
const DocumentUploadForm = () => {
|
|
170
|
-
const [files, setFiles] = useState([]);
|
|
171
|
-
const [metadata, setMetadata] = useState({
|
|
172
|
-
department: 'HR',
|
|
173
|
-
tags: ['confidential', 'employee'],
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
const {
|
|
177
|
-
runMutation,
|
|
178
|
-
mutationLoading,
|
|
179
|
-
isMutationSucceeded
|
|
180
|
-
} = useGlobalMutation({
|
|
181
|
-
url: '/api/documents/upload',
|
|
182
|
-
queriesToInvalidate: ['documents'],
|
|
183
|
-
methodType: 'POST',
|
|
184
|
-
isFormData: true,
|
|
185
|
-
excludedIndexKeys: ['files'], // files will be sent without indices
|
|
186
|
-
data: {
|
|
187
|
-
files: files,
|
|
188
|
-
metadata: metadata,
|
|
189
|
-
timestamp: new Date().toISOString(),
|
|
190
|
-
user: {
|
|
191
|
-
id: currentUserId,
|
|
192
|
-
role: userRole
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
const handleFileChange = (e) => {
|
|
198
|
-
setFiles(Array.from(e.target.files));
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
const handleUpload = () => {
|
|
202
|
-
runMutation();
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
return (
|
|
206
|
-
<div>
|
|
207
|
-
<input
|
|
208
|
-
type="file"
|
|
209
|
-
multiple
|
|
210
|
-
onChange={handleFileChange}
|
|
211
|
-
/>
|
|
212
|
-
{mutationLoading ? (
|
|
213
|
-
<p>Uploading...</p>
|
|
214
|
-
) : (
|
|
215
|
-
<button onClick={handleUpload}>
|
|
216
|
-
Upload Documents
|
|
217
|
-
</button>
|
|
218
|
-
)}
|
|
219
|
-
{isMutationSucceeded && (
|
|
220
|
-
<p>Upload completed successfully!</p>
|
|
221
|
-
)}
|
|
222
|
-
</div>
|
|
223
|
-
);
|
|
224
|
-
};
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
This will generate FormData where:
|
|
228
|
-
- Multiple files are sent with the same key name ('files')
|
|
229
|
-
- Metadata is properly nested with array indices preserved
|
|
230
|
-
- Additional data is structured appropriately
|
|
231
|
-
|
|
232
|
-
The resulting FormData structure will be:
|
|
233
|
-
```plaintext
|
|
234
|
-
files = File1
|
|
235
|
-
files = File2
|
|
236
|
-
metadata[department] = HR
|
|
237
|
-
metadata[tags][0] = confidential
|
|
238
|
-
metadata[tags][1] = employee
|
|
239
|
-
timestamp = 2024-02-23T10:00:00.000Z
|
|
240
|
-
user[id] = 123
|
|
241
|
-
user[role] = admin
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
#### When to Use excludedIndexKeys
|
|
245
|
-
|
|
246
|
-
Use `excludedIndexKeys` when:
|
|
247
|
-
1. Working with file upload APIs that expect multiple files with the same key
|
|
248
|
-
2. Dealing with legacy APIs that don't support indexed form fields
|
|
249
|
-
3. Implementing multi-file upload where the server expects a flat structure
|
|
250
|
-
4. Handling file arrays where order doesn't matter
|
|
251
|
-
|
|
252
|
-
### useGlobalInfiniteQuery
|
|
253
|
-
|
|
254
|
-
A custom hook for handling infinite scroll or pagination scenarios.
|
|
255
|
-
|
|
256
|
-
#### Features
|
|
257
|
-
- Automatic pagination handling
|
|
258
|
-
- Built-in cache management
|
|
259
|
-
- Total count tracking
|
|
260
|
-
- Next page detection
|
|
261
|
-
|
|
262
|
-
#### Basic Usage
|
|
263
|
-
|
|
264
|
-
```javascript
|
|
265
|
-
const {
|
|
266
|
-
queryData,
|
|
267
|
-
isLoading,
|
|
268
|
-
isError,
|
|
269
|
-
error,
|
|
270
|
-
fetchNextPage,
|
|
271
|
-
hasNextPage,
|
|
272
|
-
totalCount
|
|
273
|
-
} = useGlobalInfiniteQuery({
|
|
274
|
-
url: '/api/posts',
|
|
275
|
-
queryKey: ['posts'],
|
|
276
|
-
methodType: 'GET',
|
|
277
|
-
data: { limit: 10 }
|
|
278
|
-
});
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### useCookie
|
|
282
|
-
|
|
283
|
-
A utility hook for managing cookies across the application.
|
|
284
|
-
|
|
285
|
-
#### Features
|
|
286
|
-
- Simple cookie management
|
|
287
|
-
- Type-safe cookie operations
|
|
288
|
-
- Easy integration with authentication
|
|
289
|
-
|
|
290
|
-
#### Basic Usage
|
|
291
|
-
|
|
292
|
-
```javascript
|
|
293
|
-
const { cookie, setCookie, removeCookie } = useCookie({
|
|
294
|
-
cookieName: 'accessToken'
|
|
295
|
-
});
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
## API Reference
|
|
299
|
-
|
|
300
|
-
### useGlobalQuery Options
|
|
301
|
-
|
|
302
|
-
| Option | Type | Default | Description |
|
|
303
|
-
|--------|------|---------|-------------|
|
|
304
|
-
| url | string | required | API endpoint URL |
|
|
305
|
-
| queryKey | string[] | required | Unique key for caching |
|
|
306
|
-
| methodType | string | required | HTTP method (GET, POST, etc.) |
|
|
307
|
-
| data | object | optional | Request payload |
|
|
308
|
-
| enabled | boolean | true | Whether to enable the query |
|
|
309
|
-
| cacheTime | number | 300000 | Cache duration in ms |
|
|
310
|
-
| staleTime | number | 300000 | Stale data duration in ms |
|
|
311
|
-
|
|
312
|
-
### useGlobalMutation Options
|
|
313
|
-
|
|
314
|
-
| Option | Type | Default | Description |
|
|
315
|
-
|--------|------|---------|-------------|
|
|
316
|
-
| url | string | required | API endpoint URL |
|
|
317
|
-
| queriesToInvalidate | string[] | required | Queries to refresh after mutation |
|
|
318
|
-
| methodType | string | required | HTTP method (POST, PUT, DELETE) |
|
|
319
|
-
| data | object | optional | Default mutation data |
|
|
320
|
-
| isFormData | boolean | false | Whether to handle as FormData |
|
|
321
|
-
| closePopup | function | optional | Callback after success |
|
|
322
|
-
| excludedIndexKeys | string[] | optional | Keys to exclude from FormData indices |
|
|
323
|
-
|
|
324
|
-
### useGlobalInfiniteQuery Options
|
|
325
|
-
|
|
326
|
-
| Option | Type | Default | Description |
|
|
327
|
-
|--------|------|---------|-------------|
|
|
328
|
-
| url | string | required | API endpoint URL |
|
|
329
|
-
| queryKey | string[] | required | Unique key for caching |
|
|
330
|
-
| methodType | string | required | HTTP method |
|
|
331
|
-
| data | object | optional | Additional query parameters |
|
|
332
|
-
| enabled | boolean | true | Whether to enable the query |
|
|
333
|
-
| cacheTime | number | 300000 | Cache duration in ms |
|
|
334
|
-
| staleTime | number | 300000 | Stale data duration in ms |
|
|
335
|
-
|
|
336
|
-
## Best Practices
|
|
337
|
-
|
|
338
|
-
1. Always provide unique and descriptive query keys
|
|
339
|
-
2. Set appropriate cache and stale times based on data volatility
|
|
340
|
-
3. Handle loading and error states in your UI
|
|
341
|
-
4. Use the refetchQuery function for manual data refresh
|
|
342
|
-
5. Implement proper error boundaries in your application
|
|
343
|
-
|
|
344
|
-
## Contributing
|
|
345
|
-
|
|
346
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
347
|
-
|
|
348
|
-
## License
|
|
349
|
-
|
|
1
|
+
# Query Harbor
|
|
2
|
+
|
|
3
|
+
A collection of custom React hooks built on top of TanStack Query (formerly React Query) for handling API requests, mutations, and cookie management. These hooks provide a standardized way to handle data fetching, caching, and state management in React applications.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Hooks Overview](#hooks-overview)
|
|
9
|
+
- [useGlobalQuery](#useglobalquery)
|
|
10
|
+
- [useGlobalMutation](#useglobalmutation)
|
|
11
|
+
- [useGlobalInfiniteQuery](#useglobalinfinitequery)
|
|
12
|
+
- [useCookie](#usecookie)
|
|
13
|
+
- [Usage Examples](#usage-examples)
|
|
14
|
+
- [API Reference](#api-reference)
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Install required dependencies
|
|
20
|
+
npm install @tanstack/react-query react-cookie axios
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Hooks Overview
|
|
24
|
+
|
|
25
|
+
### useGlobalQuery
|
|
26
|
+
|
|
27
|
+
A custom hook for making standard API requests with built-in caching and state management.
|
|
28
|
+
|
|
29
|
+
#### Features
|
|
30
|
+
- Automatic authentication header handling
|
|
31
|
+
- Configurable cache and stale times
|
|
32
|
+
- Built-in error handling
|
|
33
|
+
- Query invalidation support
|
|
34
|
+
|
|
35
|
+
#### Basic Usage
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
const {
|
|
39
|
+
queryData,
|
|
40
|
+
isLoading,
|
|
41
|
+
isError,
|
|
42
|
+
error,
|
|
43
|
+
refetchQuery
|
|
44
|
+
} = useGlobalQuery({
|
|
45
|
+
url: '/api/users',
|
|
46
|
+
queryKey: ['users'],
|
|
47
|
+
methodType: 'GET'
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### useGlobalMutation
|
|
52
|
+
|
|
53
|
+
A custom hook for handling data mutations (create, update, delete operations) with support for FormData.
|
|
54
|
+
|
|
55
|
+
#### Features
|
|
56
|
+
- FormData support with nested object handling
|
|
57
|
+
- Automatic query invalidation after successful mutation
|
|
58
|
+
- Priority data support
|
|
59
|
+
- Built-in error handling
|
|
60
|
+
|
|
61
|
+
#### Basic Usage
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
const {
|
|
65
|
+
runMutation,
|
|
66
|
+
mutationLoading,
|
|
67
|
+
mutationData,
|
|
68
|
+
mutationError,
|
|
69
|
+
isMutationSucceeded
|
|
70
|
+
} = useGlobalMutation({
|
|
71
|
+
url: '/api/users',
|
|
72
|
+
queriesToInvalidate: ['users'],
|
|
73
|
+
methodType: 'POST',
|
|
74
|
+
data: userData
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### FormData Upload Examples
|
|
79
|
+
|
|
80
|
+
##### Basic FormData Upload
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
const {
|
|
84
|
+
runMutation,
|
|
85
|
+
mutationLoading
|
|
86
|
+
} = useGlobalMutation({
|
|
87
|
+
url: '/api/upload',
|
|
88
|
+
queriesToInvalidate: ['files'],
|
|
89
|
+
methodType: 'POST',
|
|
90
|
+
isFormData: true,
|
|
91
|
+
data: {
|
|
92
|
+
file: fileObject,
|
|
93
|
+
title: 'My Document'
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
##### Complex FormData with Arrays
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
// Without excludedIndexKeys
|
|
102
|
+
const data = {
|
|
103
|
+
files: [file1, file2],
|
|
104
|
+
metadata: {
|
|
105
|
+
titles: ['Doc 1', 'Doc 2']
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// This will generate FormData with structure:
|
|
110
|
+
// files[0] = file1
|
|
111
|
+
// files[1] = file2
|
|
112
|
+
// metadata[titles][0] = Doc 1
|
|
113
|
+
// metadata[titles][1] = Doc 2
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
##### Using excludedIndexKeys
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
const MultipleFileUpload = () => {
|
|
120
|
+
const {
|
|
121
|
+
runMutation,
|
|
122
|
+
mutationLoading
|
|
123
|
+
} = useGlobalMutation({
|
|
124
|
+
url: '/api/upload-multiple',
|
|
125
|
+
queriesToInvalidate: ['files'],
|
|
126
|
+
methodType: 'POST',
|
|
127
|
+
isFormData: true,
|
|
128
|
+
// Specify which keys should not include array indices
|
|
129
|
+
excludedIndexKeys: ['files', 'documents'],
|
|
130
|
+
data: {
|
|
131
|
+
files: [file1, file2, file3],
|
|
132
|
+
documents: [docFile1, docFile2],
|
|
133
|
+
metadata: {
|
|
134
|
+
titles: ['Doc 1', 'Doc 2', 'Doc 3'],
|
|
135
|
+
categories: ['Cat 1', 'Cat 2', 'Cat 3']
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<button onClick={() => runMutation()}>
|
|
142
|
+
Upload Files
|
|
143
|
+
</button>
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The above example will generate FormData with the following structure:
|
|
149
|
+
```plaintext
|
|
150
|
+
// Keys with excludedIndexKeys:
|
|
151
|
+
files = file1
|
|
152
|
+
files = file2
|
|
153
|
+
files = file3
|
|
154
|
+
documents = docFile1
|
|
155
|
+
documents = docFile2
|
|
156
|
+
|
|
157
|
+
// Regular array keys (maintain indices):
|
|
158
|
+
metadata[titles][0] = Doc 1
|
|
159
|
+
metadata[titles][1] = Doc 2
|
|
160
|
+
metadata[titles][2] = Doc 3
|
|
161
|
+
metadata[categories][0] = Cat 1
|
|
162
|
+
metadata[categories][1] = Cat 2
|
|
163
|
+
metadata[categories][2] = Cat 3
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
##### Real-World Example: Multiple File Upload with Metadata
|
|
167
|
+
|
|
168
|
+
```javascript
|
|
169
|
+
const DocumentUploadForm = () => {
|
|
170
|
+
const [files, setFiles] = useState([]);
|
|
171
|
+
const [metadata, setMetadata] = useState({
|
|
172
|
+
department: 'HR',
|
|
173
|
+
tags: ['confidential', 'employee'],
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const {
|
|
177
|
+
runMutation,
|
|
178
|
+
mutationLoading,
|
|
179
|
+
isMutationSucceeded
|
|
180
|
+
} = useGlobalMutation({
|
|
181
|
+
url: '/api/documents/upload',
|
|
182
|
+
queriesToInvalidate: ['documents'],
|
|
183
|
+
methodType: 'POST',
|
|
184
|
+
isFormData: true,
|
|
185
|
+
excludedIndexKeys: ['files'], // files will be sent without indices
|
|
186
|
+
data: {
|
|
187
|
+
files: files,
|
|
188
|
+
metadata: metadata,
|
|
189
|
+
timestamp: new Date().toISOString(),
|
|
190
|
+
user: {
|
|
191
|
+
id: currentUserId,
|
|
192
|
+
role: userRole
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const handleFileChange = (e) => {
|
|
198
|
+
setFiles(Array.from(e.target.files));
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const handleUpload = () => {
|
|
202
|
+
runMutation();
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<div>
|
|
207
|
+
<input
|
|
208
|
+
type="file"
|
|
209
|
+
multiple
|
|
210
|
+
onChange={handleFileChange}
|
|
211
|
+
/>
|
|
212
|
+
{mutationLoading ? (
|
|
213
|
+
<p>Uploading...</p>
|
|
214
|
+
) : (
|
|
215
|
+
<button onClick={handleUpload}>
|
|
216
|
+
Upload Documents
|
|
217
|
+
</button>
|
|
218
|
+
)}
|
|
219
|
+
{isMutationSucceeded && (
|
|
220
|
+
<p>Upload completed successfully!</p>
|
|
221
|
+
)}
|
|
222
|
+
</div>
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
This will generate FormData where:
|
|
228
|
+
- Multiple files are sent with the same key name ('files')
|
|
229
|
+
- Metadata is properly nested with array indices preserved
|
|
230
|
+
- Additional data is structured appropriately
|
|
231
|
+
|
|
232
|
+
The resulting FormData structure will be:
|
|
233
|
+
```plaintext
|
|
234
|
+
files = File1
|
|
235
|
+
files = File2
|
|
236
|
+
metadata[department] = HR
|
|
237
|
+
metadata[tags][0] = confidential
|
|
238
|
+
metadata[tags][1] = employee
|
|
239
|
+
timestamp = 2024-02-23T10:00:00.000Z
|
|
240
|
+
user[id] = 123
|
|
241
|
+
user[role] = admin
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
#### When to Use excludedIndexKeys
|
|
245
|
+
|
|
246
|
+
Use `excludedIndexKeys` when:
|
|
247
|
+
1. Working with file upload APIs that expect multiple files with the same key
|
|
248
|
+
2. Dealing with legacy APIs that don't support indexed form fields
|
|
249
|
+
3. Implementing multi-file upload where the server expects a flat structure
|
|
250
|
+
4. Handling file arrays where order doesn't matter
|
|
251
|
+
|
|
252
|
+
### useGlobalInfiniteQuery
|
|
253
|
+
|
|
254
|
+
A custom hook for handling infinite scroll or pagination scenarios.
|
|
255
|
+
|
|
256
|
+
#### Features
|
|
257
|
+
- Automatic pagination handling
|
|
258
|
+
- Built-in cache management
|
|
259
|
+
- Total count tracking
|
|
260
|
+
- Next page detection
|
|
261
|
+
|
|
262
|
+
#### Basic Usage
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
const {
|
|
266
|
+
queryData,
|
|
267
|
+
isLoading,
|
|
268
|
+
isError,
|
|
269
|
+
error,
|
|
270
|
+
fetchNextPage,
|
|
271
|
+
hasNextPage,
|
|
272
|
+
totalCount
|
|
273
|
+
} = useGlobalInfiniteQuery({
|
|
274
|
+
url: '/api/posts',
|
|
275
|
+
queryKey: ['posts'],
|
|
276
|
+
methodType: 'GET',
|
|
277
|
+
data: { limit: 10 }
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### useCookie
|
|
282
|
+
|
|
283
|
+
A utility hook for managing cookies across the application.
|
|
284
|
+
|
|
285
|
+
#### Features
|
|
286
|
+
- Simple cookie management
|
|
287
|
+
- Type-safe cookie operations
|
|
288
|
+
- Easy integration with authentication
|
|
289
|
+
|
|
290
|
+
#### Basic Usage
|
|
291
|
+
|
|
292
|
+
```javascript
|
|
293
|
+
const { cookie, setCookie, removeCookie } = useCookie({
|
|
294
|
+
cookieName: 'accessToken'
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## API Reference
|
|
299
|
+
|
|
300
|
+
### useGlobalQuery Options
|
|
301
|
+
|
|
302
|
+
| Option | Type | Default | Description |
|
|
303
|
+
|--------|------|---------|-------------|
|
|
304
|
+
| url | string | required | API endpoint URL |
|
|
305
|
+
| queryKey | string[] | required | Unique key for caching |
|
|
306
|
+
| methodType | string | required | HTTP method (GET, POST, etc.) |
|
|
307
|
+
| data | object | optional | Request payload |
|
|
308
|
+
| enabled | boolean | true | Whether to enable the query |
|
|
309
|
+
| cacheTime | number | 300000 | Cache duration in ms |
|
|
310
|
+
| staleTime | number | 300000 | Stale data duration in ms |
|
|
311
|
+
|
|
312
|
+
### useGlobalMutation Options
|
|
313
|
+
|
|
314
|
+
| Option | Type | Default | Description |
|
|
315
|
+
|--------|------|---------|-------------|
|
|
316
|
+
| url | string | required | API endpoint URL |
|
|
317
|
+
| queriesToInvalidate | string[] | required | Queries to refresh after mutation |
|
|
318
|
+
| methodType | string | required | HTTP method (POST, PUT, DELETE) |
|
|
319
|
+
| data | object | optional | Default mutation data |
|
|
320
|
+
| isFormData | boolean | false | Whether to handle as FormData |
|
|
321
|
+
| closePopup | function | optional | Callback after success |
|
|
322
|
+
| excludedIndexKeys | string[] | optional | Keys to exclude from FormData indices |
|
|
323
|
+
|
|
324
|
+
### useGlobalInfiniteQuery Options
|
|
325
|
+
|
|
326
|
+
| Option | Type | Default | Description |
|
|
327
|
+
|--------|------|---------|-------------|
|
|
328
|
+
| url | string | required | API endpoint URL |
|
|
329
|
+
| queryKey | string[] | required | Unique key for caching |
|
|
330
|
+
| methodType | string | required | HTTP method |
|
|
331
|
+
| data | object | optional | Additional query parameters |
|
|
332
|
+
| enabled | boolean | true | Whether to enable the query |
|
|
333
|
+
| cacheTime | number | 300000 | Cache duration in ms |
|
|
334
|
+
| staleTime | number | 300000 | Stale data duration in ms |
|
|
335
|
+
|
|
336
|
+
## Best Practices
|
|
337
|
+
|
|
338
|
+
1. Always provide unique and descriptive query keys
|
|
339
|
+
2. Set appropriate cache and stale times based on data volatility
|
|
340
|
+
3. Handle loading and error states in your UI
|
|
341
|
+
4. Use the refetchQuery function for manual data refresh
|
|
342
|
+
5. Implement proper error boundaries in your application
|
|
343
|
+
|
|
344
|
+
## Contributing
|
|
345
|
+
|
|
346
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
347
|
+
|
|
348
|
+
## License
|
|
349
|
+
|
|
350
350
|
MIT
|
package/dist/query-harbor.es.js
CHANGED
|
@@ -1,274 +1,228 @@
|
|
|
1
|
-
import { useQueryClient as
|
|
1
|
+
import { useQueryClient as N, useQuery as b, useMutation as v, useInfiniteQuery as L } from "@tanstack/react-query";
|
|
2
2
|
import { useCookies as j } from "react-cookie";
|
|
3
3
|
import F from "axios";
|
|
4
|
-
const
|
|
5
|
-
const [
|
|
6
|
-
return { cookie:
|
|
7
|
-
},
|
|
8
|
-
|
|
9
|
-
queryKey: n,
|
|
10
|
-
methodType: o,
|
|
11
|
-
data: c,
|
|
12
|
-
enabled: y = !0,
|
|
13
|
-
cacheTime: h = 5 * 60 * 1e3,
|
|
14
|
-
staleTime: E = 5 * 60 * 1e3
|
|
15
|
-
}) => {
|
|
16
|
-
const m = M(), { cookie: e } = b({ cookieName: "accessToken" });
|
|
17
|
-
let t = {};
|
|
18
|
-
e != null && e.accessToken && (t = { Authorization: `Bearer ${e == null ? void 0 : e.accessToken}` });
|
|
19
|
-
const s = L({
|
|
20
|
-
queryKey: n,
|
|
21
|
-
queryFn: async () => {
|
|
22
|
-
try {
|
|
23
|
-
const i = await APIHandler({
|
|
24
|
-
action: o,
|
|
25
|
-
url: u,
|
|
26
|
-
data: c,
|
|
27
|
-
headers: t
|
|
28
|
-
});
|
|
29
|
-
return i != null && i.data ? i.data : { totalCount: 0, data: [] };
|
|
30
|
-
} catch (i) {
|
|
31
|
-
throw console.error("Query Error:", i), i;
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
enabled: y,
|
|
35
|
-
cacheTime: h,
|
|
36
|
-
staleTime: E,
|
|
37
|
-
refetchOnWindowFocus: !1
|
|
38
|
-
});
|
|
39
|
-
return {
|
|
40
|
-
refetchQuery: () => {
|
|
41
|
-
m.invalidateQueries({ queryKey: n });
|
|
42
|
-
},
|
|
43
|
-
queryData: s.data,
|
|
44
|
-
isLoading: s.isLoading,
|
|
45
|
-
isError: s.isError,
|
|
46
|
-
error: s.error
|
|
47
|
-
};
|
|
48
|
-
}, G = async ({ action: u, url: n, data: o, headers: c }) => {
|
|
49
|
-
switch (u) {
|
|
4
|
+
const M = ({ cookieName: i }) => {
|
|
5
|
+
const [s, o, n] = j([i]);
|
|
6
|
+
return { cookie: s, setCookie: o, removeCookie: n };
|
|
7
|
+
}, q = async ({ action: i, url: s, data: o, headers: n }) => {
|
|
8
|
+
switch (i) {
|
|
50
9
|
case "GET":
|
|
51
|
-
return await F.get(
|
|
52
|
-
headers: c
|
|
53
|
-
});
|
|
10
|
+
return await F.get(s, { headers: n });
|
|
54
11
|
case "POST":
|
|
55
|
-
return await F.post(
|
|
56
|
-
headers: c
|
|
57
|
-
});
|
|
12
|
+
return await F.post(s, o, { headers: n });
|
|
58
13
|
case "PUT":
|
|
59
|
-
return await F.put(
|
|
60
|
-
headers: c
|
|
61
|
-
});
|
|
14
|
+
return await F.put(s, o, { headers: n });
|
|
62
15
|
case "DELETE":
|
|
63
|
-
return await F.delete(
|
|
16
|
+
return await F.delete(s, { data: o, headers: n });
|
|
64
17
|
default:
|
|
65
|
-
throw new Error(`Invalid action: ${
|
|
18
|
+
throw new Error(`Invalid action: ${i}`);
|
|
66
19
|
}
|
|
67
|
-
},
|
|
68
|
-
var y,
|
|
20
|
+
}, I = async ({ action: i, url: s, data: o, headers: n }) => {
|
|
21
|
+
var y, m, P, d, r, e, a;
|
|
69
22
|
try {
|
|
70
|
-
const
|
|
71
|
-
return
|
|
23
|
+
const c = await q({ action: i, url: s, data: o, headers: n });
|
|
24
|
+
return c.status >= 200 && c.status <= 299 ? {
|
|
72
25
|
status: !0,
|
|
73
|
-
data:
|
|
74
|
-
message:
|
|
75
|
-
statusCode:
|
|
26
|
+
data: c.data,
|
|
27
|
+
message: c.data.message,
|
|
28
|
+
statusCode: c.status
|
|
76
29
|
} : {
|
|
77
30
|
status: !1,
|
|
78
31
|
error: "API Failed",
|
|
79
32
|
message: "API Failed",
|
|
80
|
-
statusCode:
|
|
33
|
+
statusCode: c.status
|
|
81
34
|
};
|
|
82
|
-
} catch (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
35
|
+
} catch (c) {
|
|
36
|
+
if (F.isAxiosError(c)) {
|
|
37
|
+
const t = c;
|
|
38
|
+
return t.message === "Network Error" ? {
|
|
39
|
+
status: !1,
|
|
40
|
+
error: "Network Error",
|
|
41
|
+
message: "Network Error"
|
|
42
|
+
} : {
|
|
43
|
+
status: !1,
|
|
44
|
+
type: (m = (y = t.response) == null ? void 0 : y.data) == null ? void 0 : m.type,
|
|
45
|
+
message: ((d = (P = t.response) == null ? void 0 : P.data) == null ? void 0 : d.message) || "API Failed",
|
|
46
|
+
error: ((e = (r = t.response) == null ? void 0 : r.data) == null ? void 0 : e.error) || "API Failed",
|
|
47
|
+
statusCode: (a = t.response) == null ? void 0 : a.status
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
94
51
|
status: !1,
|
|
95
52
|
error: "API Failed",
|
|
96
53
|
message: "API Failed"
|
|
97
54
|
};
|
|
98
55
|
}
|
|
99
|
-
},
|
|
100
|
-
url:
|
|
101
|
-
|
|
56
|
+
}, B = ({
|
|
57
|
+
url: i,
|
|
58
|
+
queryKey: s,
|
|
102
59
|
methodType: o,
|
|
103
|
-
data:
|
|
60
|
+
data: n,
|
|
61
|
+
enabled: y = !0,
|
|
62
|
+
staleTime: m = 5 * 60 * 1e3,
|
|
63
|
+
refetchOnWindowFocus: P = !1
|
|
64
|
+
}) => {
|
|
65
|
+
const d = N(), { cookie: r } = M({ cookieName: "accessToken" });
|
|
66
|
+
let e = {};
|
|
67
|
+
r != null && r.accessToken && (e = { Authorization: `Bearer ${r.accessToken}` });
|
|
68
|
+
const a = b({
|
|
69
|
+
queryKey: s,
|
|
70
|
+
queryFn: async () => {
|
|
71
|
+
try {
|
|
72
|
+
const t = await I({
|
|
73
|
+
action: o,
|
|
74
|
+
url: i,
|
|
75
|
+
data: n,
|
|
76
|
+
headers: e
|
|
77
|
+
});
|
|
78
|
+
return t != null && t.data ? t.data : { totalCount: 0, data: [] };
|
|
79
|
+
} catch (t) {
|
|
80
|
+
throw console.error("Query Error:", t), t;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
enabled: y,
|
|
84
|
+
staleTime: m,
|
|
85
|
+
refetchOnWindowFocus: P
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
refetchQuery: () => {
|
|
89
|
+
d.invalidateQueries({ queryKey: s });
|
|
90
|
+
},
|
|
91
|
+
queryData: a.data,
|
|
92
|
+
isLoading: a.isLoading,
|
|
93
|
+
isError: a.isError,
|
|
94
|
+
error: a.error
|
|
95
|
+
};
|
|
96
|
+
}, H = ({
|
|
97
|
+
url: i,
|
|
98
|
+
queriesToInvalidate: s,
|
|
99
|
+
methodType: o,
|
|
100
|
+
data: n,
|
|
104
101
|
isFormData: y,
|
|
105
|
-
closePopup:
|
|
106
|
-
excludedIndexKeys:
|
|
102
|
+
closePopup: m,
|
|
103
|
+
excludedIndexKeys: P
|
|
107
104
|
}) => {
|
|
108
|
-
var
|
|
109
|
-
const
|
|
110
|
-
let
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
* @async
|
|
116
|
-
* @param {Object} params - Mutation parameters.
|
|
117
|
-
* @param {boolean} [params.isPriorityDataAvailable] - Whether to use priority data.
|
|
118
|
-
* @param {*} [params.priorityData] - Priority data to override default data.
|
|
119
|
-
* @returns {Promise<*>} The response data from the API.
|
|
120
|
-
* @throws {Error} If the API request fails.
|
|
121
|
-
*/
|
|
122
|
-
mutationFn: async ({ isPriorityDataAvailable: l, priorityData: P }) => {
|
|
123
|
-
const p = l ? P : c;
|
|
105
|
+
var t;
|
|
106
|
+
const d = N(), { cookie: r } = M({ cookieName: "accessToken" });
|
|
107
|
+
let e = {};
|
|
108
|
+
r != null && r.accessToken && (e = { Authorization: `Bearer ${r == null ? void 0 : r.accessToken}` });
|
|
109
|
+
const a = v({
|
|
110
|
+
mutationFn: async ({ isPriorityDataAvailable: w, priorityData: k }) => {
|
|
111
|
+
const C = w ? k : n;
|
|
124
112
|
if (y) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
T(
|
|
135
|
-
r,
|
|
136
|
-
Q,
|
|
137
|
-
q,
|
|
138
|
-
A
|
|
139
|
-
);
|
|
140
|
-
}) : a !== "" && a !== void 0 && a !== null && (r == null || r.append(g, a));
|
|
141
|
-
}, x = {
|
|
142
|
-
excludedIndexKeys: E
|
|
113
|
+
const x = new FormData(), p = (u, l, g = "", A = {}) => {
|
|
114
|
+
if (l == null) return;
|
|
115
|
+
const { excludedIndexKeys: S = [] } = A;
|
|
116
|
+
Array.isArray(l) ? l.forEach((h, $) => {
|
|
117
|
+
const T = S.includes(g) ? g : g ? `${g}[${$}]` : `${$}`;
|
|
118
|
+
h instanceof File ? u.append(T, h) : typeof h == "object" && h !== null ? p(u, h, T, A) : u.append(T, String(h));
|
|
119
|
+
}) : typeof l == "object" && l !== null ? l instanceof File ? u.append(g, l) : Object.keys(l).forEach((h) => {
|
|
120
|
+
p(u, l[h], g ? `${g}[${h}]` : h, A);
|
|
121
|
+
}) : u.append(g, String(l));
|
|
143
122
|
};
|
|
144
|
-
Object.keys(
|
|
145
|
-
|
|
123
|
+
Object.keys(C || {}).forEach((u) => {
|
|
124
|
+
p(x, C == null ? void 0 : C[u], u, { excludedIndexKeys: P });
|
|
146
125
|
});
|
|
147
|
-
const {
|
|
148
|
-
status: w,
|
|
149
|
-
message: d,
|
|
150
|
-
data: k
|
|
151
|
-
} = await $({
|
|
126
|
+
const { status: f, message: E, data: Q } = await I({
|
|
152
127
|
action: o,
|
|
153
|
-
url:
|
|
154
|
-
data:
|
|
155
|
-
headers:
|
|
128
|
+
url: i,
|
|
129
|
+
data: x,
|
|
130
|
+
headers: e
|
|
156
131
|
});
|
|
157
|
-
if (
|
|
158
|
-
return
|
|
159
|
-
throw new Error(
|
|
132
|
+
if (f)
|
|
133
|
+
return Q;
|
|
134
|
+
throw new Error(E || "Something went wrong!");
|
|
160
135
|
} else {
|
|
161
|
-
const {
|
|
162
|
-
status: C,
|
|
163
|
-
message: T,
|
|
164
|
-
data: x
|
|
165
|
-
} = await $({
|
|
136
|
+
const { status: x, message: p, data: f } = await I({
|
|
166
137
|
action: o,
|
|
167
|
-
url:
|
|
168
|
-
data:
|
|
169
|
-
headers:
|
|
138
|
+
url: i,
|
|
139
|
+
data: C,
|
|
140
|
+
headers: e
|
|
170
141
|
});
|
|
171
|
-
if (
|
|
172
|
-
return
|
|
173
|
-
throw new Error(
|
|
142
|
+
if (x)
|
|
143
|
+
return f;
|
|
144
|
+
throw new Error(p || "Something went wrong!");
|
|
174
145
|
}
|
|
175
146
|
},
|
|
176
|
-
/**
|
|
177
|
-
* Callback executed on successful mutation.
|
|
178
|
-
* Invalidates specified queries and closes popup if provided.
|
|
179
|
-
*/
|
|
180
147
|
onSuccess: () => {
|
|
181
|
-
|
|
148
|
+
d.invalidateQueries({ queryKey: s }), m && m(!1);
|
|
182
149
|
},
|
|
183
|
-
|
|
184
|
-
* Callback executed on mutation error.
|
|
185
|
-
* @param {Error} error - The error object from the failed mutation.
|
|
186
|
-
* @returns {string} The error message.
|
|
187
|
-
*/
|
|
188
|
-
onError: (l) => (console.log("mutationError", l == null ? void 0 : l.message), l == null ? void 0 : l.message)
|
|
150
|
+
onError: (w) => (console.error("mutationError", w.message), w.message)
|
|
189
151
|
});
|
|
190
152
|
return {
|
|
191
|
-
runMutation: (
|
|
153
|
+
runMutation: (w) => {
|
|
192
154
|
try {
|
|
193
|
-
|
|
194
|
-
} catch (
|
|
195
|
-
console.
|
|
155
|
+
a.mutate(w || {});
|
|
156
|
+
} catch (k) {
|
|
157
|
+
console.error("Mutation Error: ", k);
|
|
196
158
|
}
|
|
197
159
|
},
|
|
198
|
-
mutationLoading:
|
|
199
|
-
mutationData:
|
|
200
|
-
mutationError: (
|
|
201
|
-
isMutationSucceeded:
|
|
160
|
+
mutationLoading: a.isPending,
|
|
161
|
+
mutationData: a.data,
|
|
162
|
+
mutationError: (t = a.error) == null ? void 0 : t.message,
|
|
163
|
+
isMutationSucceeded: a.isSuccess
|
|
202
164
|
};
|
|
203
|
-
},
|
|
204
|
-
url:
|
|
205
|
-
queryKey:
|
|
165
|
+
}, W = ({
|
|
166
|
+
url: i,
|
|
167
|
+
queryKey: s,
|
|
206
168
|
methodType: o,
|
|
207
|
-
data:
|
|
169
|
+
data: n,
|
|
208
170
|
enabled: y = !0,
|
|
209
|
-
|
|
210
|
-
staleTime: E = 5 * 60 * 1e3
|
|
171
|
+
staleTime: m = 5 * 60 * 1e3
|
|
211
172
|
}) => {
|
|
212
|
-
var
|
|
213
|
-
const
|
|
214
|
-
let
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
queryKey:
|
|
218
|
-
queryFn: async ({ pageParam:
|
|
173
|
+
var w, k, C, x, p;
|
|
174
|
+
const P = N(), { cookie: d } = M({ cookieName: "accessToken" });
|
|
175
|
+
let r = {};
|
|
176
|
+
d != null && d.accessToken && (r = { Authorization: `Bearer ${d.accessToken}` });
|
|
177
|
+
const e = L({
|
|
178
|
+
queryKey: s,
|
|
179
|
+
queryFn: async ({ pageParam: f = 1 }) => {
|
|
219
180
|
try {
|
|
220
|
-
const
|
|
181
|
+
const E = await I({
|
|
221
182
|
action: o,
|
|
222
|
-
url:
|
|
183
|
+
url: i,
|
|
223
184
|
data: {
|
|
224
|
-
...
|
|
225
|
-
page:
|
|
185
|
+
...n,
|
|
186
|
+
page: f
|
|
226
187
|
},
|
|
227
|
-
headers:
|
|
188
|
+
headers: r
|
|
228
189
|
});
|
|
229
|
-
if (
|
|
230
|
-
const { data:
|
|
190
|
+
if (E != null && E.data) {
|
|
191
|
+
const { data: Q, page: u, totalPages: l, totalCount: g } = E.data, A = u < l;
|
|
231
192
|
return {
|
|
232
|
-
data:
|
|
233
|
-
nextPage: A ?
|
|
193
|
+
data: Q,
|
|
194
|
+
nextPage: A ? u + 1 : void 0,
|
|
234
195
|
hasMore: A,
|
|
235
196
|
totalCount: g
|
|
236
197
|
};
|
|
237
198
|
} else
|
|
238
|
-
return { totalCount: 0, data: [] };
|
|
239
|
-
} catch (
|
|
240
|
-
throw console.error("Query Error:",
|
|
199
|
+
return { totalCount: 0, data: [], hasMore: !1 };
|
|
200
|
+
} catch (E) {
|
|
201
|
+
throw console.error("Query Error:", E), E;
|
|
241
202
|
}
|
|
242
203
|
},
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
* @param {Object} lastPage - The data from the last fetched page.
|
|
246
|
-
* @param {boolean} lastPage.hasMore - Whether more pages exist.
|
|
247
|
-
* @param {number} [lastPage.nextPage] - The next page number.
|
|
248
|
-
* @returns {number|undefined} The next page parameter or undefined if no more pages.
|
|
249
|
-
*/
|
|
250
|
-
getNextPageParam: (w) => w.hasMore ? w.nextPage : void 0,
|
|
204
|
+
initialPageParam: 1,
|
|
205
|
+
getNextPageParam: (f) => f.hasMore ? f.nextPage : void 0,
|
|
251
206
|
enabled: y,
|
|
252
|
-
|
|
253
|
-
staleTime: E,
|
|
207
|
+
staleTime: m,
|
|
254
208
|
refetchOnWindowFocus: !1
|
|
255
|
-
}),
|
|
256
|
-
|
|
257
|
-
},
|
|
209
|
+
}), a = () => {
|
|
210
|
+
P.invalidateQueries({ queryKey: s });
|
|
211
|
+
}, c = ((k = (w = e == null ? void 0 : e.data) == null ? void 0 : w.pages) == null ? void 0 : k.flatMap((f) => f.data)) || [], t = ((p = (x = (C = e == null ? void 0 : e.data) == null ? void 0 : C.pages) == null ? void 0 : x[0]) == null ? void 0 : p.totalCount) ?? 0;
|
|
258
212
|
return {
|
|
259
|
-
refetchQuery:
|
|
260
|
-
queryData:
|
|
261
|
-
isLoading:
|
|
262
|
-
isError:
|
|
263
|
-
error:
|
|
264
|
-
fetchNextPage:
|
|
265
|
-
hasNextPage:
|
|
266
|
-
totalCount:
|
|
213
|
+
refetchQuery: a,
|
|
214
|
+
queryData: c,
|
|
215
|
+
isLoading: e.isLoading,
|
|
216
|
+
isError: e.isError,
|
|
217
|
+
error: e.error,
|
|
218
|
+
fetchNextPage: e.fetchNextPage,
|
|
219
|
+
hasNextPage: e.hasNextPage,
|
|
220
|
+
totalCount: t
|
|
267
221
|
};
|
|
268
222
|
};
|
|
269
223
|
export {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
224
|
+
M as useCookie,
|
|
225
|
+
W as useGlobalInfiniteQuery,
|
|
226
|
+
H as useGlobalMutation,
|
|
227
|
+
B as useGlobalQuery
|
|
274
228
|
};
|
package/dist/query-harbor.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(c,g){typeof exports=="object"&&typeof module<"u"?g(exports,require("@tanstack/react-query"),require("react-cookie"),require("axios")):typeof define=="function"&&define.amd?define(["exports","@tanstack/react-query","react-cookie","axios"],g):(c=typeof globalThis<"u"?globalThis:c||self,g(c.QueryHarbor={},c.ReactQuery,c.ReactCookie,c.axios))})(this,function(c,g,
|
|
1
|
+
(function(c,g){typeof exports=="object"&&typeof module<"u"?g(exports,require("@tanstack/react-query"),require("react-cookie"),require("axios")):typeof define=="function"&&define.amd?define(["exports","@tanstack/react-query","react-cookie","axios"],g):(c=typeof globalThis<"u"?globalThis:c||self,g(c.QueryHarbor={},c.ReactQuery,c.ReactCookie,c.axios))})(this,function(c,g,q,Q){"use strict";const x=({cookieName:i})=>{const[s,o,n]=q.useCookies([i]);return{cookie:s,setCookie:o,removeCookie:n}},$=async({action:i,url:s,data:o,headers:n})=>{switch(i){case"GET":return await Q.get(s,{headers:n});case"POST":return await Q.post(s,o,{headers:n});case"PUT":return await Q.put(s,o,{headers:n});case"DELETE":return await Q.delete(s,{data:o,headers:n});default:throw new Error(`Invalid action: ${i}`)}},M=async({action:i,url:s,data:o,headers:n})=>{var k,E,P,f,r,e,a;try{const u=await $({action:i,url:s,data:o,headers:n});return u.status>=200&&u.status<=299?{status:!0,data:u.data,message:u.data.message,statusCode:u.status}:{status:!1,error:"API Failed",message:"API Failed",statusCode:u.status}}catch(u){if(Q.isAxiosError(u)){const t=u;return t.message==="Network Error"?{status:!1,error:"Network Error",message:"Network Error"}:{status:!1,type:(E=(k=t.response)==null?void 0:k.data)==null?void 0:E.type,message:((f=(P=t.response)==null?void 0:P.data)==null?void 0:f.message)||"API Failed",error:((e=(r=t.response)==null?void 0:r.data)==null?void 0:e.error)||"API Failed",statusCode:(a=t.response)==null?void 0:a.status}}return{status:!1,error:"API Failed",message:"API Failed"}}},v=({url:i,queryKey:s,methodType:o,data:n,enabled:k=!0,staleTime:E=3e5,refetchOnWindowFocus:P=!1})=>{const f=g.useQueryClient(),{cookie:r}=x({cookieName:"accessToken"});let e={};r!=null&&r.accessToken&&(e={Authorization:`Bearer ${r.accessToken}`});const a=g.useQuery({queryKey:s,queryFn:async()=>{try{const t=await M({action:o,url:i,data:n,headers:e});return t!=null&&t.data?t.data:{totalCount:0,data:[]}}catch(t){throw console.error("Query Error:",t),t}},enabled:k,staleTime:E,refetchOnWindowFocus:P});return{refetchQuery:()=>{f.invalidateQueries({queryKey:s})},queryData:a.data,isLoading:a.isLoading,isError:a.isError,error:a.error}},G=({url:i,queriesToInvalidate:s,methodType:o,data:n,isFormData:k,closePopup:E,excludedIndexKeys:P})=>{var t;const f=g.useQueryClient(),{cookie:r}=x({cookieName:"accessToken"});let e={};r!=null&&r.accessToken&&(e={Authorization:`Bearer ${r==null?void 0:r.accessToken}`});const a=g.useMutation({mutationFn:async({isPriorityDataAvailable:m,priorityData:A})=>{const p=m?A:n;if(k){const T=new FormData,I=(l,d,y="",F={})=>{if(d==null)return;const{excludedIndexKeys:L=[]}=F;Array.isArray(d)?d.forEach((w,S)=>{const N=L.includes(y)?y:y?`${y}[${S}]`:`${S}`;w instanceof File?l.append(N,w):typeof w=="object"&&w!==null?I(l,w,N,F):l.append(N,String(w))}):typeof d=="object"&&d!==null?d instanceof File?l.append(y,d):Object.keys(d).forEach(w=>{I(l,d[w],y?`${y}[${w}]`:w,F)}):l.append(y,String(d))};Object.keys(p||{}).forEach(l=>{I(T,p==null?void 0:p[l],l,{excludedIndexKeys:P})});const{status:h,message:C,data:b}=await M({action:o,url:i,data:T,headers:e});if(h)return b;throw new Error(C||"Something went wrong!")}else{const{status:T,message:I,data:h}=await M({action:o,url:i,data:p,headers:e});if(T)return h;throw new Error(I||"Something went wrong!")}},onSuccess:()=>{f.invalidateQueries({queryKey:s}),E&&E(!1)},onError:m=>(console.error("mutationError",m.message),m.message)});return{runMutation:m=>{try{a.mutate(m||{})}catch(A){console.error("Mutation Error: ",A)}},mutationLoading:a.isPending,mutationData:a.data,mutationError:(t=a.error)==null?void 0:t.message,isMutationSucceeded:a.isSuccess}},j=({url:i,queryKey:s,methodType:o,data:n,enabled:k=!0,staleTime:E=3e5})=>{var m,A,p,T,I;const P=g.useQueryClient(),{cookie:f}=x({cookieName:"accessToken"});let r={};f!=null&&f.accessToken&&(r={Authorization:`Bearer ${f.accessToken}`});const e=g.useInfiniteQuery({queryKey:s,queryFn:async({pageParam:h=1})=>{try{const C=await M({action:o,url:i,data:{...n,page:h},headers:r});if(C!=null&&C.data){const{data:b,page:l,totalPages:d,totalCount:y}=C.data,F=l<d;return{data:b,nextPage:F?l+1:void 0,hasMore:F,totalCount:y}}else return{totalCount:0,data:[],hasMore:!1}}catch(C){throw console.error("Query Error:",C),C}},initialPageParam:1,getNextPageParam:h=>h.hasMore?h.nextPage:void 0,enabled:k,staleTime:E,refetchOnWindowFocus:!1}),a=()=>{P.invalidateQueries({queryKey:s})},u=((A=(m=e==null?void 0:e.data)==null?void 0:m.pages)==null?void 0:A.flatMap(h=>h.data))||[],t=((I=(T=(p=e==null?void 0:e.data)==null?void 0:p.pages)==null?void 0:T[0])==null?void 0:I.totalCount)??0;return{refetchQuery:a,queryData:u,isLoading:e.isLoading,isError:e.isError,error:e.error,fetchNextPage:e.fetchNextPage,hasNextPage:e.hasNextPage,totalCount:t}};c.useCookie=x,c.useGlobalInfiniteQuery=j,c.useGlobalMutation=G,c.useGlobalQuery=v,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "query-harbor",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"files": ["dist"],
|
|
6
|
-
"main": "./dist/query-harbor.umd.js",
|
|
7
|
-
"module": "./dist/query-harbor.es.js",
|
|
8
|
-
"keywords": ["react", "react-query", "hooks", "tanstack", "query", "react-hooks", "harbor", "query-harbor", "react-query-hooks", "custom-hooks"],
|
|
9
|
-
"author": "Rohit Chaware",
|
|
10
|
-
"license": "MIT",
|
|
11
|
-
"description": "A React Query utility package for efficient data fetching and caching",
|
|
12
|
-
"repository": {
|
|
13
|
-
"type": "git",
|
|
14
|
-
"url": "https://github.com/R4Rohit23/react-query-hooks"
|
|
15
|
-
},
|
|
16
|
-
"exports": {
|
|
17
|
-
".": {
|
|
18
|
-
"import": "./dist/query-harbor.es.js",
|
|
19
|
-
"require": "./dist/query-harbor.umd.js"
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"scripts": {
|
|
23
|
-
"dev": "vite",
|
|
24
|
-
"build": "vite build",
|
|
25
|
-
"lint": "eslint .",
|
|
26
|
-
"preview": "vite preview"
|
|
27
|
-
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"@tanstack/react-query": "^5.66.9",
|
|
30
|
-
"axios": "^1.7.9",
|
|
31
|
-
"react": "^19.0.0",
|
|
32
|
-
"react-cookie": "^7.2.2",
|
|
33
|
-
"react-dom": "^19.0.0"
|
|
34
|
-
},
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"@eslint/js": "^9.19.0",
|
|
37
|
-
"@types/react": "^19.0.8",
|
|
38
|
-
"@types/react-dom": "^19.0.3",
|
|
39
|
-
"@vitejs/plugin-react": "^4.3.4",
|
|
40
|
-
"eslint": "^9.19.0",
|
|
41
|
-
"eslint-plugin-react": "^7.37.4",
|
|
42
|
-
"eslint-plugin-react-hooks": "^5.0.0",
|
|
43
|
-
"eslint-plugin-react-refresh": "^0.4.18",
|
|
44
|
-
"globals": "^15.14.0",
|
|
45
|
-
"vite": "^6.1.0"
|
|
46
|
-
}
|
|
47
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "query-harbor",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"files": ["dist"],
|
|
6
|
+
"main": "./dist/query-harbor.umd.js",
|
|
7
|
+
"module": "./dist/query-harbor.es.js",
|
|
8
|
+
"keywords": ["react", "react-query", "hooks", "tanstack", "query", "react-hooks", "harbor", "query-harbor", "react-query-hooks", "custom-hooks"],
|
|
9
|
+
"author": "Rohit Chaware",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"description": "A React Query utility package for efficient data fetching and caching",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/R4Rohit23/react-query-hooks"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./dist/query-harbor.es.js",
|
|
19
|
+
"require": "./dist/query-harbor.umd.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"dev": "vite",
|
|
24
|
+
"build": "vite build",
|
|
25
|
+
"lint": "eslint .",
|
|
26
|
+
"preview": "vite preview"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@tanstack/react-query": "^5.66.9",
|
|
30
|
+
"axios": "^1.7.9",
|
|
31
|
+
"react": "^19.0.0",
|
|
32
|
+
"react-cookie": "^7.2.2",
|
|
33
|
+
"react-dom": "^19.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@eslint/js": "^9.19.0",
|
|
37
|
+
"@types/react": "^19.0.8",
|
|
38
|
+
"@types/react-dom": "^19.0.3",
|
|
39
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
40
|
+
"eslint": "^9.19.0",
|
|
41
|
+
"eslint-plugin-react": "^7.37.4",
|
|
42
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
43
|
+
"eslint-plugin-react-refresh": "^0.4.18",
|
|
44
|
+
"globals": "^15.14.0",
|
|
45
|
+
"vite": "^6.1.0"
|
|
46
|
+
}
|
|
47
|
+
}
|