vue-api-kit 1.10.7 → 1.10.8

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
@@ -235,7 +235,7 @@ async function handleSubmit() {
235
235
  - ✅ **POST Queries**: Support for both GET and POST methods in queries for complex data retrieval
236
236
  - ✅ **Modular APIs**: Merge queries and mutations from separate files with full type safety
237
237
  - ✅ **Multi-Level Nesting**: Organize queries and mutations in nested structures with full type safety
238
- - ✅ **File Upload**: Support for multipart/form-data in mutations
238
+ - ✅ **File Upload**: Support for multipart/form-data with nested objects in mutations
239
239
  - ✅ **Path Parameters**: Automatic path parameter replacement
240
240
  - ✅ **Debouncing**: Built-in request debouncing
241
241
  - ✅ **CSRF Protection**: Automatic CSRF token refresh on 403/419 errors
@@ -434,7 +434,7 @@ const api = createApiClient({
434
434
  console.log('Request finished');
435
435
  },
436
436
 
437
- onErrorRequest: (error) => {
437
+ onError: (error) => {
438
438
  // Global error handler
439
439
  console.error('API Error:', error.message);
440
440
  },
@@ -832,6 +832,66 @@ async function handleUpload(file: File) {
832
832
  }
833
833
  ```
834
834
 
835
+ ### Nested Objects in Multipart
836
+
837
+ The multipart feature supports nested objects with automatic bracket notation flattening:
838
+
839
+ ```typescript
840
+ const api = createApiClient({
841
+ baseURL: 'https://api.example.com',
842
+ mutations: {
843
+ createProduct: {
844
+ method: 'POST',
845
+ path: '/products',
846
+ isMultipart: true,
847
+ response: z.object({
848
+ id: z.number(),
849
+ success: z.boolean()
850
+ })
851
+ }
852
+ }
853
+ });
854
+
855
+ // In component
856
+ const { mutate } = api.mutation.createProduct();
857
+
858
+ async function handleSubmit(file: File) {
859
+ await mutate({
860
+ data: {
861
+ code: 'PROD001',
862
+ name: 'Product Name',
863
+ description: 'Product description',
864
+ // Nested objects are automatically flattened with bracket notation
865
+ image: {
866
+ file_url: 'https://example.com/existing.jpg',
867
+ file: file
868
+ }
869
+ }
870
+ });
871
+ }
872
+
873
+ // The FormData will be sent as:
874
+ // code=PROD001
875
+ // name=Product Name
876
+ // description=Product description
877
+ // image[file_url]=https://example.com/existing.jpg
878
+ // image[file]=<File>
879
+ ```
880
+
881
+ You can also use flat bracket notation directly:
882
+
883
+ ```typescript
884
+ await mutate({
885
+ data: {
886
+ 'image[file]': file,
887
+ 'image[file_url]': 'https://example.com/existing.jpg',
888
+ code: 'PROD001'
889
+ }
890
+ });
891
+ ```
892
+
893
+ Both approaches work seamlessly together, giving you flexibility in how you structure your data.
894
+
835
895
  ## 🔒 CSRF Token Protection
836
896
 
837
897
  The client includes built-in CSRF token protection, perfect for Laravel Sanctum or similar CSRF-based authentication systems.
@@ -104,7 +104,22 @@ export type MutationHooksFromDefinitions<M> = {
104
104
  * csrfRefreshEndpoint: "/auth/refresh-csrf",
105
105
  * queries: { getUsers: { path: "/users" } },
106
106
  * mutations: { createUser: { method: "POST", path: "/users" } },
107
- * onErrorRequest: (error) => console.error(error.message)
107
+ * onBeforeRequest: async (config) => {
108
+ * // Modify config before request
109
+ * return config;
110
+ * },
111
+ * onStartRequest: async () => {
112
+ * console.log("Request started");
113
+ * },
114
+ * onFinishRequest: async () => {
115
+ * console.log("Request finished");
116
+ * },
117
+ * onError: ({ err, message }) => {
118
+ * console.error("API Error:", message);
119
+ * },
120
+ * onZodError: (zodError) => {
121
+ * console.error("Validation Error:", zodError);
122
+ * }
108
123
  * };
109
124
  * @example
110
125
  * // Nested structure
package/dist/index.js CHANGED
@@ -1,18 +1,33 @@
1
- import A, { ZodError as F } from "zod";
2
- import * as G from "zod";
3
- import { ZodError as ae } from "zod";
4
- import U, { AxiosError as Z } from "axios";
5
- import { AxiosError as ie } from "axios";
6
- import { nextTick as _, ref as p, onMounted as N, watch as Q, onBeforeUnmount as z } from "vue";
7
- import { debounce as J } from "lodash-es";
8
- function H(e) {
1
+ import A, { ZodError as Z } from "zod";
2
+ import * as I from "zod";
3
+ import { ZodError as ne } from "zod";
4
+ import U, { AxiosError as $ } from "axios";
5
+ import { AxiosError as ue } from "axios";
6
+ import { nextTick as _, ref as g, onMounted as Q, watch as z, onBeforeUnmount as N } from "vue";
7
+ import { debounce as H } from "lodash-es";
8
+ function W(e) {
9
9
  return e && typeof e == "object" && e !== null && typeof e.path == "string";
10
10
  }
11
- function W(e) {
11
+ function X(e) {
12
12
  return e && typeof e == "object" && e !== null && typeof e.path == "string" && typeof e.method == "string";
13
13
  }
14
- function Y(e) {
15
- const h = U.create({
14
+ function F(e, a, h = "") {
15
+ if (a instanceof File || a instanceof Blob)
16
+ e.append(h, a);
17
+ else if (Array.isArray(a))
18
+ a.forEach((l) => {
19
+ l instanceof File || l instanceof Blob ? e.append(h, l) : typeof l == "object" && l !== null ? F(e, l, h) : e.append(h, String(l));
20
+ });
21
+ else if (typeof a == "object" && a !== null)
22
+ for (const [l, B] of Object.entries(a)) {
23
+ const k = h ? `${h}[${l}]` : l;
24
+ F(e, B, k);
25
+ }
26
+ else
27
+ e.append(h, String(a));
28
+ }
29
+ function K(e) {
30
+ const a = U.create({
16
31
  baseURL: e.baseURL,
17
32
  headers: {
18
33
  "Content-Type": "application/json",
@@ -22,212 +37,210 @@ function Y(e) {
22
37
  withCredentials: e.withCredentials ?? !1,
23
38
  withXSRFToken: e.withXSRFToken ?? !1
24
39
  });
25
- let B = !1, w = null;
26
- e.onBeforeRequest && h.interceptors.request.use(
40
+ let h = !1, l = null;
41
+ e.onBeforeRequest && a.interceptors.request.use(
27
42
  async (r) => {
28
43
  try {
29
44
  return await e.onBeforeRequest(r) || r;
30
- } catch (n) {
31
- return Promise.reject(n);
45
+ } catch (u) {
46
+ return Promise.reject(u);
32
47
  }
33
48
  },
34
49
  (r) => Promise.reject(r)
35
- ), e.onStartRequest && h.interceptors.request.use(
50
+ ), e.onStartRequest && a.interceptors.request.use(
36
51
  async (r) => {
37
52
  try {
38
53
  return await e.onStartRequest(), r;
39
- } catch (n) {
40
- return Promise.reject(n);
54
+ } catch (u) {
55
+ return Promise.reject(u);
41
56
  }
42
57
  },
43
58
  (r) => Promise.reject(r)
44
- ), e.onFinishRequest && h.interceptors.response.use(
59
+ ), e.onFinishRequest && a.interceptors.response.use(
45
60
  (r) => (e.onFinishRequest(), r),
46
61
  (r) => (e.onFinishRequest(), Promise.reject(r))
47
- ), h.interceptors.request.use((r) => {
62
+ ), a.interceptors.request.use((r) => {
48
63
  if (!r.url) return r;
49
- const n = (m) => {
50
- if (m)
51
- for (const [c, s] of Object.entries(m)) {
52
- const o = `{${c}}`;
53
- r.url.includes(o) && (r.url = r.url.replace(
54
- o,
55
- encodeURIComponent(String(s))
56
- ), delete m[c]);
64
+ const u = (v) => {
65
+ if (v)
66
+ for (const [m, o] of Object.entries(v)) {
67
+ const s = `{${m}}`;
68
+ r.url.includes(s) && (r.url = r.url.replace(
69
+ s,
70
+ encodeURIComponent(String(o))
71
+ ), delete v[m]);
57
72
  }
58
73
  };
59
- return r.method !== "get" && r.data?.params && n(r.data.params), n(r.params), r;
60
- }), e.csrfRefreshEndpoint && h.interceptors.response.use(
74
+ return r.method !== "get" && r.data?.params && u(r.data.params), u(r.params), r;
75
+ }), e.csrfRefreshEndpoint && a.interceptors.response.use(
61
76
  (r) => r,
62
77
  async (r) => {
63
- const n = r.config;
64
- if (n?.url === e.csrfRefreshEndpoint)
78
+ const u = r.config;
79
+ if (u?.url === e.csrfRefreshEndpoint)
65
80
  return Promise.reject(r);
66
- if (r.response && (r.response.status === 403 || r.response.status === 419) && !n?._retry) {
67
- n._retry = !0;
81
+ if (r.response && (r.response.status === 403 || r.response.status === 419) && !u?._retry) {
82
+ u._retry = !0;
68
83
  try {
69
- return B && w ? await w : (B = !0, w = h.get(e.csrfRefreshEndpoint).then(() => {
70
- B = !1, w = null;
71
- }), await w), h.request(n);
72
- } catch (m) {
73
- return B = !1, w = null, Promise.reject(m);
84
+ return h && l ? await l : (h = !0, l = a.get(e.csrfRefreshEndpoint).then(() => {
85
+ h = !1, l = null;
86
+ }), await l), a.request(u);
87
+ } catch (v) {
88
+ return h = !1, l = null, Promise.reject(v);
74
89
  }
75
90
  }
76
91
  return Promise.reject(r);
77
92
  }
78
- ), h.interceptors.response.use(
93
+ ), a.interceptors.response.use(
79
94
  (r) => r,
80
95
  (r) => (_(() => {
81
- r.code;
96
+ r?.code;
82
97
  }), Promise.reject(r))
83
98
  );
84
- function D(r) {
85
- const n = {};
86
- for (const m in r) {
87
- const c = r[m];
88
- if (c)
89
- if (H(c)) {
90
- const s = c;
91
- n[m] = (o) => {
92
- let a;
93
- o && typeof o == "object" && ("loadOnMount" in o || "debounce" in o || "onResult" in o || "onError" in o || "onZodError" in o || "onBeforeRequest" in o || "params" in o || "data" in o ? a = o : a = { params: o });
94
- const g = p(), v = p(), E = p(), C = p(!1), j = p(!1), k = p(!0);
99
+ function B(r) {
100
+ const u = {};
101
+ for (const v in r) {
102
+ const m = r[v];
103
+ if (m)
104
+ if (W(m)) {
105
+ const o = m;
106
+ u[v] = (s) => {
107
+ let n;
108
+ s && typeof s == "object" && ("loadOnMount" in s || "debounce" in s || "onResult" in s || "onError" in s || "onZodError" in s || "onBeforeRequest" in s || "params" in s || "data" in s ? n = s : n = { params: s });
109
+ const R = g(), E = g(), p = g(), C = g(!1), w = g(!1), M = g(!0);
95
110
  let P = new AbortController();
96
111
  const i = () => {
97
112
  P?.abort(), P = new AbortController();
98
- }, l = async () => {
99
- C.value && i(), C.value = !0, v.value = void 0;
113
+ }, f = async () => {
114
+ C.value && i(), C.value = !0, E.value = void 0;
100
115
  try {
101
- s.params && a?.params && s.params.parse(a.params);
102
- let t = a?.data;
103
- s.data && t && s.data.parse(t);
104
- const u = {
105
- method: s.method ?? "GET",
106
- url: s.path,
107
- params: a?.params,
116
+ o.params && n?.params && o.params.parse(n.params);
117
+ let t = n?.data;
118
+ o.data && t && o.data.parse(t);
119
+ const c = {
120
+ method: o.method ?? "GET",
121
+ url: o.path,
122
+ params: n?.params,
108
123
  signal: P.signal
109
124
  };
110
- if (s.method === "POST" && t && (u.data = t), s.onBeforeRequest) {
111
- const d = await s.onBeforeRequest(u);
112
- d !== void 0 && Object.assign(u, d);
125
+ if (o.method === "POST" && t && (c.data = t), o.onBeforeRequest) {
126
+ const y = await o.onBeforeRequest(c);
127
+ y !== void 0 && Object.assign(c, y);
113
128
  }
114
- if (a?.onBeforeRequest) {
115
- const d = await a.onBeforeRequest(u);
116
- d !== void 0 && Object.assign(u, d);
129
+ if (n?.onBeforeRequest) {
130
+ const y = await n.onBeforeRequest(c);
131
+ y !== void 0 && Object.assign(c, y);
117
132
  }
118
- v.value = void 0, E.value = void 0;
119
- const b = await h.request(u), f = s.response ? s.response.parse(b.data) : b.data;
120
- g.value = f, a?.onResult?.(f);
133
+ E.value = void 0, p.value = void 0;
134
+ const j = await a.request(c), d = o.response ? o.response.parse(j.data) : j.data;
135
+ R.value = d, n?.onResult?.(d);
121
136
  } catch (t) {
122
- if (t instanceof Z) {
137
+ if (t instanceof $) {
123
138
  if (t.code !== "ERR_CANCELED") {
124
- const u = t.response?.data?.message || t.message || "An error occurred";
125
- v.value = u, a?.onError?.(t), e.onError?.({ err: t, message: u });
139
+ const c = t.response?.data?.message || t.message || "An error occurred";
140
+ E.value = c, n?.onError?.(t), e.onError?.({ err: t, message: c });
126
141
  }
127
- } else if (t instanceof F) {
128
- E.value = A.flattenError(t);
129
- const u = Object.keys(E.value.fieldErrors).length, b = `${Object.values(E.value.fieldErrors).at(0)}.${u > 1 ? ` (and ${u - 1} more errors)` : ""}`;
130
- v.value = b, a?.onError?.(t), a?.onZodError?.(A.flattenError(t)), e.onError?.({ err: t, message: b }), e.onZodError && e.onZodError(A.flattenError(t));
142
+ } else if (t instanceof Z) {
143
+ p.value = A.flattenError(t);
144
+ const c = Object.keys(p.value.fieldErrors).length, j = `${Object.values(p.value.fieldErrors).at(0)}.${c > 1 ? ` (and ${c - 1} more errors)` : ""}`;
145
+ E.value = j, n?.onError?.(t), n?.onZodError?.(A.flattenError(t)), e.onError?.({ err: t, message: j }), e.onZodError && e.onZodError(A.flattenError(t));
131
146
  } else {
132
- const u = t.message || "An error occurred";
133
- v.value = u, a?.onError?.(u), e.onError?.({ err: t, message: u });
147
+ const c = t.message || "An error occurred";
148
+ E.value = c, n?.onError?.(c), e.onError?.({ err: t, message: c });
134
149
  }
135
150
  } finally {
136
- C.value = !1, j.value = !0;
151
+ C.value = !1, w.value = !0;
137
152
  }
138
- }, R = a?.debounce ? J(l, a.debounce) : l;
139
- let q = null;
140
- return (a?.params || a?.data) && (N(() => {
141
- q && q(), q = Q(
142
- () => JSON.stringify({ params: a.params, data: a.data }),
153
+ }, q = n?.debounce ? H(f, n.debounce) : f;
154
+ let b = null;
155
+ return (n?.params || n?.data) && (Q(() => {
156
+ b && b(), b = z(
157
+ () => JSON.stringify({ params: n.params, data: n.data }),
143
158
  () => {
144
- R();
159
+ q();
145
160
  },
146
161
  { immediate: !1 }
147
162
  );
148
- }), z(() => {
149
- q && q(), P?.abort();
150
- })), (a?.loadOnMount === void 0 || a.loadOnMount) && !j.value && (k.value ? (k.value = !1, l()) : R()), { result: g, errorMessage: v, zodError: E, isLoading: C, isDone: j, refetch: l };
163
+ }), N(() => {
164
+ b && b(), P?.abort();
165
+ })), (n?.loadOnMount === void 0 || n.loadOnMount) && !w.value && (M.value ? (M.value = !1, f()) : q()), { result: R, errorMessage: E, zodError: p, isLoading: C, isDone: w, refetch: f };
151
166
  };
152
- } else typeof c == "object" && (n[m] = D(c));
167
+ } else typeof m == "object" && (u[v] = B(m));
153
168
  }
154
- return n;
169
+ return u;
155
170
  }
156
- const x = e.queries ?? {}, L = D(x);
171
+ const k = e.queries ?? {}, x = B(k);
157
172
  function S(r) {
158
- const n = {};
159
- for (const m in r) {
160
- const c = r[m];
161
- if (c)
162
- if (W(c)) {
163
- const s = c;
164
- n[m] = (o) => {
165
- const a = p(), g = p(), v = p(), E = p(!1), C = p(!1), j = p(0);
166
- return { result: a, errorMessage: g, zodError: v, isLoading: E, isDone: C, uploadProgress: j, mutate: async (P) => {
167
- if (!E.value) {
168
- E.value = !0, g.value = void 0, j.value = 0;
173
+ const u = {};
174
+ for (const v in r) {
175
+ const m = r[v];
176
+ if (m)
177
+ if (X(m)) {
178
+ const o = m;
179
+ u[v] = (s) => {
180
+ const n = g(), R = g(), E = g(), p = g(!1), C = g(!1), w = g(0);
181
+ return { result: n, errorMessage: R, zodError: E, isLoading: p, isDone: C, uploadProgress: w, mutate: async (P) => {
182
+ if (!p.value) {
183
+ p.value = !0, R.value = void 0, w.value = 0;
169
184
  try {
170
- const { data: i = {}, params: l } = P ?? {};
171
- let R = i ?? {}, q = {};
172
- if (s.isMultipart) {
173
- const f = new FormData();
174
- for (const [d, y] of Object.entries(i))
175
- y instanceof File || y instanceof Blob ? f.append(d, y) : Array.isArray(y) ? y.forEach((M) => {
176
- M instanceof File || M instanceof Blob ? f.append(d, M) : f.append(d, JSON.stringify(M));
177
- }) : typeof y == "object" && y !== null ? f.append(d, JSON.stringify(y)) : f.append(d, String(y));
178
- R = f, q["Content-Type"] = "multipart/form-data";
179
- } else s.data && s.data.parse(i);
180
- s.params && l && s.params.parse(l);
185
+ const { data: i = {}, params: f } = P ?? {};
186
+ let q = i ?? {}, b = {};
187
+ if (o.isMultipart) {
188
+ const d = new FormData();
189
+ for (const [y, D] of Object.entries(i))
190
+ F(d, D, y);
191
+ q = d, b["Content-Type"] = "multipart/form-data";
192
+ } else o.data && o.data.parse(i);
193
+ o.params && f && o.params.parse(f);
181
194
  const t = {
182
- method: s.method,
183
- url: s.path,
184
- data: R,
185
- params: l,
186
- headers: q,
187
- onUploadProgress: (f) => {
188
- if (f.total) {
189
- const d = Math.round(f.loaded * 100 / f.total);
190
- j.value = d, o?.onUploadProgress?.(d);
195
+ method: o.method,
196
+ url: o.path,
197
+ data: q,
198
+ params: f,
199
+ headers: b,
200
+ onUploadProgress: (d) => {
201
+ if (d.total) {
202
+ const y = Math.round(d.loaded * 100 / d.total);
203
+ w.value = y, s?.onUploadProgress?.(y);
191
204
  }
192
205
  }
193
206
  };
194
- if (s.onBeforeRequest) {
195
- const f = await s.onBeforeRequest(t);
196
- f !== void 0 && Object.assign(t, f);
207
+ if (o.onBeforeRequest) {
208
+ const d = await o.onBeforeRequest(t);
209
+ d !== void 0 && Object.assign(t, d);
197
210
  }
198
- if (o?.onBeforeRequest) {
199
- const f = await o.onBeforeRequest(t);
200
- f !== void 0 && Object.assign(t, f);
211
+ if (s?.onBeforeRequest) {
212
+ const d = await s.onBeforeRequest(t);
213
+ d !== void 0 && Object.assign(t, d);
201
214
  }
202
- g.value = void 0, v.value = void 0;
203
- const u = await h.request(t), b = s.response ? s.response.parse(u.data) : u.data;
204
- a.value = b, o?.onResult?.(b);
215
+ R.value = void 0, E.value = void 0;
216
+ const c = await a.request(t), j = o.response ? o.response.parse(c.data) : c.data;
217
+ n.value = j, s?.onResult?.(j);
205
218
  } catch (i) {
206
- if (i instanceof Z) {
207
- const l = i.response?.data?.message || i.message || "An error occurred";
208
- g.value = l, o?.onError?.(i), e.onError?.({ err: i, message: l });
209
- } else if (i instanceof F) {
210
- v.value = A.flattenError(i);
211
- const l = Object.keys(v.value.fieldErrors).length, R = `${Object.values(v.value.fieldErrors).at(0)}.${l > 1 ? ` (and ${l - 1} more errors)` : ""}`;
212
- g.value = R, o?.onError?.(i), o?.onZodError?.(A.flattenError(i)), e.onError?.({ err: i, message: R }), e.onZodError && e.onZodError(A.flattenError(i));
219
+ if (i instanceof $) {
220
+ const f = i.response?.data?.message || i.message || "An error occurred";
221
+ R.value = f, s?.onError?.(i), e.onError?.({ err: i, message: f });
222
+ } else if (i instanceof Z) {
223
+ E.value = A.flattenError(i);
224
+ const f = Object.keys(E.value.fieldErrors).length, q = `${Object.values(E.value.fieldErrors).at(0)}.${f > 1 ? ` (and ${f - 1} more errors)` : ""}`;
225
+ R.value = q, s?.onError?.(i), s?.onZodError?.(A.flattenError(i)), e.onError?.({ err: i, message: q }), e.onZodError && e.onZodError(A.flattenError(i));
213
226
  } else {
214
- const l = i.message || "An error occurred";
215
- g.value = l, o?.onError?.(i), e.onError?.({ err: i, message: l });
227
+ const f = i.message || "An error occurred";
228
+ R.value = f, s?.onError?.(i), e.onError?.({ err: i, message: f });
216
229
  }
217
230
  } finally {
218
- E.value = !1, C.value = !0;
231
+ p.value = !1, C.value = !0;
219
232
  }
220
233
  }
221
234
  } };
222
235
  };
223
- } else typeof c == "object" && (n[m] = S(c));
236
+ } else typeof m == "object" && (u[v] = S(m));
224
237
  }
225
- return n;
238
+ return u;
226
239
  }
227
- const T = e.mutations ?? {}, $ = S(T);
240
+ const T = e.mutations ?? {}, L = S(T);
228
241
  return {
229
- query: L,
230
- mutation: $
242
+ query: x,
243
+ mutation: L
231
244
  };
232
245
  }
233
246
  function O(e) {
@@ -243,12 +256,12 @@ function te(...e) {
243
256
  return Object.assign({}, ...e);
244
257
  }
245
258
  export {
246
- ie as AxiosError,
247
- ae as ZodError,
248
- Y as createApiClient,
259
+ ue as AxiosError,
260
+ ne as ZodError,
261
+ K as createApiClient,
249
262
  ee as defineMutation,
250
263
  O as defineQuery,
251
264
  te as mergeMutations,
252
265
  re as mergeQueries,
253
- G as z
266
+ I as z
254
267
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vue-api-kit",
3
3
  "type": "module",
4
- "version": "1.10.7",
4
+ "version": "1.10.8",
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",