vue-api-kit 1.6.0 → 1.8.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 CHANGED
@@ -251,7 +251,8 @@ const api = createApiClient({
251
251
  headers: {
252
252
  'Authorization': 'Bearer token'
253
253
  },
254
- withCredentials: true,
254
+ withCredentials: true, // Enable cookies
255
+ withXSRFToken: true, // Enable automatic XSRF token handling
255
256
 
256
257
  // CSRF Token Protection
257
258
  csrfRefreshEndpoint: '/sanctum/csrf-cookie', // Auto-refresh CSRF token on 403/419 errors
@@ -289,6 +290,133 @@ const api = createApiClient({
289
290
  });
290
291
  ```
291
292
 
293
+ ## 🎯 Per-Query and Per-Mutation Request Interceptors
294
+
295
+ In addition to global request interceptors, you can define `onBeforeRequest` hooks for individual queries and mutations. This is useful when you need to append specific headers or modify the request configuration for certain endpoints only.
296
+
297
+ ### Query-Level onBeforeRequest
298
+
299
+ You can define `onBeforeRequest` in two ways for queries:
300
+
301
+ **1. In the query definition:**
302
+
303
+ ```typescript
304
+ const api = createApiClient({
305
+ baseURL: 'https://api.example.com',
306
+ queries: {
307
+ getUser: {
308
+ path: '/users/{id}',
309
+ params: z.object({ id: z.number() }),
310
+ response: z.object({ id: z.number(), name: z.string() }),
311
+ // Query-level interceptor
312
+ onBeforeRequest: async (config) => {
313
+ config.headers['X-Custom-Query-Header'] = 'special-value';
314
+ return config;
315
+ }
316
+ }
317
+ }
318
+ });
319
+ ```
320
+
321
+ **2. In the query options when calling it:**
322
+
323
+ ```typescript
324
+ const { result, isLoading } = api.query.getUser({
325
+ params: { id: 1 },
326
+ // Runtime interceptor
327
+ onBeforeRequest: async (config) => {
328
+ const token = await getAuthToken();
329
+ config.headers.Authorization = `Bearer ${token}`;
330
+ return config;
331
+ }
332
+ });
333
+ ```
334
+
335
+ ### Mutation-Level onBeforeRequest
336
+
337
+ Similarly, you can define `onBeforeRequest` for mutations:
338
+
339
+ **1. In the mutation definition:**
340
+
341
+ ```typescript
342
+ const api = createApiClient({
343
+ baseURL: 'https://api.example.com',
344
+ mutations: {
345
+ createUser: {
346
+ method: 'POST',
347
+ path: '/users',
348
+ data: z.object({ name: z.string(), email: z.string() }),
349
+ response: z.object({ id: z.number(), name: z.string() }),
350
+ // Mutation-level interceptor
351
+ onBeforeRequest: async (config) => {
352
+ config.headers['X-Action'] = 'create-user';
353
+ return config;
354
+ }
355
+ }
356
+ }
357
+ });
358
+ ```
359
+
360
+ **2. In the mutation options when calling it:**
361
+
362
+ ```typescript
363
+ const { mutate } = api.mutation.createUser({
364
+ // Runtime interceptor
365
+ onBeforeRequest: async (config) => {
366
+ const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content;
367
+ if (csrfToken) {
368
+ config.headers['X-CSRF-Token'] = csrfToken;
369
+ }
370
+ return config;
371
+ }
372
+ });
373
+
374
+ await mutate({ data: { name: 'John', email: 'john@example.com' } });
375
+ ```
376
+
377
+ ### Execution Order
378
+
379
+ When multiple `onBeforeRequest` hooks are defined, they execute in the following order:
380
+
381
+ 1. **Global interceptor** (defined in `createApiClient` options) - Applied via axios interceptor
382
+ 2. **Query/Mutation definition interceptor** (defined in query/mutation object)
383
+ 3. **Options interceptor** (defined when calling the query/mutation)
384
+
385
+ Each hook can modify the config, and later hooks can see and override changes made by earlier hooks.
386
+
387
+ ### Use Cases
388
+
389
+ - **Authentication**: Add tokens for specific endpoints that require authentication
390
+ - **Custom Headers**: Append API keys, correlation IDs, or feature flags for specific requests
391
+ - **Request Transformation**: Modify request data or parameters before sending
392
+ - **Conditional Logic**: Apply different configurations based on runtime conditions
393
+ - **Debugging**: Add request IDs or trace headers for specific endpoints
394
+
395
+ ### Example: Dynamic Authorization
396
+
397
+ ```typescript
398
+ const api = createApiClient({
399
+ baseURL: 'https://api.example.com',
400
+ queries: {
401
+ getProtectedData: {
402
+ path: '/protected/data',
403
+ response: z.object({ data: z.string() }),
404
+ onBeforeRequest: async (config) => {
405
+ // This query always needs fresh token
406
+ const token = await refreshAndGetToken();
407
+ config.headers.Authorization = `Bearer ${token}`;
408
+ return config;
409
+ }
410
+ },
411
+ getPublicData: {
412
+ path: '/public/data',
413
+ response: z.object({ data: z.string() })
414
+ // No onBeforeRequest needed for public endpoint
415
+ }
416
+ }
417
+ });
418
+ ```
419
+
292
420
  ## 🧩 Modular API Definitions
293
421
 
294
422
  For large applications, you can organize your API definitions into separate files and merge them together with full type safety.
@@ -459,9 +587,10 @@ The client includes built-in CSRF token protection, perfect for Laravel Sanctum
459
587
  ### How it works
460
588
 
461
589
  **Automatic XSRF Token Handling:**
462
- 1. When `withCredentials: true`, the client automatically reads the `XSRF-TOKEN` cookie
463
- 2. Decodes and sends it as `X-XSRF-TOKEN` header with every request
464
- 3. This satisfies Laravel Sanctum's CSRF protection requirements
590
+ 1. Set `withCredentials: true` to enable cookie-based authentication
591
+ 2. Set `withXSRFToken: true` to enable automatic XSRF token handling
592
+ 3. Axios automatically reads `XSRF-TOKEN` cookie and sends it as `X-XSRF-TOKEN` header
593
+ 4. This satisfies Laravel Sanctum's CSRF protection requirements
465
594
 
466
595
  **Automatic CSRF Refresh:**
467
596
  1. Detects CSRF errors (403 or 419 status codes)
@@ -474,9 +603,9 @@ The client includes built-in CSRF token protection, perfect for Laravel Sanctum
474
603
  ```typescript
475
604
  const api = createApiClient({
476
605
  baseURL: 'https://api.example.com',
477
- withCredentials: true, // Required! Enables cookies AND auto XSRF header
606
+ withCredentials: true, // Enable cookies for authentication
607
+ withXSRFToken: true, // Enable automatic XSRF token handling
478
608
  csrfRefreshEndpoint: '/sanctum/csrf-cookie', // Laravel Sanctum endpoint
479
-
480
609
  queries: { /* ... */ },
481
610
  mutations: { /* ... */ }
482
611
  });
@@ -491,10 +620,9 @@ import { z } from 'zod';
491
620
 
492
621
  export const api = createApiClient({
493
622
  baseURL: 'https://api.example.com',
494
- withCredentials: true, // Enables cookies AND automatic XSRF-TOKEN header
495
- csrfRefreshEndpoint: '/sanctum/csrf-cookie', // Laravel's CSRF endpoint
496
-
497
- mutations: {
623
+ withCredentials: true, // Enables cookies
624
+ withXSRFToken: true, // Enables automatic XSRF-TOKEN header
625
+ csrfRefreshEndpoint: '/sanctum/csrf-cookie', // Laravel's CSRF endpoint mutations: {
498
626
  login: {
499
627
  method: 'POST',
500
628
  path: '/login',
@@ -524,7 +652,8 @@ export const api = createApiClient({
524
652
 
525
653
  ### Benefits
526
654
 
527
- - ✅ **Automatic XSRF Header**: Cookie automatically sent as `X-XSRF-TOKEN` header
655
+ - ✅ **Separate Options**: `withCredentials` and `withXSRFToken` can be configured independently
656
+ - ✅ **Built-in XSRF Support**: Axios `withXSRFToken` handles token automatically
528
657
  - ✅ **Automatic Recovery**: No manual token refresh needed
529
658
  - ✅ **Seamless UX**: Users don't experience authentication errors
530
659
  - ✅ **Race Condition Safe**: Multiple simultaneous requests share the same refresh
@@ -533,7 +662,9 @@ export const api = createApiClient({
533
662
 
534
663
  ### Important Notes
535
664
 
536
- 1. **withCredentials is required**: Set `withCredentials: true` to enable both cookies and automatic XSRF token handling
665
+ 1. **Two separate options**:
666
+ - `withCredentials: true` - Enables sending cookies with requests
667
+ - `withXSRFToken: true` - Enables automatic XSRF token header handling
537
668
  2. **Cookie Domain**: Ensure your API sets cookies with the correct domain (e.g., `.localhost` for local development)
538
669
  3. **CORS Configuration**: Your Laravel backend must allow credentials:
539
670
  ```php
@@ -42,6 +42,7 @@ export interface ApiQuery<TParams extends ZodType<any> | undefined = ZodType<any
42
42
  params?: TParams;
43
43
  data?: TData;
44
44
  response?: TResponse;
45
+ onBeforeRequest?: (config: InternalAxiosRequestConfig<any>) => Promise<any> | void | any;
45
46
  }
46
47
  /**
47
48
  * Defines a mutation (POST, PUT, PATCH, DELETE) endpoint configuration
@@ -63,6 +64,7 @@ export interface ApiMutation<TData extends ZodType<any> | undefined = ZodType<an
63
64
  data?: TData;
64
65
  response?: TResponse;
65
66
  isMultipart?: boolean;
67
+ onBeforeRequest?: (config: InternalAxiosRequestConfig<any>) => Promise<any> | void | any;
66
68
  }
67
69
  /**
68
70
  * Configuration options for creating an API client
@@ -83,6 +85,7 @@ export interface ApiClientOptions<Q extends Record<string, ApiQuery> = Record<st
83
85
  baseURL: string;
84
86
  headers?: Record<string, string>;
85
87
  withCredentials?: boolean;
88
+ withXSRFToken?: boolean;
86
89
  csrfRefreshEndpoint?: string;
87
90
  queries?: Q;
88
91
  mutations?: M;
@@ -125,6 +128,7 @@ export interface UseQueryOptions<TParams = any, TData = any, TResult = any> {
125
128
  onResult?: (result: TResult) => void;
126
129
  onError?: (error: AxiosError | ZodError | Error) => void;
127
130
  onZodError?: (issues: Omit<$ZodIssue, "input">[]) => void;
131
+ onBeforeRequest?: (config: InternalAxiosRequestConfig<any>) => Promise<any> | void | any;
128
132
  }
129
133
  /**
130
134
  * Options for configuring a mutation hook
@@ -141,6 +145,7 @@ export interface UseMutationOptions<TResult = any> {
141
145
  onError?: (error: AxiosError | ZodError | Error) => void;
142
146
  onZodError?: (issues: Omit<$ZodIssue, "input">[]) => void;
143
147
  onUploadProgress?: (progress: number) => void;
148
+ onBeforeRequest?: (config: InternalAxiosRequestConfig<any>) => Promise<any> | void | any;
144
149
  }
145
150
  /**
146
151
  * Return type from a query hook
package/dist/index.js CHANGED
@@ -1,190 +1,204 @@
1
1
  import { ZodError as S } from "zod";
2
- import * as $ from "zod";
2
+ import * as U from "zod";
3
3
  import L, { AxiosError as F } from "axios";
4
- import { AxiosError as G } from "axios";
5
- import { nextTick as N, ref as m, onMounted as T, watch as k, onBeforeUnmount as x } from "vue";
6
- import { debounce as U } from "lodash-es";
7
- function z(t) {
8
- const p = L.create({
9
- baseURL: t.baseURL,
4
+ import { AxiosError as K } from "axios";
5
+ import { nextTick as T, ref as m, onMounted as k, watch as Z, onBeforeUnmount as x } from "vue";
6
+ import { debounce as N } from "lodash-es";
7
+ function V(s) {
8
+ const R = L.create({
9
+ baseURL: s.baseURL,
10
10
  headers: {
11
11
  "Content-Type": "application/json",
12
12
  Accept: "application/json",
13
- ...t.headers
13
+ ...s.headers
14
14
  },
15
- withCredentials: t.withCredentials ?? !1
15
+ withCredentials: s.withCredentials ?? !1,
16
+ withXSRFToken: s.withXSRFToken ?? !1
16
17
  });
17
- let C = !1, q = null;
18
- t.onBeforeRequest && p.interceptors.request.use(
18
+ let C = !1, j = null;
19
+ s.onBeforeRequest && R.interceptors.request.use(
19
20
  async (e) => {
20
21
  try {
21
- return await t.onBeforeRequest(e) || e;
22
+ return await s.onBeforeRequest(e) || e;
22
23
  } catch (r) {
23
24
  return Promise.reject(r);
24
25
  }
25
26
  },
26
27
  (e) => Promise.reject(e)
27
- ), t.onStartRequest && p.interceptors.request.use(
28
+ ), s.onStartRequest && R.interceptors.request.use(
28
29
  async (e) => {
29
30
  try {
30
- return await t.onStartRequest(), e;
31
+ return await s.onStartRequest(), e;
31
32
  } catch (r) {
32
33
  return Promise.reject(r);
33
34
  }
34
35
  },
35
36
  (e) => Promise.reject(e)
36
- ), t.onFinishRequest && p.interceptors.response.use(
37
- (e) => (t.onFinishRequest(), e),
38
- (e) => (t.onFinishRequest(), Promise.reject(e))
39
- ), p.interceptors.request.use((e) => {
37
+ ), s.onFinishRequest && R.interceptors.response.use(
38
+ (e) => (s.onFinishRequest(), e),
39
+ (e) => (s.onFinishRequest(), Promise.reject(e))
40
+ ), R.interceptors.request.use((e) => {
40
41
  if (!e.url) return e;
41
42
  const r = (a) => {
42
43
  if (a)
43
- for (const [s, R] of Object.entries(a)) {
44
- const l = `{${s}}`;
45
- e.url.includes(l) && (e.url = e.url.replace(
46
- l,
47
- encodeURIComponent(String(R))
48
- ), delete a[s]);
44
+ for (const [t, p] of Object.entries(a)) {
45
+ const f = `{${t}}`;
46
+ e.url.includes(f) && (e.url = e.url.replace(
47
+ f,
48
+ encodeURIComponent(String(p))
49
+ ), delete a[t]);
49
50
  }
50
51
  };
51
52
  return e.method !== "get" && e.data?.params && r(e.data.params), r(e.params), e;
52
- }), t.withCredentials && p.interceptors.request.use((e) => {
53
- const r = document.cookie.split("; ").find((a) => a.startsWith("XSRF-TOKEN="))?.split("=")[1];
54
- return r && (e.headers["X-XSRF-TOKEN"] = decodeURIComponent(r)), e;
55
- }), t.csrfRefreshEndpoint && p.interceptors.response.use(
53
+ }), s.csrfRefreshEndpoint && R.interceptors.response.use(
56
54
  (e) => e,
57
55
  async (e) => {
58
56
  const r = e.config;
59
- if (r?.url === t.csrfRefreshEndpoint)
57
+ if (r?.url === s.csrfRefreshEndpoint)
60
58
  return Promise.reject(e);
61
59
  if (e.response && (e.response.status === 403 || e.response.status === 419) && !r?._retry) {
62
60
  r._retry = !0;
63
61
  try {
64
- return C && q ? await q : (C = !0, q = p.get(t.csrfRefreshEndpoint).then(() => {
65
- C = !1, q = null;
66
- }), await q), p.request(r);
62
+ return C && j ? await j : (C = !0, j = R.get(s.csrfRefreshEndpoint).then(() => {
63
+ C = !1, j = null;
64
+ }), await j), R.request(r);
67
65
  } catch (a) {
68
- return C = !1, q = null, Promise.reject(a);
66
+ return C = !1, j = null, Promise.reject(a);
69
67
  }
70
68
  }
71
69
  return Promise.reject(e);
72
70
  }
73
- ), p.interceptors.response.use(
71
+ ), R.interceptors.response.use(
74
72
  (e) => e,
75
- (e) => (N(() => {
73
+ (e) => (T(() => {
76
74
  e.code;
77
75
  }), Promise.reject(e))
78
76
  );
79
- const P = t.queries ?? {}, M = {};
77
+ const P = s.queries ?? {}, M = {};
80
78
  for (const e in P) {
81
79
  const r = P[e];
82
80
  r && (M[e] = (a) => {
83
- let s;
84
- a && typeof a == "object" && ("loadOnMount" in a || "debounce" in a || "onResult" in a || "onError" in a || "data" in a ? s = a : s = { params: a });
85
- const R = m(), l = m(), v = m(), g = m(!1), y = m(!1), A = m(!0);
86
- let j = new AbortController();
87
- const u = () => {
88
- j?.abort(), j = new AbortController();
89
- }, c = async () => {
90
- g.value && u(), g.value = !0, l.value = void 0;
81
+ let t;
82
+ a && typeof a == "object" && ("loadOnMount" in a || "debounce" in a || "onResult" in a || "onError" in a || "onZodError" in a || "onBeforeRequest" in a || "data" in a ? t = a : t = { params: a });
83
+ const p = m(), f = m(), g = m(), E = m(!1), y = m(!1), A = m(!0);
84
+ let b = new AbortController();
85
+ const i = () => {
86
+ b?.abort(), b = new AbortController();
87
+ }, l = async () => {
88
+ E.value && i(), E.value = !0, f.value = void 0;
91
89
  try {
92
- r.params && s?.params && r.params.parse(s.params);
93
- let o = s?.data;
90
+ r.params && t?.params && r.params.parse(t.params);
91
+ let o = t?.data;
94
92
  r.data && o && r.data.parse(o);
95
- const f = {
93
+ const u = {
96
94
  method: r.method ?? "GET",
97
95
  url: r.path,
98
- params: s?.params,
99
- signal: j.signal
96
+ params: t?.params,
97
+ signal: b.signal
100
98
  };
101
- r.method === "POST" && o && (f.data = o);
102
- const n = await p.request(f), i = r.response ? r.response.parse(n.data) : n.data;
103
- R.value = i, s?.onResult?.(i);
99
+ if (r.method === "POST" && o && (u.data = o), r.onBeforeRequest) {
100
+ const c = await r.onBeforeRequest(u);
101
+ c !== void 0 && Object.assign(u, c);
102
+ }
103
+ if (t?.onBeforeRequest) {
104
+ const c = await t.onBeforeRequest(u);
105
+ c !== void 0 && Object.assign(u, c);
106
+ }
107
+ const h = await R.request(u), n = r.response ? r.response.parse(h.data) : h.data;
108
+ p.value = n, t?.onResult?.(n);
104
109
  } catch (o) {
105
110
  if (o instanceof F) {
106
111
  if (o.code !== "ERR_CANCELED") {
107
- const f = o.response?.data?.message || o.message || "An error occurred", n = o.response?.status, i = o.code, h = o.response?.data;
108
- l.value = f, s?.onError?.(o), t.onErrorRequest?.({ message: f, status: n, code: i, data: h });
112
+ const u = o.response?.data?.message || o.message || "An error occurred", h = o.response?.status, n = o.code, c = o.response?.data;
113
+ f.value = u, t?.onError?.(o), s.onErrorRequest?.({ message: u, status: h, code: n, data: c });
109
114
  }
110
115
  } else if (o instanceof S) {
111
- v.value = o.issues || [];
112
- const n = `Validation error: ${v.value.map(
113
- (i) => `${i.path.join(".")}: ${i.message}`
116
+ g.value = o.issues || [];
117
+ const h = `Validation error: ${g.value.map(
118
+ (n) => `${n.path.join(".")}: ${n.message}`
114
119
  ).join(", ")}`;
115
- l.value = n, s?.onError?.(o), s?.onZodError?.(v.value), t.onErrorRequest?.({ message: n, code: "VALIDATION_ERROR" }), t.onZodError && t.onZodError(v.value);
120
+ f.value = h, t?.onError?.(o), t?.onZodError?.(g.value), s.onErrorRequest?.({ message: h, code: "VALIDATION_ERROR" }), s.onZodError && s.onZodError(g.value);
116
121
  } else {
117
- const f = o.message || "An error occurred";
118
- l.value = f, s?.onError?.(f), t.onErrorRequest?.({ message: f });
122
+ const u = o.message || "An error occurred";
123
+ f.value = u, t?.onError?.(u), s.onErrorRequest?.({ message: u });
119
124
  }
120
125
  } finally {
121
- g.value = !1, y.value = !0;
126
+ E.value = !1, y.value = !0;
122
127
  }
123
- }, E = s?.debounce ? U(c, s.debounce) : c;
128
+ }, v = t?.debounce ? N(l, t.debounce) : l;
124
129
  let d = null;
125
- return (s?.params || s?.data) && (T(() => {
126
- d && d(), d = k(
127
- () => JSON.stringify({ params: s.params, data: s.data }),
130
+ return (t?.params || t?.data) && (k(() => {
131
+ d && d(), d = Z(
132
+ () => JSON.stringify({ params: t.params, data: t.data }),
128
133
  () => {
129
- E();
134
+ v();
130
135
  },
131
136
  { immediate: !1 }
132
137
  );
133
138
  }), x(() => {
134
- d && d(), j?.abort();
135
- })), (s?.loadOnMount === void 0 || s.loadOnMount) && !y.value && (A.value ? (A.value = !1, c()) : E()), { result: R, errorMessage: l, zodErrors: v, isLoading: g, isDone: y, refetch: c };
139
+ d && d(), b?.abort();
140
+ })), (t?.loadOnMount === void 0 || t.loadOnMount) && !y.value && (A.value ? (A.value = !1, l()) : v()), { result: p, errorMessage: f, zodErrors: g, isLoading: E, isDone: y, refetch: l };
136
141
  });
137
142
  }
138
- const D = t.mutations ?? {}, w = {};
139
- for (const e in D) {
140
- const r = D[e];
141
- r && (w[e] = (a) => {
142
- const s = m(), R = m(), l = m(), v = m(!1), g = m(!1), y = m(0);
143
- return { result: s, errorMessage: R, zodErrors: l, isLoading: v, isDone: g, uploadProgress: y, mutate: async (j) => {
144
- if (!v.value) {
145
- v.value = !0, R.value = void 0, y.value = 0;
143
+ const B = s.mutations ?? {}, D = {};
144
+ for (const e in B) {
145
+ const r = B[e];
146
+ r && (D[e] = (a) => {
147
+ const t = m(), p = m(), f = m(), g = m(!1), E = m(!1), y = m(0);
148
+ return { result: t, errorMessage: p, zodErrors: f, isLoading: g, isDone: E, uploadProgress: y, mutate: async (b) => {
149
+ if (!g.value) {
150
+ g.value = !0, p.value = void 0, y.value = 0;
146
151
  try {
147
- const { data: u = {}, params: c } = j ?? {};
148
- let E = u ?? {}, d = {};
152
+ const { data: i = {}, params: l } = b ?? {};
153
+ let v = i ?? {}, d = {};
149
154
  if (r.isMultipart) {
150
155
  const n = new FormData();
151
- for (const [i, h] of Object.entries(u))
152
- h instanceof File || h instanceof Blob ? n.append(i, h) : Array.isArray(h) ? h.forEach((b) => {
153
- b instanceof File || b instanceof Blob ? n.append(i, b) : n.append(i, JSON.stringify(b));
154
- }) : typeof h == "object" && h !== null ? n.append(i, JSON.stringify(h)) : n.append(i, String(h));
155
- E = n, d["Content-Type"] = "multipart/form-data";
156
- } else r.data && r.data.parse(u);
157
- r.params && c && r.params.parse(c);
158
- const o = await p.request({
156
+ for (const [c, q] of Object.entries(i))
157
+ q instanceof File || q instanceof Blob ? n.append(c, q) : Array.isArray(q) ? q.forEach((w) => {
158
+ w instanceof File || w instanceof Blob ? n.append(c, w) : n.append(c, JSON.stringify(w));
159
+ }) : typeof q == "object" && q !== null ? n.append(c, JSON.stringify(q)) : n.append(c, String(q));
160
+ v = n, d["Content-Type"] = "multipart/form-data";
161
+ } else r.data && r.data.parse(i);
162
+ r.params && l && r.params.parse(l);
163
+ const o = {
159
164
  method: r.method,
160
165
  url: r.path,
161
- data: E,
162
- params: c,
166
+ data: v,
167
+ params: l,
163
168
  headers: d,
164
169
  onUploadProgress: (n) => {
165
170
  if (n.total) {
166
- const i = Math.round(n.loaded * 100 / n.total);
167
- y.value = i, a?.onUploadProgress?.(i);
171
+ const c = Math.round(n.loaded * 100 / n.total);
172
+ y.value = c, a?.onUploadProgress?.(c);
168
173
  }
169
174
  }
170
- }), f = r.response ? r.response.parse(o.data) : o.data;
171
- s.value = f, a?.onResult?.(f);
172
- } catch (u) {
173
- if (u instanceof F) {
174
- const c = u.response?.data?.message || u.message || "An error occurred", E = u.response?.status, d = u.code;
175
- R.value = c, a?.onError?.(u), t.onErrorRequest?.({ message: c, status: E, code: d });
176
- } else if (u instanceof S) {
177
- l.value = u.issues || [];
178
- const E = `Validation error: ${l.value.map(
175
+ };
176
+ if (r.onBeforeRequest) {
177
+ const n = await r.onBeforeRequest(o);
178
+ n !== void 0 && Object.assign(o, n);
179
+ }
180
+ if (a?.onBeforeRequest) {
181
+ const n = await a.onBeforeRequest(o);
182
+ n !== void 0 && Object.assign(o, n);
183
+ }
184
+ const u = await R.request(o), h = r.response ? r.response.parse(u.data) : u.data;
185
+ t.value = h, a?.onResult?.(h);
186
+ } catch (i) {
187
+ if (i instanceof F) {
188
+ const l = i.response?.data?.message || i.message || "An error occurred", v = i.response?.status, d = i.code;
189
+ p.value = l, a?.onError?.(i), s.onErrorRequest?.({ message: l, status: v, code: d });
190
+ } else if (i instanceof S) {
191
+ f.value = i.issues || [];
192
+ const v = `Validation error: ${f.value.map(
179
193
  (d) => `${d.path.join(".")}: ${d.message}`
180
194
  ).join(", ")}`;
181
- R.value = E, a?.onError?.(u), a?.onZodError?.(l.value), t.onErrorRequest?.({ message: E, code: "VALIDATION_ERROR" }), t.onZodError && t.onZodError(l.value);
195
+ p.value = v, a?.onError?.(i), a?.onZodError?.(f.value), s.onErrorRequest?.({ message: v, code: "VALIDATION_ERROR" }), s.onZodError && s.onZodError(f.value);
182
196
  } else {
183
- const c = u.message || "An error occurred";
184
- R.value = c, a?.onError?.(u), t.onErrorRequest?.({ message: c });
197
+ const l = i.message || "An error occurred";
198
+ p.value = l, a?.onError?.(i), s.onErrorRequest?.({ message: l });
185
199
  }
186
200
  } finally {
187
- v.value = !1, g.value = !0;
201
+ g.value = !1, E.value = !0;
188
202
  }
189
203
  }
190
204
  } };
@@ -192,27 +206,27 @@ function z(t) {
192
206
  }
193
207
  return {
194
208
  query: M,
195
- mutation: w
209
+ mutation: D
196
210
  };
197
211
  }
198
- function V(t) {
199
- return t;
212
+ function J(s) {
213
+ return s;
200
214
  }
201
- function J(t) {
202
- return t;
215
+ function Q(s) {
216
+ return s;
203
217
  }
204
- function Q(...t) {
205
- return Object.assign({}, ...t);
218
+ function W(...s) {
219
+ return Object.assign({}, ...s);
206
220
  }
207
- function W(...t) {
208
- return Object.assign({}, ...t);
221
+ function X(...s) {
222
+ return Object.assign({}, ...s);
209
223
  }
210
224
  export {
211
- G as AxiosError,
212
- z as createApiClient,
213
- J as defineMutation,
214
- V as defineQuery,
215
- W as mergeMutations,
216
- Q as mergeQueries,
217
- $ as z
225
+ K as AxiosError,
226
+ V as createApiClient,
227
+ Q as defineMutation,
228
+ J as defineQuery,
229
+ X as mergeMutations,
230
+ W as mergeQueries,
231
+ U as z
218
232
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vue-api-kit",
3
3
  "type": "module",
4
- "version": "1.6.0",
4
+ "version": "1.8.0",
5
5
  "description": "A powerful and flexible API client for Vue 3 applications, built with TypeScript and Zod for type-safe API interactions.",
6
6
  "keywords": [
7
7
  "vue3",