@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 +267 -0
- package/dist/index.cjs +573 -0
- package/dist/index.d.cts +350 -0
- package/dist/index.d.ts +350 -0
- package/dist/index.mjs +521 -0
- package/package.json +48 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import * as vue from 'vue';
|
|
2
|
+
import { MaybeRefOrGetter, Ref, App } from 'vue';
|
|
3
|
+
import { AxiosInstance, AxiosRequestConfig, AxiosResponse, CreateAxiosDefaults } from 'axios';
|
|
4
|
+
|
|
5
|
+
interface ApiError {
|
|
6
|
+
message: string;
|
|
7
|
+
status: number;
|
|
8
|
+
code?: string;
|
|
9
|
+
errors?: Record<string, string[]>;
|
|
10
|
+
details?: unknown;
|
|
11
|
+
}
|
|
12
|
+
type AuthMode = "default" | "public" | "optional";
|
|
13
|
+
interface ApiState<T = unknown> {
|
|
14
|
+
data: T | null;
|
|
15
|
+
loading: boolean;
|
|
16
|
+
error: ApiError | null;
|
|
17
|
+
statusCode: number | null;
|
|
18
|
+
}
|
|
19
|
+
interface ApiRequestConfig<D = unknown> extends Omit<AxiosRequestConfig<D>, "data"> {
|
|
20
|
+
data?: MaybeRefOrGetter<D> | D;
|
|
21
|
+
skipErrorNotification?: boolean;
|
|
22
|
+
authMode?: AuthMode;
|
|
23
|
+
retry?: boolean | number;
|
|
24
|
+
retryDelay?: number;
|
|
25
|
+
}
|
|
26
|
+
interface UseApiOptions<T = unknown, D = unknown> extends ApiRequestConfig<D> {
|
|
27
|
+
immediate?: boolean;
|
|
28
|
+
onSuccess?: (response: AxiosResponse<T>) => void;
|
|
29
|
+
onError?: (error: ApiError) => void;
|
|
30
|
+
onBefore?: () => void;
|
|
31
|
+
onFinish?: () => void;
|
|
32
|
+
initialData?: T;
|
|
33
|
+
debounce?: number;
|
|
34
|
+
useGlobalAbort?: boolean;
|
|
35
|
+
initialLoading?: boolean;
|
|
36
|
+
}
|
|
37
|
+
interface UseApiReturn<T = unknown, D = unknown> {
|
|
38
|
+
data: Ref<T | null>;
|
|
39
|
+
loading: Ref<boolean>;
|
|
40
|
+
error: Ref<ApiError | null>;
|
|
41
|
+
statusCode: Ref<number | null>;
|
|
42
|
+
response: Ref<AxiosResponse<T> | null>;
|
|
43
|
+
execute: (config?: ApiRequestConfig<D>) => Promise<T | null | undefined>;
|
|
44
|
+
abort: (message?: string) => void;
|
|
45
|
+
reset: () => void;
|
|
46
|
+
}
|
|
47
|
+
interface ApiPluginOptions {
|
|
48
|
+
axios: AxiosInstance;
|
|
49
|
+
onError?: (error: ApiError, originalError: any) => void;
|
|
50
|
+
globalOptions?: {
|
|
51
|
+
retry?: number | boolean;
|
|
52
|
+
retryDelay?: number;
|
|
53
|
+
useGlobalAbort?: boolean;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
interface AuthTokens$1 {
|
|
57
|
+
accessToken: string;
|
|
58
|
+
refreshToken?: string;
|
|
59
|
+
expiresIn?: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare function createApi(options: ApiPluginOptions): {
|
|
63
|
+
install(app: App): void;
|
|
64
|
+
};
|
|
65
|
+
declare function useApiConfig(): ApiPluginOptions;
|
|
66
|
+
|
|
67
|
+
declare function useApi<T = unknown, D = unknown>(url: string | Ref<string>, options?: UseApiOptions<T, D>): UseApiReturn<T, D>;
|
|
68
|
+
/**
|
|
69
|
+
* Helper for GET requests
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* const { data, loading, error } = useApiGet<User[]>('/users', {
|
|
74
|
+
* immediate: true
|
|
75
|
+
* })
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
declare function useApiGet<T = unknown>(url: string | Ref<string>, options?: Omit<UseApiOptions<T>, "method">): UseApiReturn<T>;
|
|
79
|
+
/**
|
|
80
|
+
* Helper for POST requests
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* const { data, loading, execute } = useApiPost<User, CreateUserDto>('/users')
|
|
85
|
+
* await execute({ data: { name: 'John' } })
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
declare function useApiPost<T = unknown, D = unknown>(url: string | Ref<string>, options?: Omit<UseApiOptions<T, D>, "method">): UseApiReturn<T, D>;
|
|
89
|
+
/**
|
|
90
|
+
* Helper for PUT requests
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* const { execute } = useApiPut<User, UpdateUserDto>('/users/1')
|
|
95
|
+
* await execute({ data: { name: 'John Doe' } })
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare function useApiPut<T = unknown, D = unknown>(url: string | Ref<string>, options?: Omit<UseApiOptions<T, D>, "method">): UseApiReturn<T, D>;
|
|
99
|
+
/**
|
|
100
|
+
* Helper for PATCH requests
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* const { execute } = useApiPatch<User, Partial<User>>('/users/1')
|
|
105
|
+
* await execute({ data: { name: 'John' } })
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare function useApiPatch<T = unknown, D = unknown>(url: string | Ref<string>, options?: Omit<UseApiOptions<T, D>, "method">): UseApiReturn<T, D>;
|
|
109
|
+
/**
|
|
110
|
+
* Helper for DELETE requests
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* const { execute } = useApiDelete('/users/1')
|
|
115
|
+
* await execute()
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
declare function useApiDelete<T = unknown>(url: string | Ref<string>, options?: Omit<UseApiOptions<T>, "method">): UseApiReturn<T>;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* API State Composable
|
|
122
|
+
*
|
|
123
|
+
* Managing API request state with minimal, essential properties
|
|
124
|
+
*
|
|
125
|
+
* Design principles:
|
|
126
|
+
* - Keep it simple: only data, loading, error, statusCode
|
|
127
|
+
* - No computed helpers: use raw state directly (more explicit, less magic)
|
|
128
|
+
* - No status enum: state can be derived from loading/error/data
|
|
129
|
+
*
|
|
130
|
+
* State derivation:
|
|
131
|
+
* - loading === true → request in progress
|
|
132
|
+
* - error !== null → request failed
|
|
133
|
+
* - data !== null && !error && !loading → request succeeded
|
|
134
|
+
* - !data && !error && !loading → idle
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
interface UseApiStateReturn<T = unknown> {
|
|
138
|
+
/** Response data */
|
|
139
|
+
data: Ref<T | null>;
|
|
140
|
+
/** Loading flag - true while request is in progress */
|
|
141
|
+
loading: Ref<boolean>;
|
|
142
|
+
/** Error object - null if no error */
|
|
143
|
+
error: Ref<ApiError | null>;
|
|
144
|
+
/** HTTP status code - useful for handling specific codes (404, 403, etc) */
|
|
145
|
+
statusCode: Ref<number | null>;
|
|
146
|
+
/** Full Axios response - includes headers, status, config, etc (optional, for advanced use cases) */
|
|
147
|
+
response: Ref<AxiosResponse<T> | null>;
|
|
148
|
+
/** Set data and clear error */
|
|
149
|
+
setData: (newData: T | null, fullResponse?: AxiosResponse<T> | null) => void;
|
|
150
|
+
/** Set error */
|
|
151
|
+
setError: (newError: ApiError | null) => void;
|
|
152
|
+
/** Set loading state */
|
|
153
|
+
setLoading: (isLoading: boolean) => void;
|
|
154
|
+
/** Set HTTP status code */
|
|
155
|
+
setStatusCode: (code: number | null) => void;
|
|
156
|
+
/** Reset to initial state */
|
|
157
|
+
reset: () => void;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Composable for API state management
|
|
161
|
+
*
|
|
162
|
+
* Simple, explicit state management without magic computed properties
|
|
163
|
+
* Use raw state directly in your components for clarity
|
|
164
|
+
*
|
|
165
|
+
* @example Basic usage (most common)
|
|
166
|
+
* ```ts
|
|
167
|
+
* const state = useApiState<User[]>();
|
|
168
|
+
*
|
|
169
|
+
* // Check states explicitly (no magic):
|
|
170
|
+
* if (state.loading.value) { ... }
|
|
171
|
+
* if (state.error.value) { ... }
|
|
172
|
+
* if (state.data.value) { ... }
|
|
173
|
+
* if (state.data.value?.length === 0) { ... } // Empty array check
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @example Advanced usage with full response
|
|
177
|
+
* ```ts
|
|
178
|
+
* const state = useApiState<User[]>();
|
|
179
|
+
*
|
|
180
|
+
* // Access response headers, status, etc:
|
|
181
|
+
* if (state.response.value) {
|
|
182
|
+
* console.log('Headers:', state.response.value.headers)
|
|
183
|
+
* console.log('Status:', state.response.value.status)
|
|
184
|
+
* console.log('Status Text:', state.response.value.statusText)
|
|
185
|
+
* console.log('Config:', state.response.value.config)
|
|
186
|
+
*
|
|
187
|
+
* // Example: Check rate limit headers
|
|
188
|
+
* const rateLimit = state.response.value.headers['x-ratelimit-remaining']
|
|
189
|
+
* if (rateLimit && parseInt(rateLimit) < 10) {
|
|
190
|
+
* console.warn('Low rate limit!')
|
|
191
|
+
* }
|
|
192
|
+
* }
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
interface UseApiStateOptions {
|
|
196
|
+
initialLoading?: boolean;
|
|
197
|
+
}
|
|
198
|
+
declare function useApiState<T = unknown>(initialData?: T | null, options?: UseApiStateOptions): UseApiStateReturn<T>;
|
|
199
|
+
|
|
200
|
+
declare function useAbortController(): {
|
|
201
|
+
signal: Readonly<vue.Ref<{
|
|
202
|
+
readonly aborted: boolean;
|
|
203
|
+
readonly onabort: ((this: AbortSignal, ev: Event) => any) | null;
|
|
204
|
+
readonly reason: any;
|
|
205
|
+
readonly throwIfAborted: () => void;
|
|
206
|
+
readonly addEventListener: {
|
|
207
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
208
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
209
|
+
};
|
|
210
|
+
readonly removeEventListener: {
|
|
211
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
212
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
213
|
+
};
|
|
214
|
+
readonly dispatchEvent: (event: Event) => boolean;
|
|
215
|
+
}, {
|
|
216
|
+
readonly aborted: boolean;
|
|
217
|
+
readonly onabort: ((this: AbortSignal, ev: Event) => any) | null;
|
|
218
|
+
readonly reason: any;
|
|
219
|
+
readonly throwIfAborted: () => void;
|
|
220
|
+
readonly addEventListener: {
|
|
221
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
222
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
223
|
+
};
|
|
224
|
+
readonly removeEventListener: {
|
|
225
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
226
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
227
|
+
};
|
|
228
|
+
readonly dispatchEvent: (event: Event) => boolean;
|
|
229
|
+
}>>;
|
|
230
|
+
abort: () => void;
|
|
231
|
+
getSignal: () => AbortSignal;
|
|
232
|
+
isAbortError: (error: unknown) => boolean;
|
|
233
|
+
abortCount: Readonly<vue.Ref<number, number>>;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
interface InterceptorOptions {
|
|
237
|
+
refreshUrl?: string;
|
|
238
|
+
onTokenRefreshFailed?: () => void;
|
|
239
|
+
extractTokens?: (response: AxiosResponse) => {
|
|
240
|
+
accessToken: string;
|
|
241
|
+
refreshToken?: string;
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
declare function setupInterceptors(axiosInstance: AxiosInstance, options?: InterceptorOptions): void;
|
|
245
|
+
|
|
246
|
+
interface CreateApiClientOptions extends CreateAxiosDefaults {
|
|
247
|
+
withAuth?: boolean;
|
|
248
|
+
authOptions?: InterceptorOptions;
|
|
249
|
+
}
|
|
250
|
+
declare function createApiClient(options?: CreateApiClientOptions): AxiosInstance;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Token Manager
|
|
254
|
+
*
|
|
255
|
+
* Centralized authorization token management
|
|
256
|
+
* Solves problems:
|
|
257
|
+
* - Tight coupling with localStorage
|
|
258
|
+
* - Easy to mock in tests
|
|
259
|
+
* - Single point of access to tokens
|
|
260
|
+
*/
|
|
261
|
+
interface AuthTokens {
|
|
262
|
+
accessToken: string;
|
|
263
|
+
refreshToken?: string;
|
|
264
|
+
expiresIn?: number;
|
|
265
|
+
}
|
|
266
|
+
interface TokenStorage {
|
|
267
|
+
getAccessToken(): string | null;
|
|
268
|
+
getRefreshToken(): string | null;
|
|
269
|
+
setTokens(tokens: AuthTokens): void;
|
|
270
|
+
clearTokens(): void;
|
|
271
|
+
getTokenExpiresAt(): number | null;
|
|
272
|
+
isTokenExpired(): boolean;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Token Manager class
|
|
276
|
+
* Singleton for token management
|
|
277
|
+
*/
|
|
278
|
+
declare class TokenManager {
|
|
279
|
+
private storage;
|
|
280
|
+
private refreshPromise;
|
|
281
|
+
constructor(storage?: TokenStorage);
|
|
282
|
+
/**
|
|
283
|
+
* Get access token
|
|
284
|
+
*/
|
|
285
|
+
getAccessToken(): string | null;
|
|
286
|
+
/**
|
|
287
|
+
* Get refresh token
|
|
288
|
+
*/
|
|
289
|
+
getRefreshToken(): string | null;
|
|
290
|
+
/**
|
|
291
|
+
* Save tokens
|
|
292
|
+
*/
|
|
293
|
+
setTokens(tokens: AuthTokens): void;
|
|
294
|
+
/**
|
|
295
|
+
* Clear tokens
|
|
296
|
+
*/
|
|
297
|
+
clearTokens(): void;
|
|
298
|
+
/**
|
|
299
|
+
* Check if token is expired
|
|
300
|
+
*/
|
|
301
|
+
isTokenExpired(): boolean;
|
|
302
|
+
/**
|
|
303
|
+
* Get token expiration time
|
|
304
|
+
*/
|
|
305
|
+
getTokenExpiresAt(): number | null;
|
|
306
|
+
/**
|
|
307
|
+
* Check if tokens exist
|
|
308
|
+
*/
|
|
309
|
+
hasTokens(): boolean;
|
|
310
|
+
/**
|
|
311
|
+
* Get Authorization header
|
|
312
|
+
*/
|
|
313
|
+
getAuthHeader(): string | null;
|
|
314
|
+
/**
|
|
315
|
+
* Set token refresh promise (to prevent race conditions)
|
|
316
|
+
*/
|
|
317
|
+
setRefreshPromise(promise: Promise<string | null>): void;
|
|
318
|
+
/**
|
|
319
|
+
* Get token refresh promise
|
|
320
|
+
*/
|
|
321
|
+
getRefreshPromise(): Promise<string | null> | null;
|
|
322
|
+
/**
|
|
323
|
+
* Clear token refresh promise
|
|
324
|
+
*/
|
|
325
|
+
clearRefreshPromise(): void;
|
|
326
|
+
/**
|
|
327
|
+
* Set storage (useful for tests)
|
|
328
|
+
*/
|
|
329
|
+
setStorage(storage: TokenStorage): void;
|
|
330
|
+
}
|
|
331
|
+
declare const tokenManager: TokenManager;
|
|
332
|
+
|
|
333
|
+
declare enum AuthEventType {
|
|
334
|
+
REFRESH_START = "AUTH_REFRESH_START",
|
|
335
|
+
REQUEST_QUEUED = "AUTH_REQUEST_QUEUED",
|
|
336
|
+
REFRESH_SUCCESS = "AUTH_REFRESH_SUCCESS",
|
|
337
|
+
REFRESH_ERROR = "AUTH_REFRESH_ERROR"
|
|
338
|
+
}
|
|
339
|
+
interface AuthEventPayload {
|
|
340
|
+
url?: string;
|
|
341
|
+
queueSize?: number;
|
|
342
|
+
error?: unknown;
|
|
343
|
+
timestamp?: string;
|
|
344
|
+
requestId?: string;
|
|
345
|
+
details?: Record<string, unknown>;
|
|
346
|
+
}
|
|
347
|
+
type AuthMonitorFn = (type: AuthEventType, payload: AuthEventPayload) => void;
|
|
348
|
+
declare function setAuthMonitor(fn: AuthMonitorFn): void;
|
|
349
|
+
|
|
350
|
+
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 };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import * as vue from 'vue';
|
|
2
|
+
import { MaybeRefOrGetter, Ref, App } from 'vue';
|
|
3
|
+
import { AxiosInstance, AxiosRequestConfig, AxiosResponse, CreateAxiosDefaults } from 'axios';
|
|
4
|
+
|
|
5
|
+
interface ApiError {
|
|
6
|
+
message: string;
|
|
7
|
+
status: number;
|
|
8
|
+
code?: string;
|
|
9
|
+
errors?: Record<string, string[]>;
|
|
10
|
+
details?: unknown;
|
|
11
|
+
}
|
|
12
|
+
type AuthMode = "default" | "public" | "optional";
|
|
13
|
+
interface ApiState<T = unknown> {
|
|
14
|
+
data: T | null;
|
|
15
|
+
loading: boolean;
|
|
16
|
+
error: ApiError | null;
|
|
17
|
+
statusCode: number | null;
|
|
18
|
+
}
|
|
19
|
+
interface ApiRequestConfig<D = unknown> extends Omit<AxiosRequestConfig<D>, "data"> {
|
|
20
|
+
data?: MaybeRefOrGetter<D> | D;
|
|
21
|
+
skipErrorNotification?: boolean;
|
|
22
|
+
authMode?: AuthMode;
|
|
23
|
+
retry?: boolean | number;
|
|
24
|
+
retryDelay?: number;
|
|
25
|
+
}
|
|
26
|
+
interface UseApiOptions<T = unknown, D = unknown> extends ApiRequestConfig<D> {
|
|
27
|
+
immediate?: boolean;
|
|
28
|
+
onSuccess?: (response: AxiosResponse<T>) => void;
|
|
29
|
+
onError?: (error: ApiError) => void;
|
|
30
|
+
onBefore?: () => void;
|
|
31
|
+
onFinish?: () => void;
|
|
32
|
+
initialData?: T;
|
|
33
|
+
debounce?: number;
|
|
34
|
+
useGlobalAbort?: boolean;
|
|
35
|
+
initialLoading?: boolean;
|
|
36
|
+
}
|
|
37
|
+
interface UseApiReturn<T = unknown, D = unknown> {
|
|
38
|
+
data: Ref<T | null>;
|
|
39
|
+
loading: Ref<boolean>;
|
|
40
|
+
error: Ref<ApiError | null>;
|
|
41
|
+
statusCode: Ref<number | null>;
|
|
42
|
+
response: Ref<AxiosResponse<T> | null>;
|
|
43
|
+
execute: (config?: ApiRequestConfig<D>) => Promise<T | null | undefined>;
|
|
44
|
+
abort: (message?: string) => void;
|
|
45
|
+
reset: () => void;
|
|
46
|
+
}
|
|
47
|
+
interface ApiPluginOptions {
|
|
48
|
+
axios: AxiosInstance;
|
|
49
|
+
onError?: (error: ApiError, originalError: any) => void;
|
|
50
|
+
globalOptions?: {
|
|
51
|
+
retry?: number | boolean;
|
|
52
|
+
retryDelay?: number;
|
|
53
|
+
useGlobalAbort?: boolean;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
interface AuthTokens$1 {
|
|
57
|
+
accessToken: string;
|
|
58
|
+
refreshToken?: string;
|
|
59
|
+
expiresIn?: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare function createApi(options: ApiPluginOptions): {
|
|
63
|
+
install(app: App): void;
|
|
64
|
+
};
|
|
65
|
+
declare function useApiConfig(): ApiPluginOptions;
|
|
66
|
+
|
|
67
|
+
declare function useApi<T = unknown, D = unknown>(url: string | Ref<string>, options?: UseApiOptions<T, D>): UseApiReturn<T, D>;
|
|
68
|
+
/**
|
|
69
|
+
* Helper for GET requests
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* const { data, loading, error } = useApiGet<User[]>('/users', {
|
|
74
|
+
* immediate: true
|
|
75
|
+
* })
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
declare function useApiGet<T = unknown>(url: string | Ref<string>, options?: Omit<UseApiOptions<T>, "method">): UseApiReturn<T>;
|
|
79
|
+
/**
|
|
80
|
+
* Helper for POST requests
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* const { data, loading, execute } = useApiPost<User, CreateUserDto>('/users')
|
|
85
|
+
* await execute({ data: { name: 'John' } })
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
declare function useApiPost<T = unknown, D = unknown>(url: string | Ref<string>, options?: Omit<UseApiOptions<T, D>, "method">): UseApiReturn<T, D>;
|
|
89
|
+
/**
|
|
90
|
+
* Helper for PUT requests
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* const { execute } = useApiPut<User, UpdateUserDto>('/users/1')
|
|
95
|
+
* await execute({ data: { name: 'John Doe' } })
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare function useApiPut<T = unknown, D = unknown>(url: string | Ref<string>, options?: Omit<UseApiOptions<T, D>, "method">): UseApiReturn<T, D>;
|
|
99
|
+
/**
|
|
100
|
+
* Helper for PATCH requests
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* const { execute } = useApiPatch<User, Partial<User>>('/users/1')
|
|
105
|
+
* await execute({ data: { name: 'John' } })
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare function useApiPatch<T = unknown, D = unknown>(url: string | Ref<string>, options?: Omit<UseApiOptions<T, D>, "method">): UseApiReturn<T, D>;
|
|
109
|
+
/**
|
|
110
|
+
* Helper for DELETE requests
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* const { execute } = useApiDelete('/users/1')
|
|
115
|
+
* await execute()
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
declare function useApiDelete<T = unknown>(url: string | Ref<string>, options?: Omit<UseApiOptions<T>, "method">): UseApiReturn<T>;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* API State Composable
|
|
122
|
+
*
|
|
123
|
+
* Managing API request state with minimal, essential properties
|
|
124
|
+
*
|
|
125
|
+
* Design principles:
|
|
126
|
+
* - Keep it simple: only data, loading, error, statusCode
|
|
127
|
+
* - No computed helpers: use raw state directly (more explicit, less magic)
|
|
128
|
+
* - No status enum: state can be derived from loading/error/data
|
|
129
|
+
*
|
|
130
|
+
* State derivation:
|
|
131
|
+
* - loading === true → request in progress
|
|
132
|
+
* - error !== null → request failed
|
|
133
|
+
* - data !== null && !error && !loading → request succeeded
|
|
134
|
+
* - !data && !error && !loading → idle
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
interface UseApiStateReturn<T = unknown> {
|
|
138
|
+
/** Response data */
|
|
139
|
+
data: Ref<T | null>;
|
|
140
|
+
/** Loading flag - true while request is in progress */
|
|
141
|
+
loading: Ref<boolean>;
|
|
142
|
+
/** Error object - null if no error */
|
|
143
|
+
error: Ref<ApiError | null>;
|
|
144
|
+
/** HTTP status code - useful for handling specific codes (404, 403, etc) */
|
|
145
|
+
statusCode: Ref<number | null>;
|
|
146
|
+
/** Full Axios response - includes headers, status, config, etc (optional, for advanced use cases) */
|
|
147
|
+
response: Ref<AxiosResponse<T> | null>;
|
|
148
|
+
/** Set data and clear error */
|
|
149
|
+
setData: (newData: T | null, fullResponse?: AxiosResponse<T> | null) => void;
|
|
150
|
+
/** Set error */
|
|
151
|
+
setError: (newError: ApiError | null) => void;
|
|
152
|
+
/** Set loading state */
|
|
153
|
+
setLoading: (isLoading: boolean) => void;
|
|
154
|
+
/** Set HTTP status code */
|
|
155
|
+
setStatusCode: (code: number | null) => void;
|
|
156
|
+
/** Reset to initial state */
|
|
157
|
+
reset: () => void;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Composable for API state management
|
|
161
|
+
*
|
|
162
|
+
* Simple, explicit state management without magic computed properties
|
|
163
|
+
* Use raw state directly in your components for clarity
|
|
164
|
+
*
|
|
165
|
+
* @example Basic usage (most common)
|
|
166
|
+
* ```ts
|
|
167
|
+
* const state = useApiState<User[]>();
|
|
168
|
+
*
|
|
169
|
+
* // Check states explicitly (no magic):
|
|
170
|
+
* if (state.loading.value) { ... }
|
|
171
|
+
* if (state.error.value) { ... }
|
|
172
|
+
* if (state.data.value) { ... }
|
|
173
|
+
* if (state.data.value?.length === 0) { ... } // Empty array check
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @example Advanced usage with full response
|
|
177
|
+
* ```ts
|
|
178
|
+
* const state = useApiState<User[]>();
|
|
179
|
+
*
|
|
180
|
+
* // Access response headers, status, etc:
|
|
181
|
+
* if (state.response.value) {
|
|
182
|
+
* console.log('Headers:', state.response.value.headers)
|
|
183
|
+
* console.log('Status:', state.response.value.status)
|
|
184
|
+
* console.log('Status Text:', state.response.value.statusText)
|
|
185
|
+
* console.log('Config:', state.response.value.config)
|
|
186
|
+
*
|
|
187
|
+
* // Example: Check rate limit headers
|
|
188
|
+
* const rateLimit = state.response.value.headers['x-ratelimit-remaining']
|
|
189
|
+
* if (rateLimit && parseInt(rateLimit) < 10) {
|
|
190
|
+
* console.warn('Low rate limit!')
|
|
191
|
+
* }
|
|
192
|
+
* }
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
interface UseApiStateOptions {
|
|
196
|
+
initialLoading?: boolean;
|
|
197
|
+
}
|
|
198
|
+
declare function useApiState<T = unknown>(initialData?: T | null, options?: UseApiStateOptions): UseApiStateReturn<T>;
|
|
199
|
+
|
|
200
|
+
declare function useAbortController(): {
|
|
201
|
+
signal: Readonly<vue.Ref<{
|
|
202
|
+
readonly aborted: boolean;
|
|
203
|
+
readonly onabort: ((this: AbortSignal, ev: Event) => any) | null;
|
|
204
|
+
readonly reason: any;
|
|
205
|
+
readonly throwIfAborted: () => void;
|
|
206
|
+
readonly addEventListener: {
|
|
207
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
208
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
209
|
+
};
|
|
210
|
+
readonly removeEventListener: {
|
|
211
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
212
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
213
|
+
};
|
|
214
|
+
readonly dispatchEvent: (event: Event) => boolean;
|
|
215
|
+
}, {
|
|
216
|
+
readonly aborted: boolean;
|
|
217
|
+
readonly onabort: ((this: AbortSignal, ev: Event) => any) | null;
|
|
218
|
+
readonly reason: any;
|
|
219
|
+
readonly throwIfAborted: () => void;
|
|
220
|
+
readonly addEventListener: {
|
|
221
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
222
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
223
|
+
};
|
|
224
|
+
readonly removeEventListener: {
|
|
225
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
226
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
227
|
+
};
|
|
228
|
+
readonly dispatchEvent: (event: Event) => boolean;
|
|
229
|
+
}>>;
|
|
230
|
+
abort: () => void;
|
|
231
|
+
getSignal: () => AbortSignal;
|
|
232
|
+
isAbortError: (error: unknown) => boolean;
|
|
233
|
+
abortCount: Readonly<vue.Ref<number, number>>;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
interface InterceptorOptions {
|
|
237
|
+
refreshUrl?: string;
|
|
238
|
+
onTokenRefreshFailed?: () => void;
|
|
239
|
+
extractTokens?: (response: AxiosResponse) => {
|
|
240
|
+
accessToken: string;
|
|
241
|
+
refreshToken?: string;
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
declare function setupInterceptors(axiosInstance: AxiosInstance, options?: InterceptorOptions): void;
|
|
245
|
+
|
|
246
|
+
interface CreateApiClientOptions extends CreateAxiosDefaults {
|
|
247
|
+
withAuth?: boolean;
|
|
248
|
+
authOptions?: InterceptorOptions;
|
|
249
|
+
}
|
|
250
|
+
declare function createApiClient(options?: CreateApiClientOptions): AxiosInstance;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Token Manager
|
|
254
|
+
*
|
|
255
|
+
* Centralized authorization token management
|
|
256
|
+
* Solves problems:
|
|
257
|
+
* - Tight coupling with localStorage
|
|
258
|
+
* - Easy to mock in tests
|
|
259
|
+
* - Single point of access to tokens
|
|
260
|
+
*/
|
|
261
|
+
interface AuthTokens {
|
|
262
|
+
accessToken: string;
|
|
263
|
+
refreshToken?: string;
|
|
264
|
+
expiresIn?: number;
|
|
265
|
+
}
|
|
266
|
+
interface TokenStorage {
|
|
267
|
+
getAccessToken(): string | null;
|
|
268
|
+
getRefreshToken(): string | null;
|
|
269
|
+
setTokens(tokens: AuthTokens): void;
|
|
270
|
+
clearTokens(): void;
|
|
271
|
+
getTokenExpiresAt(): number | null;
|
|
272
|
+
isTokenExpired(): boolean;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Token Manager class
|
|
276
|
+
* Singleton for token management
|
|
277
|
+
*/
|
|
278
|
+
declare class TokenManager {
|
|
279
|
+
private storage;
|
|
280
|
+
private refreshPromise;
|
|
281
|
+
constructor(storage?: TokenStorage);
|
|
282
|
+
/**
|
|
283
|
+
* Get access token
|
|
284
|
+
*/
|
|
285
|
+
getAccessToken(): string | null;
|
|
286
|
+
/**
|
|
287
|
+
* Get refresh token
|
|
288
|
+
*/
|
|
289
|
+
getRefreshToken(): string | null;
|
|
290
|
+
/**
|
|
291
|
+
* Save tokens
|
|
292
|
+
*/
|
|
293
|
+
setTokens(tokens: AuthTokens): void;
|
|
294
|
+
/**
|
|
295
|
+
* Clear tokens
|
|
296
|
+
*/
|
|
297
|
+
clearTokens(): void;
|
|
298
|
+
/**
|
|
299
|
+
* Check if token is expired
|
|
300
|
+
*/
|
|
301
|
+
isTokenExpired(): boolean;
|
|
302
|
+
/**
|
|
303
|
+
* Get token expiration time
|
|
304
|
+
*/
|
|
305
|
+
getTokenExpiresAt(): number | null;
|
|
306
|
+
/**
|
|
307
|
+
* Check if tokens exist
|
|
308
|
+
*/
|
|
309
|
+
hasTokens(): boolean;
|
|
310
|
+
/**
|
|
311
|
+
* Get Authorization header
|
|
312
|
+
*/
|
|
313
|
+
getAuthHeader(): string | null;
|
|
314
|
+
/**
|
|
315
|
+
* Set token refresh promise (to prevent race conditions)
|
|
316
|
+
*/
|
|
317
|
+
setRefreshPromise(promise: Promise<string | null>): void;
|
|
318
|
+
/**
|
|
319
|
+
* Get token refresh promise
|
|
320
|
+
*/
|
|
321
|
+
getRefreshPromise(): Promise<string | null> | null;
|
|
322
|
+
/**
|
|
323
|
+
* Clear token refresh promise
|
|
324
|
+
*/
|
|
325
|
+
clearRefreshPromise(): void;
|
|
326
|
+
/**
|
|
327
|
+
* Set storage (useful for tests)
|
|
328
|
+
*/
|
|
329
|
+
setStorage(storage: TokenStorage): void;
|
|
330
|
+
}
|
|
331
|
+
declare const tokenManager: TokenManager;
|
|
332
|
+
|
|
333
|
+
declare enum AuthEventType {
|
|
334
|
+
REFRESH_START = "AUTH_REFRESH_START",
|
|
335
|
+
REQUEST_QUEUED = "AUTH_REQUEST_QUEUED",
|
|
336
|
+
REFRESH_SUCCESS = "AUTH_REFRESH_SUCCESS",
|
|
337
|
+
REFRESH_ERROR = "AUTH_REFRESH_ERROR"
|
|
338
|
+
}
|
|
339
|
+
interface AuthEventPayload {
|
|
340
|
+
url?: string;
|
|
341
|
+
queueSize?: number;
|
|
342
|
+
error?: unknown;
|
|
343
|
+
timestamp?: string;
|
|
344
|
+
requestId?: string;
|
|
345
|
+
details?: Record<string, unknown>;
|
|
346
|
+
}
|
|
347
|
+
type AuthMonitorFn = (type: AuthEventType, payload: AuthEventPayload) => void;
|
|
348
|
+
declare function setAuthMonitor(fn: AuthMonitorFn): void;
|
|
349
|
+
|
|
350
|
+
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 };
|