@ametie/vue-muza-use 0.0.1

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 ADDED
@@ -0,0 +1,267 @@
1
+ # Vue Muza Use 🎹
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@mortyq/vue-muza-use.svg?style=flat-square)](https://www.npmjs.com/package/@mortyq/vue-muza-use)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
5
+ [![Vue 3](https://img.shields.io/badge/Vue-3.x-green.svg?style=flat-square)](https://vuejs.org/)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Included-blue.svg?style=flat-square)](https://www.typescriptlang.org/)
7
+
8
+ **The Enterprise-Ready API Composable for Vue 3.**
9
+
10
+ `vue-muza-use` is a powerful, framework-agnostic wrapper around **Axios** designed to solve the most common headaches in modern frontend development: race conditions, token refreshing, request cancellation, and strict typing.
11
+
12
+ ---
13
+
14
+ ## 🚀 Why is this cool?
15
+
16
+ * 🛡 **Zombie Retry Protection**
17
+ Smart retries that respect component lifecycle. If your component is unmounted, the retries stop immediately. No more memory leaks or errors trying to update dead components.
18
+
19
+ * 💉 **Dependency Injection**
20
+ Fully decoupled architecture. You inject your own Axios instance via the Vue plugin system (`app.use`). This means you keep full control over your HTTP client configuration.
21
+
22
+ * 🔄 **Smart Auth Refresh**
23
+ Built-in (but optional) **Interceptor Queue**. If your Access Token expires (`401`), the library pauses all outgoing requests, refreshes the token once, replays the queue, and then resumes normal operation.
24
+
25
+ * ⚡ **Developer Experience**
26
+ * **Auto-Cleanup**: Aborts pending requests automatically when the component unmounts.
27
+ * **Global Abort**: Cancel all previous pending requests when filters change (race condition killer).
28
+ * **Debounce**: Built-in debouncing for search inputs.
29
+ * **TypeScript First**: strict typing for Request/Response definitions.
30
+
31
+ ---
32
+
33
+ ## 📦 Installation
34
+
35
+ ```bash
36
+ # npm
37
+ npm install @mortyq/vue-muza-use axios
38
+
39
+ # pnpm
40
+ pnpm add @mortyq/vue-muza-use axios
41
+
42
+ # yarn
43
+ yarn add @mortyq/vue-muza-use axios
44
+ ```
45
+
46
+ ---
47
+
48
+ ## ⚡ Quick Start
49
+
50
+ ### 1. Setup in `main.ts`
51
+
52
+ Use `createApiClient` for a "batteries-included" setup, or bring your own Axios instance.
53
+
54
+ ```typescript
55
+ import { createApp } from 'vue'
56
+ import { createApi, createApiClient, setAuthMonitor } from '@mortyq/vue-muza-use'
57
+ import App from './App.vue'
58
+
59
+ const app = createApp(App)
60
+
61
+ // 1. Create Axios instance with Auth features
62
+ const api = createApiClient({
63
+ baseURL: 'https://api.example.com',
64
+ withAuth: true, // Enable automatic token injection
65
+ authOptions: {
66
+ refreshUrl: '/auth/refresh', // Endpoint for refreshing tokens
67
+ onTokenRefreshFailed: () => {
68
+ window.location.href = '/login' // Redirect on session expiry
69
+ }
70
+ }
71
+ })
72
+
73
+ // 2. Install Plugin
74
+ app.use(createApi({
75
+ axios: api,
76
+ // Global Error Handler (e.g., Toast notifications)
77
+ onError: (error) => {
78
+ console.error('Global API Error:', error.message)
79
+ // toast.error(error.message)
80
+ }
81
+ }))
82
+
83
+ // Optional: Monitor Auth Events (Debug or Analytics)
84
+ setAuthMonitor((event, payload) => {
85
+ console.log(`[Auth] ${event}`, payload)
86
+ })
87
+
88
+ app.mount('#app')
89
+ ```
90
+
91
+ ### 2. Basic Usage (Component)
92
+
93
+ ```typescript
94
+ <script setup lang="ts">
95
+ import { useApi } from '@mortyq/vue-muza-use'
96
+
97
+ interface User {
98
+ id: number
99
+ name: string
100
+ }
101
+
102
+ // GET request
103
+ const { data, loading, error, execute } = useApi<User>('/users/1', {
104
+ immediate: true, // Fetch on mount
105
+ retry: 3 // Retry 3 times on failure
106
+ })
107
+ </script>
108
+
109
+ <template>
110
+ <div v-if="loading">Loading...</div>
111
+ <div v-else-if="error">Error: {{ error.message }}</div>
112
+ <div v-else-if="data">Hello, {{ data.name }}</div>
113
+ </template>
114
+ ```
115
+
116
+ ---
117
+
118
+ ## 🛠 Usage Examples
119
+
120
+ ### Reactive POST Request
121
+ Typically used for forms. Pass a `ref` as `data`, and it automatically unwraps current values.
122
+
123
+ ```typescript
124
+ <script setup lang="ts">
125
+ import { ref } from 'vue'
126
+ import { useApiPost } from '@mortyq/vue-muza-use'
127
+
128
+ const formData = ref({ email: '', password: '' })
129
+
130
+ const { execute, loading } = useApiPost('/auth/login', {
131
+ authMode: 'public', // 👈 Important for Login/Register to skip token injection
132
+ // Data can be a static object or a Ref
133
+ data: formData,
134
+ onSuccess: (res) => console.log('Logged in!', res.data)
135
+ })
136
+ </script>
137
+
138
+ <button @click="execute()" :disabled="loading">Login</button>
139
+ ```
140
+
141
+ ### Public Endpoints (No Auth)
142
+ For endpoints that don't require authentication (like Registration, Forgot Password, or Public Data), explicitly set `authMode: 'public'`.
143
+
144
+ ```typescript
145
+ const { execute } = useApiPost('/auth/register', {
146
+ authMode: 'public', // 👈 Prevents Authorization header injection
147
+ data: registrationForm
148
+ })
149
+ ```
150
+
151
+ ### Debounced Search
152
+ Perfect for search inputs to avoid spamming the API.
153
+
154
+ ```typescript
155
+ const searchQuery = ref('')
156
+
157
+ const { data, execute } = useApi<SearchResult[]>('/search', {
158
+ method: 'GET',
159
+ debounce: 500, // Wait 500ms after last call
160
+ abortPrevious: true // Kill previous request if new one starts
161
+ })
162
+
163
+ // Watcher triggers execute wrapped in debounce
164
+ watch(searchQuery, (newVal) => {
165
+ execute({ params: { q: newVal } })
166
+ })
167
+ ```
168
+
169
+ ### Global Abort (Race Condition Killer)
170
+ Useful for complex filters where changing one filter should invalidate all pending requests on the page (or scope).
171
+
172
+ ```typescript
173
+ import { useAbortController } from '@mortyq/vue-muza-use'
174
+
175
+ // In your specific composable or component
176
+ const { execute } = useApi('/heavy-report', {
177
+ useGlobalAbort: true // Subscribe to global abort signal
178
+ })
179
+
180
+ // Somewhere else (e.g. "Clear Filters" button)
181
+ const { abortAll } = useAbortController()
182
+ // abortAll() will cancel the '/heavy-report' request if it's pending
183
+ ```
184
+
185
+ ---
186
+
187
+ ## 📚 API Reference
188
+
189
+ ### `useApi<T, D>(url, options)`
190
+
191
+ The main composable.
192
+
193
+ **Arguments:**
194
+
195
+ | Argument | Type | Description |
196
+ |---|---|---|
197
+ | `url` | `string | Ref<string>` | The API endpoint URL. |
198
+ | `options` | `UseApiOptions` | Configuration object (see below). |
199
+
200
+ **Options (`options`):**
201
+
202
+ | Option | Type | Default | Description |
203
+ |---|---|---|---|
204
+ | `method` | `'GET' \| 'POST' ...` | `'GET'` | HTTP method. |
205
+ | `data` | `Ref<D> \| D` | `undefined` | Request body. |
206
+ | `immediate` | `boolean` | `false` | Trigger request automatically on creation. |
207
+ | `retry` | `boolean \| number` | `false` | Number of retries on failure. |
208
+ | `debounce` | `number` | `0` | Debounce time in ms. |
209
+ | `authMode` | `'default' \| 'public'` | `'default'` | `'public'` skips token injection. |
210
+ | `initialData` | `T` | `null` | Initial value for `data` ref. |
211
+ | `onSuccess` | `(res) => void` | - | Callback on 2xx response. |
212
+ | `onError` | `(err) => void` | - | Callback on error. |
213
+ | `skipErrorNotification`| `boolean` | `false` | Prevents triggering the global `onError`. |
214
+
215
+ **Return Values:**
216
+
217
+ ```typescript
218
+ {
219
+ data: Ref<T | null> // The response data
220
+ loading: Ref<boolean> // Loading state
221
+ error: Ref<ApiError | null> // Typed error object
222
+ execute: (config?) => Promise<T | null> // Manual trigger
223
+ abort: (msg?) => void // Cancel current request
224
+ response: Ref<AxiosResponse> // Full axios response object
225
+ }
226
+ ```
227
+
228
+ ### `createApiClient(options)`
229
+
230
+ Factory function to create a configured Axios instance.
231
+
232
+ ```typescript
233
+ interface CreateApiClientOptions {
234
+ // Standard Axios config (baseURL, timeout, etc.)
235
+ baseURL?: string;
236
+ timeout?: number;
237
+
238
+ // Custom auth features
239
+ withAuth?: boolean; // Default: true. Inject Authorization header?
240
+ authOptions?: {
241
+ refreshUrl?: string; // Default: '/auth/refresh'
242
+ onTokenRefreshFailed?: () => void;
243
+ };
244
+ }
245
+ ```
246
+
247
+ ---
248
+
249
+ ## 🔐 Auth & Refresh Logic
250
+
251
+ `vue-muza-use` implements a robust **Interceptor Queue** pattern for handling JWTs.
252
+
253
+ 1. **Request Flow**: Every request automatically gets the `Authorization: Bearer ...` header if `withAuth` is enabled.
254
+ 2. **401 Detection**: If a request fails with `401 Unauthorized`, it is **caught** by the interceptor.
255
+ 3. **Queueing**: The failed request is added to a `failedQueue`. Any subsequent requests that happen while refreshing are also paused and added to this queue.
256
+ 4. **Reference Refresh**: The system triggers a call to `refreshUrl` (e.g., sends a `refresh_token` HTTP-only cookie).
257
+ 5. **Retry**:
258
+ * **Success**: The system gets a new Access Token, updates the store, and **replays** all queued requests with the new token.
259
+ * **Failure**: If the refresh fails, `onTokenRefreshFailed` is called (use this to logout user) and all queued requests are rejected.
260
+
261
+ This happens transparently to your components. They just "wait" a bit longer for the response.
262
+
263
+ ---
264
+
265
+ ## License
266
+
267
+ MIT © [MortyQ](https://github.com/MortyQ)