@ametie/vue-muza-use 0.4.10 โ 0.5.0
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 +169 -17
- package/dist/index.cjs +7 -2
- package/dist/index.d.cts +32 -3
- package/dist/index.d.ts +32 -3
- package/dist/index.mjs +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,18 +13,48 @@ A production-ready composable that eliminates boilerplate and solves the hard pr
|
|
|
13
13
|
|
|
14
14
|
## โจ Features
|
|
15
15
|
|
|
16
|
+
**Core Features** (Get started in minutes):
|
|
16
17
|
- ๐ฏ **Fully Type-Safe** โ End-to-end TypeScript support with strict typing for requests and responses
|
|
17
18
|
- ๐ **Smart Reactivity** โ Watch refs and automatically refetch when dependencies change
|
|
18
19
|
- โฑ๏ธ **Built-in Debouncing** โ Perfect for search inputs and auto-save forms
|
|
19
20
|
- ๐ก๏ธ **Race Condition Protection** โ Global abort controller cancels stale requests automatically
|
|
20
|
-
- ๐ **JWT Token Management** โ Automatic token refresh with request queueing on 401 responses
|
|
21
|
-
- โป๏ธ **Intelligent Retries** โ Lifecycle-aware retry logic that respects component unmounting
|
|
22
21
|
- ๐ **Auto-Polling** โ Built-in interval fetching with smart tab visibility detection
|
|
23
22
|
- ๐งน **Zero Memory Leaks** โ Automatic cleanup of pending requests on component unmount
|
|
23
|
+
|
|
24
|
+
**Advanced Features** (When you need them):
|
|
25
|
+
- โป๏ธ **Intelligent Retries** โ Lifecycle-aware retry logic that respects component unmounting
|
|
26
|
+
- ๐ **JWT Token Management** โ Automatic token refresh with request queueing on 401 responses
|
|
24
27
|
- ๐๏ธ **Flexible Architecture** โ Bring your own Axios instance with full configuration control
|
|
25
28
|
|
|
26
29
|
---
|
|
27
30
|
|
|
31
|
+
## ๐ Table of Contents
|
|
32
|
+
|
|
33
|
+
**Getting Started:**
|
|
34
|
+
- [Installation](#-installation)
|
|
35
|
+
- [Quick Start](#-quick-start)
|
|
36
|
+
- [Basic Usage](#-basic-usage)
|
|
37
|
+
|
|
38
|
+
**Core Features:**
|
|
39
|
+
- [Watch & Auto-Refetch](#watch--auto-refetch)
|
|
40
|
+
- [Polling](#polling-background-updates)
|
|
41
|
+
- [Error Handling](#error-handling)
|
|
42
|
+
- [Loading States](#loading-states)
|
|
43
|
+
- [Manual Data Updates](#manual-data-updates)
|
|
44
|
+
|
|
45
|
+
**Real-World Examples:**
|
|
46
|
+
- [Data Table with Pagination](#data-table-with-pagination--sorting)
|
|
47
|
+
- [Request Cancellation](#request-cancellation)
|
|
48
|
+
|
|
49
|
+
**Advanced:**
|
|
50
|
+
- [Custom Axios Instance](#-advanced-configuration)
|
|
51
|
+
- [Authentication & Tokens](#-authentication--token-management) *(Optional)*
|
|
52
|
+
- [API Reference](#-api-reference)
|
|
53
|
+
|
|
54
|
+
> ๐ก **New to the library?** Start with [Quick Start](#-quick-start), then explore [Basic Usage](#-basic-usage). Skip authentication until you need it!
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
28
58
|
## ๐ฆ Installation
|
|
29
59
|
|
|
30
60
|
```bash
|
|
@@ -344,6 +374,108 @@ const { execute } = useApi('/analytics', {
|
|
|
344
374
|
|
|
345
375
|
---
|
|
346
376
|
|
|
377
|
+
### Manual Data Updates
|
|
378
|
+
|
|
379
|
+
Use `setData` to manually update the data ref. Supports direct values or updater functions (like React's `setState`).
|
|
380
|
+
|
|
381
|
+
> ๐ **When to use `setData`:**
|
|
382
|
+
> โ
Adding/removing/updating items in arrays
|
|
383
|
+
> โ
Local sorting/filtering (without refetching)
|
|
384
|
+
> โ
Transform data in `onSuccess` (adding computed fields)
|
|
385
|
+
>
|
|
386
|
+
> **When to use `computed` instead:**
|
|
387
|
+
> โ
Completely changing data structure (e.g., API format โ App format)
|
|
388
|
+
> โ
Extracting nested data that changes the return type
|
|
389
|
+
> โ
Complex transformations that depend on other refs
|
|
390
|
+
|
|
391
|
+
#### Add/Remove/Update Items
|
|
392
|
+
```typescript
|
|
393
|
+
const { data, setData } = useApi<Todo[]>('/todos', { immediate: true })
|
|
394
|
+
|
|
395
|
+
// Add item
|
|
396
|
+
const addTodo = (newTodo: Todo) => {
|
|
397
|
+
setData(prev => prev ? [...prev, newTodo] : [newTodo])
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Remove item
|
|
401
|
+
const removeTodo = (id: number) => {
|
|
402
|
+
setData(prev => prev?.filter(t => t.id !== id) ?? null)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Update item
|
|
406
|
+
const updateTodo = (id: number, updates: Partial<Todo>) => {
|
|
407
|
+
setData(prev =>
|
|
408
|
+
prev?.map(t => t.id === id ? { ...t, ...updates } : t) ?? null
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### Sort/Filter Locally
|
|
414
|
+
```typescript
|
|
415
|
+
const { data, setData } = useApi<Product[]>('/products', { immediate: true })
|
|
416
|
+
|
|
417
|
+
const sortByPrice = () => {
|
|
418
|
+
setData(prev => prev ? [...prev].sort((a, b) => a.price - b.price) : null)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const filterActive = () => {
|
|
422
|
+
setData(prev => prev?.filter(p => p.active) ?? null)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Reset to original
|
|
426
|
+
const resetFilters = () => execute()
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### Transform in `onSuccess`
|
|
430
|
+
|
|
431
|
+
Use `setData` in `onSuccess` to transform data right after fetching. Two approaches:
|
|
432
|
+
|
|
433
|
+
**Approach 1: Same type (recommended)**
|
|
434
|
+
```typescript
|
|
435
|
+
interface User {
|
|
436
|
+
id: number
|
|
437
|
+
firstName: string
|
|
438
|
+
lastName: string
|
|
439
|
+
fullName?: string // Optional field
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const { data, setData } = useApi<User[]>('/users', {
|
|
443
|
+
immediate: true,
|
|
444
|
+
onSuccess: ({ data: users }) => {
|
|
445
|
+
// Add computed field - still User[] type
|
|
446
|
+
setData(users.map(u => ({
|
|
447
|
+
...u,
|
|
448
|
+
fullName: `${u.firstName} ${u.lastName}`
|
|
449
|
+
})))
|
|
450
|
+
}
|
|
451
|
+
})
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
**Approach 2: Different structure (use separate computed)**
|
|
455
|
+
```typescript
|
|
456
|
+
interface ApiUser {
|
|
457
|
+
first_name: string
|
|
458
|
+
last_name: string
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// If API returns different structure, use computed for transformation
|
|
462
|
+
const { data: rawData } = useApi<ApiUser[]>('/users', { immediate: true })
|
|
463
|
+
|
|
464
|
+
const users = computed(() =>
|
|
465
|
+
rawData.value?.map(u => ({
|
|
466
|
+
firstName: u.first_name,
|
|
467
|
+
lastName: u.last_name,
|
|
468
|
+
fullName: `${u.first_name} ${u.last_name}`
|
|
469
|
+
})) ?? []
|
|
470
|
+
)
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
> ๐ก **Rule of thumb:**
|
|
474
|
+
> - โ
**Use `setData` in `onSuccess`** if you're adding/modifying fields but keeping the same base type
|
|
475
|
+
> - โ
**Use `computed`** if you're completely changing the data structure (e.g., snake_case โ camelCase)
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
347
479
|
## ๐ Real-World Examples
|
|
348
480
|
|
|
349
481
|
### Data Table with Pagination & Sorting
|
|
@@ -389,21 +521,6 @@ const { data, loading } = useApi('/orders', {
|
|
|
389
521
|
</template>
|
|
390
522
|
```
|
|
391
523
|
|
|
392
|
-
### Optimistic UI Updates
|
|
393
|
-
```typescript
|
|
394
|
-
const { execute: updateProfile } = useApi('/user/profile', {
|
|
395
|
-
method: 'PUT',
|
|
396
|
-
onBefore: () => {
|
|
397
|
-
// Show update immediately
|
|
398
|
-
localProfile.value = { ...localProfile.value, ...changes }
|
|
399
|
-
},
|
|
400
|
-
onError: () => {
|
|
401
|
-
// Rollback on error
|
|
402
|
-
localProfile.value = originalProfile
|
|
403
|
-
toast.error('Update failed')
|
|
404
|
-
}
|
|
405
|
-
})
|
|
406
|
-
```
|
|
407
524
|
|
|
408
525
|
### Request Cancellation
|
|
409
526
|
```typescript
|
|
@@ -752,11 +869,14 @@ The main composable for making HTTP requests.
|
|
|
752
869
|
data: Ref<T | null> // Response data
|
|
753
870
|
loading: Ref<boolean> // Loading state
|
|
754
871
|
error: Ref<ApiError | null> // Error object
|
|
872
|
+
statusCode: Ref<number | null> // HTTP status code
|
|
755
873
|
response: Ref<AxiosResponse<T>> // Full Axios response
|
|
756
874
|
|
|
757
875
|
// Methods
|
|
758
876
|
execute: (config?: AxiosRequestConfig) => Promise<T | null>
|
|
877
|
+
setData: (data: T | null | ((prev: T | null) => T | null)) => void
|
|
759
878
|
abort: (reason?: string) => void
|
|
879
|
+
reset: () => void
|
|
760
880
|
}
|
|
761
881
|
```
|
|
762
882
|
|
|
@@ -773,6 +893,24 @@ await execute()
|
|
|
773
893
|
await execute({ params: { page: 2 } })
|
|
774
894
|
```
|
|
775
895
|
|
|
896
|
+
#### `setData(newData)`
|
|
897
|
+
Manually update the `data` ref. Supports direct values or updater functions:
|
|
898
|
+
|
|
899
|
+
```typescript
|
|
900
|
+
const { data, setData } = useApi<User[]>('/users')
|
|
901
|
+
|
|
902
|
+
// Direct value
|
|
903
|
+
setData([{ id: 1, name: 'John' }])
|
|
904
|
+
|
|
905
|
+
// Updater function (like React's setState)
|
|
906
|
+
setData(prev => prev ? [...prev, newUser] : [newUser])
|
|
907
|
+
|
|
908
|
+
// Remove item
|
|
909
|
+
setData(prev => prev?.filter(u => u.id !== userId) ?? null)
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
> **Note:** `setData` automatically clears any existing error.
|
|
913
|
+
|
|
776
914
|
#### `abort(reason?)`
|
|
777
915
|
Cancel the current request:
|
|
778
916
|
|
|
@@ -785,6 +923,20 @@ execute()
|
|
|
785
923
|
setTimeout(() => abort('Timeout'), 5000)
|
|
786
924
|
```
|
|
787
925
|
|
|
926
|
+
#### `reset()`
|
|
927
|
+
Reset all state to initial values:
|
|
928
|
+
|
|
929
|
+
```typescript
|
|
930
|
+
const { data, error, loading, reset } = useApi('/users')
|
|
931
|
+
|
|
932
|
+
// Clear everything
|
|
933
|
+
reset()
|
|
934
|
+
// data.value = null, error.value = null, loading.value = false
|
|
935
|
+
|
|
936
|
+
// Cancel after 5 seconds
|
|
937
|
+
setTimeout(() => abort('Timeout'), 5000)
|
|
938
|
+
```
|
|
939
|
+
|
|
788
940
|
---
|
|
789
941
|
|
|
790
942
|
### `createApiClient(options)`
|
package/dist/index.cjs
CHANGED
|
@@ -126,8 +126,13 @@ function useApiState(initialData = null, options = {}) {
|
|
|
126
126
|
const error = (0, import_vue2.ref)(null);
|
|
127
127
|
const statusCode = (0, import_vue2.ref)(null);
|
|
128
128
|
const response = (0, import_vue2.ref)(null);
|
|
129
|
-
const setData = (
|
|
130
|
-
|
|
129
|
+
const setData = (newDataOrUpdater, fullResponse) => {
|
|
130
|
+
if (typeof newDataOrUpdater === "function") {
|
|
131
|
+
const updater = newDataOrUpdater;
|
|
132
|
+
data.value = updater(data.value);
|
|
133
|
+
} else {
|
|
134
|
+
data.value = newDataOrUpdater;
|
|
135
|
+
}
|
|
131
136
|
error.value = null;
|
|
132
137
|
if (fullResponse) {
|
|
133
138
|
response.value = fullResponse;
|
package/dist/index.d.cts
CHANGED
|
@@ -55,6 +55,25 @@ interface UseApiReturn<T = unknown, D = unknown> {
|
|
|
55
55
|
execute: (config?: ApiRequestConfig<D>) => Promise<T | null | undefined>;
|
|
56
56
|
abort: (message?: string) => void;
|
|
57
57
|
reset: () => void;
|
|
58
|
+
/**
|
|
59
|
+
* Manually update data. Supports direct value or updater function.
|
|
60
|
+
* Clears any existing error when called.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* // Direct value
|
|
64
|
+
* setData(newUsers)
|
|
65
|
+
*
|
|
66
|
+
* // Updater function (like React's setState)
|
|
67
|
+
* setData(prev => prev?.filter(u => u.active) ?? null)
|
|
68
|
+
*
|
|
69
|
+
* // Transform data after fetch
|
|
70
|
+
* const { data, setData } = useApi('/users', {
|
|
71
|
+
* onSuccess: ({ data }) => {
|
|
72
|
+
* setData(data.map(user => ({ ...user, fullName: `${user.first} ${user.last}` })))
|
|
73
|
+
* }
|
|
74
|
+
* })
|
|
75
|
+
*/
|
|
76
|
+
setData: (newData: T | null | ((prev: T | null) => T | null)) => void;
|
|
58
77
|
}
|
|
59
78
|
interface ApiPluginOptions {
|
|
60
79
|
axios: AxiosInstance;
|
|
@@ -151,6 +170,8 @@ declare function useApiDelete<T = unknown>(url: MaybeRefOrGetter<string | undefi
|
|
|
151
170
|
* - !data && !error && !loading โ idle
|
|
152
171
|
*/
|
|
153
172
|
|
|
173
|
+
/** Input type for setData - supports direct value or updater function */
|
|
174
|
+
type SetDataInput<T> = T | null | ((prev: T | null) => T | null);
|
|
154
175
|
interface UseApiStateReturn<T = unknown> {
|
|
155
176
|
/** Response data */
|
|
156
177
|
data: Ref<T | null>;
|
|
@@ -162,8 +183,16 @@ interface UseApiStateReturn<T = unknown> {
|
|
|
162
183
|
statusCode: Ref<number | null>;
|
|
163
184
|
/** Full Axios response - includes headers, status, config, etc (optional, for advanced use cases) */
|
|
164
185
|
response: Ref<AxiosResponse<T> | null>;
|
|
165
|
-
/**
|
|
166
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Set data and clear error. Supports direct value or updater function.
|
|
188
|
+
* @example
|
|
189
|
+
* // Direct value
|
|
190
|
+
* setData(newUsers)
|
|
191
|
+
*
|
|
192
|
+
* // Updater function (like React's setState)
|
|
193
|
+
* setData(prev => prev?.filter(u => u.active) ?? null)
|
|
194
|
+
*/
|
|
195
|
+
setData: (newData: SetDataInput<T>, fullResponse?: AxiosResponse<T> | null) => void;
|
|
167
196
|
/** Set error */
|
|
168
197
|
setError: (newError: ApiError | null) => void;
|
|
169
198
|
/** Set loading state */
|
|
@@ -395,4 +424,4 @@ interface AuthEventPayload {
|
|
|
395
424
|
type AuthMonitorFn = (type: AuthEventType, payload: AuthEventPayload) => void;
|
|
396
425
|
declare function setAuthMonitor(fn: AuthMonitorFn): void;
|
|
397
426
|
|
|
398
|
-
export { type ApiError, type ApiPluginOptions, type ApiRequestConfig, type ApiState, type AuthEventPayload, AuthEventType, type AuthMode, type AuthMonitorFn, type AuthTokens$1 as AuthTokens, type UseApiOptions, type UseApiReturn, createApi, createApiClient, setAuthMonitor, setupInterceptors, tokenManager, useAbortController, useApi, useApiConfig, useApiDelete, useApiGet, useApiPatch, useApiPost, useApiPut, useApiState };
|
|
427
|
+
export { type ApiError, type ApiPluginOptions, type ApiRequestConfig, type ApiState, type AuthEventPayload, AuthEventType, type AuthMode, type AuthMonitorFn, type AuthTokens$1 as AuthTokens, type SetDataInput, type UseApiOptions, type UseApiReturn, createApi, createApiClient, setAuthMonitor, setupInterceptors, tokenManager, useAbortController, useApi, useApiConfig, useApiDelete, useApiGet, useApiPatch, useApiPost, useApiPut, useApiState };
|
package/dist/index.d.ts
CHANGED
|
@@ -55,6 +55,25 @@ interface UseApiReturn<T = unknown, D = unknown> {
|
|
|
55
55
|
execute: (config?: ApiRequestConfig<D>) => Promise<T | null | undefined>;
|
|
56
56
|
abort: (message?: string) => void;
|
|
57
57
|
reset: () => void;
|
|
58
|
+
/**
|
|
59
|
+
* Manually update data. Supports direct value or updater function.
|
|
60
|
+
* Clears any existing error when called.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* // Direct value
|
|
64
|
+
* setData(newUsers)
|
|
65
|
+
*
|
|
66
|
+
* // Updater function (like React's setState)
|
|
67
|
+
* setData(prev => prev?.filter(u => u.active) ?? null)
|
|
68
|
+
*
|
|
69
|
+
* // Transform data after fetch
|
|
70
|
+
* const { data, setData } = useApi('/users', {
|
|
71
|
+
* onSuccess: ({ data }) => {
|
|
72
|
+
* setData(data.map(user => ({ ...user, fullName: `${user.first} ${user.last}` })))
|
|
73
|
+
* }
|
|
74
|
+
* })
|
|
75
|
+
*/
|
|
76
|
+
setData: (newData: T | null | ((prev: T | null) => T | null)) => void;
|
|
58
77
|
}
|
|
59
78
|
interface ApiPluginOptions {
|
|
60
79
|
axios: AxiosInstance;
|
|
@@ -151,6 +170,8 @@ declare function useApiDelete<T = unknown>(url: MaybeRefOrGetter<string | undefi
|
|
|
151
170
|
* - !data && !error && !loading โ idle
|
|
152
171
|
*/
|
|
153
172
|
|
|
173
|
+
/** Input type for setData - supports direct value or updater function */
|
|
174
|
+
type SetDataInput<T> = T | null | ((prev: T | null) => T | null);
|
|
154
175
|
interface UseApiStateReturn<T = unknown> {
|
|
155
176
|
/** Response data */
|
|
156
177
|
data: Ref<T | null>;
|
|
@@ -162,8 +183,16 @@ interface UseApiStateReturn<T = unknown> {
|
|
|
162
183
|
statusCode: Ref<number | null>;
|
|
163
184
|
/** Full Axios response - includes headers, status, config, etc (optional, for advanced use cases) */
|
|
164
185
|
response: Ref<AxiosResponse<T> | null>;
|
|
165
|
-
/**
|
|
166
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Set data and clear error. Supports direct value or updater function.
|
|
188
|
+
* @example
|
|
189
|
+
* // Direct value
|
|
190
|
+
* setData(newUsers)
|
|
191
|
+
*
|
|
192
|
+
* // Updater function (like React's setState)
|
|
193
|
+
* setData(prev => prev?.filter(u => u.active) ?? null)
|
|
194
|
+
*/
|
|
195
|
+
setData: (newData: SetDataInput<T>, fullResponse?: AxiosResponse<T> | null) => void;
|
|
167
196
|
/** Set error */
|
|
168
197
|
setError: (newError: ApiError | null) => void;
|
|
169
198
|
/** Set loading state */
|
|
@@ -395,4 +424,4 @@ interface AuthEventPayload {
|
|
|
395
424
|
type AuthMonitorFn = (type: AuthEventType, payload: AuthEventPayload) => void;
|
|
396
425
|
declare function setAuthMonitor(fn: AuthMonitorFn): void;
|
|
397
426
|
|
|
398
|
-
export { type ApiError, type ApiPluginOptions, type ApiRequestConfig, type ApiState, type AuthEventPayload, AuthEventType, type AuthMode, type AuthMonitorFn, type AuthTokens$1 as AuthTokens, type UseApiOptions, type UseApiReturn, createApi, createApiClient, setAuthMonitor, setupInterceptors, tokenManager, useAbortController, useApi, useApiConfig, useApiDelete, useApiGet, useApiPatch, useApiPost, useApiPut, useApiState };
|
|
427
|
+
export { type ApiError, type ApiPluginOptions, type ApiRequestConfig, type ApiState, type AuthEventPayload, AuthEventType, type AuthMode, type AuthMonitorFn, type AuthTokens$1 as AuthTokens, type SetDataInput, type UseApiOptions, type UseApiReturn, createApi, createApiClient, setAuthMonitor, setupInterceptors, tokenManager, useAbortController, useApi, useApiConfig, useApiDelete, useApiGet, useApiPatch, useApiPost, useApiPut, useApiState };
|
package/dist/index.mjs
CHANGED
|
@@ -76,8 +76,13 @@ function useApiState(initialData = null, options = {}) {
|
|
|
76
76
|
const error = ref(null);
|
|
77
77
|
const statusCode = ref(null);
|
|
78
78
|
const response = ref(null);
|
|
79
|
-
const setData = (
|
|
80
|
-
|
|
79
|
+
const setData = (newDataOrUpdater, fullResponse) => {
|
|
80
|
+
if (typeof newDataOrUpdater === "function") {
|
|
81
|
+
const updater = newDataOrUpdater;
|
|
82
|
+
data.value = updater(data.value);
|
|
83
|
+
} else {
|
|
84
|
+
data.value = newDataOrUpdater;
|
|
85
|
+
}
|
|
81
86
|
error.value = null;
|
|
82
87
|
if (fullResponse) {
|
|
83
88
|
response.value = fullResponse;
|