routesync 1.0.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/dist/sdk.mjs ADDED
@@ -0,0 +1,476 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // packages/core/src/client/HttpClient.ts
9
+ import axios from "axios";
10
+ var HttpClient = class {
11
+ constructor(config) {
12
+ this.client = axios.create({
13
+ baseURL: config.baseURL,
14
+ timeout: config.timeout ?? 1e4,
15
+ headers: {
16
+ "Content-Type": "application/json",
17
+ ...config.headers ?? {}
18
+ }
19
+ });
20
+ if (config.token) {
21
+ this.setToken(config.token);
22
+ }
23
+ this.setupInterceptors();
24
+ }
25
+ setupInterceptors() {
26
+ this.client.interceptors.response.use(
27
+ (response) => response,
28
+ (error) => {
29
+ const message = error.response?.data?.message ?? error.message ?? "Unknown error";
30
+ return Promise.reject({
31
+ success: false,
32
+ message,
33
+ status: error.response?.status,
34
+ errors: error.response?.data?.errors
35
+ });
36
+ }
37
+ );
38
+ }
39
+ setToken(token) {
40
+ this.client.defaults.headers.common["Authorization"] = `Bearer ${token}`;
41
+ }
42
+ removeToken() {
43
+ delete this.client.defaults.headers.common["Authorization"];
44
+ }
45
+ async get(url, config) {
46
+ const response = await this.client.get(url, config);
47
+ return response.data;
48
+ }
49
+ async post(url, body, config) {
50
+ const response = await this.client.post(url, body, config);
51
+ return response.data;
52
+ }
53
+ async put(url, body, config) {
54
+ const response = await this.client.put(url, body, config);
55
+ return response.data;
56
+ }
57
+ async patch(url, body, config) {
58
+ const response = await this.client.patch(url, body, config);
59
+ return response.data;
60
+ }
61
+ async delete(url, config) {
62
+ const response = await this.client.delete(url, config);
63
+ return response.data;
64
+ }
65
+ async upload(url, formData) {
66
+ const response = await this.client.post(url, formData, {
67
+ headers: { "Content-Type": "multipart/form-data" }
68
+ });
69
+ return response.data;
70
+ }
71
+ getInstance() {
72
+ return this.client;
73
+ }
74
+ };
75
+
76
+ // packages/core/src/auth/TokenManager.ts
77
+ var _TokenManager = class _TokenManager {
78
+ constructor() {
79
+ this.token = null;
80
+ }
81
+ set(token) {
82
+ this.token = token;
83
+ if (typeof localStorage !== "undefined") {
84
+ localStorage.setItem(_TokenManager.TOKEN_KEY, token);
85
+ }
86
+ }
87
+ get() {
88
+ if (this.token) return this.token;
89
+ if (typeof localStorage !== "undefined") {
90
+ return localStorage.getItem(_TokenManager.TOKEN_KEY);
91
+ }
92
+ return null;
93
+ }
94
+ clear() {
95
+ this.token = null;
96
+ if (typeof localStorage !== "undefined") {
97
+ localStorage.removeItem(_TokenManager.TOKEN_KEY);
98
+ }
99
+ }
100
+ exists() {
101
+ return this.get() !== null;
102
+ }
103
+ };
104
+ _TokenManager.TOKEN_KEY = "routesync_token";
105
+ var TokenManager = _TokenManager;
106
+
107
+ // packages/core/src/routing/PathResolver.ts
108
+ var PathResolver = class {
109
+ /**
110
+ * Resolve path params.
111
+ * e.g. resolvePath('/produk/:id', { id: 10 }) => '/produk/10'
112
+ */
113
+ static resolve(path, params) {
114
+ if (!params) return path;
115
+ let resolved = path;
116
+ for (const [key, value] of Object.entries(params)) {
117
+ resolved = resolved.replace(`:${key}`, String(value));
118
+ }
119
+ const unresolved = resolved.match(/:([a-zA-Z_]+)/g);
120
+ if (unresolved) {
121
+ throw new Error(
122
+ `Unresolved path params: ${unresolved.join(", ")}`
123
+ );
124
+ }
125
+ return resolved;
126
+ }
127
+ static extractParams(path) {
128
+ const matches = path.match(/:([a-zA-Z_]+)/g) ?? [];
129
+ return matches.map((m) => m.slice(1));
130
+ }
131
+ static hasParams(path) {
132
+ return /:([a-zA-Z_]+)/.test(path);
133
+ }
134
+ };
135
+
136
+ // packages/sdk/src/mappers/schema.ts
137
+ function parseWithSchema(schema, value) {
138
+ if (!schema) return value;
139
+ if (typeof schema === "function") {
140
+ return schema(value);
141
+ }
142
+ if (schema.safeParse) {
143
+ const result = schema.safeParse(value);
144
+ if (result.success) return result.data;
145
+ throw result.error;
146
+ }
147
+ if (schema.parse) {
148
+ return schema.parse(value);
149
+ }
150
+ return value;
151
+ }
152
+
153
+ // packages/sdk/src/defineApi.ts
154
+ var _client = null;
155
+ function getClient() {
156
+ if (!_client) {
157
+ throw new Error("RouteSync not initialized. Call createClient() first.");
158
+ }
159
+ return _client;
160
+ }
161
+ function createClient(config) {
162
+ _client = new HttpClient(config);
163
+ return _client;
164
+ }
165
+ function defineApi(definition, config) {
166
+ if (config) {
167
+ createClient(config);
168
+ }
169
+ const proxy = {};
170
+ for (const group in definition) {
171
+ const groupDef = definition[group];
172
+ const groupProxy = {};
173
+ for (const action in groupDef) {
174
+ const route = groupDef[action];
175
+ const callable = async (options) => {
176
+ const client = getClient();
177
+ const params = applyMapper(
178
+ route,
179
+ "params",
180
+ parseRouteSchema(route, "params", options?.params)
181
+ );
182
+ const query = applyMapper(
183
+ route,
184
+ "query",
185
+ parseRouteSchema(route, "query", options?.query)
186
+ );
187
+ const body = applyMapper(
188
+ route,
189
+ "body",
190
+ parseRouteSchema(route, "body", options?.body)
191
+ );
192
+ const resolvedPath = PathResolver.resolve(route.path, params);
193
+ const method = route.method.toLowerCase();
194
+ const requestConfig = { params: query, headers: route.headers };
195
+ let response;
196
+ if (method === "get" || method === "delete") {
197
+ response = await client[method](resolvedPath, requestConfig);
198
+ } else {
199
+ response = await client[method](resolvedPath, body, requestConfig);
200
+ }
201
+ return applyMapper(
202
+ route,
203
+ "response",
204
+ parseRouteSchema(route, "response", response)
205
+ );
206
+ };
207
+ callable.$def = route;
208
+ callable.$key = [group, action];
209
+ groupProxy[action] = callable;
210
+ }
211
+ ;
212
+ proxy[group] = groupProxy;
213
+ }
214
+ return proxy;
215
+ }
216
+ var routeSchemaKeys = ["params", "query", "body", "request", "response"];
217
+ function parseRouteSchema(route, part, value) {
218
+ if (value === void 0) return void 0;
219
+ const schema = pickRouteSchema(route, part);
220
+ return parseWithSchema(schema, value);
221
+ }
222
+ function pickRouteSchema(route, part) {
223
+ const schema = route.schema;
224
+ if (!schema) return void 0;
225
+ if (hasRouteSchemaKeys(schema)) {
226
+ return schema[part] ?? (part === "body" ? schema.request : void 0);
227
+ }
228
+ return defaultSchemaPart(route.method) === part ? schema : void 0;
229
+ }
230
+ function hasRouteSchemaKeys(value) {
231
+ return Boolean(
232
+ value && typeof value === "object" && routeSchemaKeys.some((key) => key in value)
233
+ );
234
+ }
235
+ function defaultSchemaPart(method) {
236
+ return method === "GET" || method === "DELETE" ? "response" : "body";
237
+ }
238
+ function applyMapper(route, part, value) {
239
+ if (value === void 0 || !route.mapper) return value;
240
+ if (typeof route.mapper === "function") {
241
+ return part === "response" ? route.mapper(value) : value;
242
+ }
243
+ const mapper = route.mapper[part] ?? (part === "body" ? route.mapper.request : void 0);
244
+ return mapper ? mapper(value) : value;
245
+ }
246
+
247
+ // packages/sdk/src/endpoint.ts
248
+ function endpoint(def) {
249
+ return def;
250
+ }
251
+
252
+ // packages/sdk/src/resource.ts
253
+ function resource(config) {
254
+ const result = {};
255
+ for (const key in config.endpoints) {
256
+ const ep = config.endpoints[key];
257
+ result[key] = {
258
+ ...ep,
259
+ // Endpoint-level overrides cascade defaults, never the other way around
260
+ auth: ep.auth ?? config.auth,
261
+ headers: {
262
+ ...config.headers ?? {},
263
+ ...ep.headers ?? {}
264
+ },
265
+ cache: ep.cache ?? config.cache,
266
+ retry: ep.retry ?? config.retry
267
+ };
268
+ }
269
+ return result;
270
+ }
271
+
272
+ // packages/sdk/src/mappers/case.ts
273
+ function snakeToCamelKey(key) {
274
+ return key.replace(/_([a-zA-Z0-9])/g, (_, char) => char.toUpperCase());
275
+ }
276
+ function camelToSnakeKey(key) {
277
+ return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase();
278
+ }
279
+ function isPlainObject(value) {
280
+ return Object.prototype.toString.call(value) === "[object Object]";
281
+ }
282
+ function mapKeysDeep(value, keyCase) {
283
+ if (Array.isArray(value)) {
284
+ return value.map((item) => mapKeysDeep(item, keyCase));
285
+ }
286
+ if (!isPlainObject(value)) {
287
+ return value;
288
+ }
289
+ const mapper = keyCase === "camel" ? snakeToCamelKey : camelToSnakeKey;
290
+ const mapped = {};
291
+ for (const [key, child] of Object.entries(value)) {
292
+ mapped[mapper(key)] = mapKeysDeep(child, keyCase);
293
+ }
294
+ return mapped;
295
+ }
296
+ function toCamelCase(value) {
297
+ return mapKeysDeep(value, "camel");
298
+ }
299
+ function toSnakeCase(value) {
300
+ return mapKeysDeep(value, "snake");
301
+ }
302
+
303
+ // packages/sdk/src/createService.ts
304
+ var GenericService = class {
305
+ constructor(client, endpoint2, options = {}) {
306
+ this.client = client;
307
+ this.endpoint = endpoint2;
308
+ this.options = options;
309
+ }
310
+ async findAll(params) {
311
+ const response = await this.client.get(this.endpoint, {
312
+ params: this.mapRequest(params)
313
+ });
314
+ const items = parseWithSchema(this.options.listSchema, this.extractData(response));
315
+ return items.map((item) => this.mapEntity(item));
316
+ }
317
+ async findById(id, params) {
318
+ const response = await this.client.get(`${this.endpoint}/${id}`, {
319
+ params: this.mapRequest(params)
320
+ });
321
+ return this.mapEntity(this.parseEntity(this.extractData(response)));
322
+ }
323
+ async create(payload) {
324
+ const validated = parseWithSchema(this.options.createSchema, payload);
325
+ const response = await this.client.post(this.endpoint, this.mapRequest(validated));
326
+ return this.mapEntity(this.parseEntity(this.extractData(response)));
327
+ }
328
+ async update(id, payload) {
329
+ const validated = parseWithSchema(this.options.updateSchema, payload);
330
+ const response = await this.client.put(
331
+ `${this.endpoint}/${id}`,
332
+ this.mapRequest(validated)
333
+ );
334
+ return this.mapEntity(this.parseEntity(this.extractData(response)));
335
+ }
336
+ async patch(id, payload) {
337
+ const validated = parseWithSchema(this.options.updateSchema, payload);
338
+ const response = await this.client.patch(
339
+ `${this.endpoint}/${id}`,
340
+ this.mapRequest(validated)
341
+ );
342
+ return this.mapEntity(this.parseEntity(this.extractData(response)));
343
+ }
344
+ async delete(id) {
345
+ const response = await this.client.delete(`${this.endpoint}/${id}`);
346
+ return this.extractData(response);
347
+ }
348
+ async upload(file) {
349
+ const response = await this.client.upload(`${this.endpoint}/upload`, file);
350
+ return this.mapEntity(this.parseEntity(this.extractData(response)));
351
+ }
352
+ extractData(response) {
353
+ if (response && typeof response === "object" && "data" in response) {
354
+ return response.data;
355
+ }
356
+ return response;
357
+ }
358
+ parseEntity(value) {
359
+ return parseWithSchema(this.options.entitySchema, value);
360
+ }
361
+ mapEntity(value) {
362
+ if (this.options.fromBackend) {
363
+ return this.options.fromBackend(value);
364
+ }
365
+ if (this.options.mapResponseKeys === false) {
366
+ return value;
367
+ }
368
+ return toCamelCase(value);
369
+ }
370
+ mapRequest(value) {
371
+ if (value === void 0) return void 0;
372
+ if (this.options.toBackend) {
373
+ return this.options.toBackend(value);
374
+ }
375
+ if (this.options.mapRequestKeys === false) {
376
+ return value;
377
+ }
378
+ return toSnakeCase(value);
379
+ }
380
+ };
381
+ function createService(client, endpoint2, options) {
382
+ return new GenericService(
383
+ client,
384
+ endpoint2,
385
+ options
386
+ );
387
+ }
388
+
389
+ // packages/sdk/src/createClient.ts
390
+ function createClient2(config) {
391
+ const client = new HttpClient(config);
392
+ const tokenManager = new TokenManager();
393
+ if (config.token) {
394
+ tokenManager.set(config.token);
395
+ }
396
+ return {
397
+ client,
398
+ tokenManager,
399
+ setToken(token) {
400
+ tokenManager.set(token);
401
+ client.setToken(token);
402
+ },
403
+ clearToken() {
404
+ tokenManager.clear();
405
+ client.removeToken();
406
+ }
407
+ };
408
+ }
409
+
410
+ // packages/sdk/src/generateHooks.ts
411
+ function generateHooks(api) {
412
+ let useQuery;
413
+ let useMutation;
414
+ let useQueryClient;
415
+ try {
416
+ const rq = __require("@tanstack/react-query");
417
+ useQuery = rq.useQuery;
418
+ useMutation = rq.useMutation;
419
+ useQueryClient = rq.useQueryClient;
420
+ } catch {
421
+ throw new Error(
422
+ "@tanstack/react-query is required to use generateHooks. Install it with: npm install @tanstack/react-query"
423
+ );
424
+ }
425
+ const hooks = {};
426
+ for (const [group, actions] of Object.entries(api)) {
427
+ for (const [action, endpoint2] of Object.entries(actions)) {
428
+ const method = endpoint2.$def.method;
429
+ const hookName = toHookName(group, action);
430
+ if (method === "GET" || method === "DELETE") {
431
+ hooks[hookName] = (options, queryOptions) => useQuery({
432
+ queryKey: options ? [...endpoint2.$key, options] : endpoint2.$key,
433
+ queryFn: () => endpoint2(options),
434
+ ...queryOptions
435
+ });
436
+ } else {
437
+ hooks[hookName] = (mutationOptions) => {
438
+ const qc = useQueryClient();
439
+ return useMutation({
440
+ ...mutationOptions,
441
+ mutationFn: (options) => endpoint2(options),
442
+ onSuccess: (...args) => {
443
+ qc.invalidateQueries({ queryKey: [group] });
444
+ mutationOptions?.invalidate?.forEach((ep) => {
445
+ qc.invalidateQueries({ queryKey: ep.$key });
446
+ });
447
+ mutationOptions?.onSuccess?.(...args);
448
+ }
449
+ });
450
+ };
451
+ }
452
+ }
453
+ }
454
+ return hooks;
455
+ }
456
+ function toHookName(group, action) {
457
+ const g = group.charAt(0).toUpperCase() + group.slice(1);
458
+ const a = action.charAt(0).toUpperCase() + action.slice(1);
459
+ return `use${g}${a}`;
460
+ }
461
+ export {
462
+ GenericService,
463
+ camelToSnakeKey,
464
+ createClient,
465
+ createClient2 as createHttpClient,
466
+ createService,
467
+ defineApi,
468
+ endpoint,
469
+ generateHooks,
470
+ mapKeysDeep,
471
+ parseWithSchema,
472
+ resource,
473
+ snakeToCamelKey,
474
+ toCamelCase,
475
+ toSnakeCase
476
+ };
package/dist/vue.d.mts ADDED
@@ -0,0 +1,108 @@
1
+ import * as _tanstack_query_core from '@tanstack/query-core';
2
+ import * as _tanstack_vue_query from '@tanstack/vue-query';
3
+ import { AxiosRequestConfig, AxiosInstance } from 'axios';
4
+
5
+ interface ServiceConfig {
6
+ baseURL: string;
7
+ token?: string;
8
+ headers?: Record<string, string>;
9
+ timeout?: number;
10
+ retry?: RetryConfig;
11
+ cache?: boolean;
12
+ }
13
+ interface RetryConfig {
14
+ attempts: number;
15
+ delay?: number;
16
+ statusCodes?: number[];
17
+ }
18
+
19
+ declare class HttpClient {
20
+ private client;
21
+ constructor(config: ServiceConfig);
22
+ private setupInterceptors;
23
+ setToken(token: string): void;
24
+ removeToken(): void;
25
+ get<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
26
+ post<T>(url: string, body?: any, config?: AxiosRequestConfig): Promise<T>;
27
+ put<T>(url: string, body?: any, config?: AxiosRequestConfig): Promise<T>;
28
+ patch<T>(url: string, body?: any, config?: AxiosRequestConfig): Promise<T>;
29
+ delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
30
+ upload<T>(url: string, formData: FormData): Promise<T>;
31
+ getInstance(): AxiosInstance;
32
+ }
33
+
34
+ type ParseResult<T> = {
35
+ success: true;
36
+ data: T;
37
+ } | {
38
+ success: false;
39
+ error: unknown;
40
+ };
41
+ interface ParserSchema<T> {
42
+ parse?: (value: unknown) => T;
43
+ safeParse?: (value: unknown) => ParseResult<T>;
44
+ }
45
+ type SchemaLike<T> = ParserSchema<T> | ((value: unknown) => T);
46
+
47
+ type Id = string | number;
48
+ type QueryParams = Record<string, any>;
49
+ interface GenericServiceOptions<TEntity, TCreateInput = Partial<TEntity>, TUpdateInput = Partial<TCreateInput>, TBackendEntity = unknown> {
50
+ /** Validate one backend entity before it is mapped to frontend shape. Works with Zod schemas. */
51
+ entitySchema?: SchemaLike<TBackendEntity>;
52
+ /** Validate backend list responses before each item is mapped to frontend shape. Works with Zod arrays. */
53
+ listSchema?: SchemaLike<TBackendEntity[]>;
54
+ /** Validate frontend create payloads before they are mapped to backend snake_case. Works with Zod schemas. */
55
+ createSchema?: SchemaLike<TCreateInput>;
56
+ /** Validate frontend update/patch payloads before they are mapped to backend snake_case. Works with Zod schemas. */
57
+ updateSchema?: SchemaLike<TUpdateInput>;
58
+ /** Override backend -> frontend mapping. Defaults to deep snake_case -> camelCase. */
59
+ fromBackend?: (value: TBackendEntity) => TEntity;
60
+ /** Override frontend -> backend mapping. Defaults to deep camelCase -> snake_case. */
61
+ toBackend?: (value: TCreateInput | TUpdateInput | QueryParams) => unknown;
62
+ /** Disable if your backend already returns camelCase. Defaults to true. */
63
+ mapResponseKeys?: boolean;
64
+ /** Disable if your backend accepts camelCase. Defaults to true. */
65
+ mapRequestKeys?: boolean;
66
+ }
67
+ declare class GenericService<TEntity = any, TCreateInput = Partial<TEntity>, TUpdateInput = Partial<TCreateInput>, TBackendEntity = unknown> {
68
+ private client;
69
+ private endpoint;
70
+ private options;
71
+ constructor(client: HttpClient, endpoint: string, options?: GenericServiceOptions<TEntity, TCreateInput, TUpdateInput, TBackendEntity>);
72
+ findAll(params?: QueryParams): Promise<TEntity[]>;
73
+ findById(id: Id, params?: QueryParams): Promise<TEntity>;
74
+ create(payload: TCreateInput): Promise<TEntity>;
75
+ update(id: Id, payload: TUpdateInput): Promise<TEntity>;
76
+ patch(id: Id, payload: TUpdateInput): Promise<TEntity>;
77
+ delete<TDeleteResponse = unknown>(id: Id): Promise<TDeleteResponse>;
78
+ upload(file: FormData): Promise<TEntity>;
79
+ private extractData;
80
+ private parseEntity;
81
+ private mapEntity;
82
+ private mapRequest;
83
+ }
84
+
85
+ declare function createComposables(service: GenericService, resourceKey: string): {
86
+ useList: (params?: Record<string, any>) => _tanstack_vue_query.UseQueryReturnType<any[], Error>;
87
+ useDetail: (id: string | number) => _tanstack_vue_query.UseQueryReturnType<any, Error>;
88
+ useCreate: () => _tanstack_vue_query.UseMutationReturnType<any, Error, any, unknown, Omit<_tanstack_query_core.MutationObserverIdleResult<any, Error, any, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverLoadingResult<any, Error, any, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverErrorResult<any, Error, any, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverSuccessResult<any, Error, any, unknown>, "mutate" | "reset">>;
89
+ useUpdate: () => _tanstack_vue_query.UseMutationReturnType<any, Error, {
90
+ id: string | number;
91
+ data: any;
92
+ }, unknown, Omit<_tanstack_query_core.MutationObserverIdleResult<any, Error, {
93
+ id: string | number;
94
+ data: any;
95
+ }, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverLoadingResult<any, Error, {
96
+ id: string | number;
97
+ data: any;
98
+ }, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverErrorResult<any, Error, {
99
+ id: string | number;
100
+ data: any;
101
+ }, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverSuccessResult<any, Error, {
102
+ id: string | number;
103
+ data: any;
104
+ }, unknown>, "mutate" | "reset">>;
105
+ useDelete: () => _tanstack_vue_query.UseMutationReturnType<unknown, Error, string | number, unknown, Omit<_tanstack_query_core.MutationObserverIdleResult<unknown, Error, string | number, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverLoadingResult<unknown, Error, string | number, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverErrorResult<unknown, Error, string | number, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverSuccessResult<unknown, Error, string | number, unknown>, "mutate" | "reset">>;
106
+ };
107
+
108
+ export { createComposables, createComposables as createVueComposables };
package/dist/vue.d.ts ADDED
@@ -0,0 +1,108 @@
1
+ import * as _tanstack_query_core from '@tanstack/query-core';
2
+ import * as _tanstack_vue_query from '@tanstack/vue-query';
3
+ import { AxiosRequestConfig, AxiosInstance } from 'axios';
4
+
5
+ interface ServiceConfig {
6
+ baseURL: string;
7
+ token?: string;
8
+ headers?: Record<string, string>;
9
+ timeout?: number;
10
+ retry?: RetryConfig;
11
+ cache?: boolean;
12
+ }
13
+ interface RetryConfig {
14
+ attempts: number;
15
+ delay?: number;
16
+ statusCodes?: number[];
17
+ }
18
+
19
+ declare class HttpClient {
20
+ private client;
21
+ constructor(config: ServiceConfig);
22
+ private setupInterceptors;
23
+ setToken(token: string): void;
24
+ removeToken(): void;
25
+ get<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
26
+ post<T>(url: string, body?: any, config?: AxiosRequestConfig): Promise<T>;
27
+ put<T>(url: string, body?: any, config?: AxiosRequestConfig): Promise<T>;
28
+ patch<T>(url: string, body?: any, config?: AxiosRequestConfig): Promise<T>;
29
+ delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
30
+ upload<T>(url: string, formData: FormData): Promise<T>;
31
+ getInstance(): AxiosInstance;
32
+ }
33
+
34
+ type ParseResult<T> = {
35
+ success: true;
36
+ data: T;
37
+ } | {
38
+ success: false;
39
+ error: unknown;
40
+ };
41
+ interface ParserSchema<T> {
42
+ parse?: (value: unknown) => T;
43
+ safeParse?: (value: unknown) => ParseResult<T>;
44
+ }
45
+ type SchemaLike<T> = ParserSchema<T> | ((value: unknown) => T);
46
+
47
+ type Id = string | number;
48
+ type QueryParams = Record<string, any>;
49
+ interface GenericServiceOptions<TEntity, TCreateInput = Partial<TEntity>, TUpdateInput = Partial<TCreateInput>, TBackendEntity = unknown> {
50
+ /** Validate one backend entity before it is mapped to frontend shape. Works with Zod schemas. */
51
+ entitySchema?: SchemaLike<TBackendEntity>;
52
+ /** Validate backend list responses before each item is mapped to frontend shape. Works with Zod arrays. */
53
+ listSchema?: SchemaLike<TBackendEntity[]>;
54
+ /** Validate frontend create payloads before they are mapped to backend snake_case. Works with Zod schemas. */
55
+ createSchema?: SchemaLike<TCreateInput>;
56
+ /** Validate frontend update/patch payloads before they are mapped to backend snake_case. Works with Zod schemas. */
57
+ updateSchema?: SchemaLike<TUpdateInput>;
58
+ /** Override backend -> frontend mapping. Defaults to deep snake_case -> camelCase. */
59
+ fromBackend?: (value: TBackendEntity) => TEntity;
60
+ /** Override frontend -> backend mapping. Defaults to deep camelCase -> snake_case. */
61
+ toBackend?: (value: TCreateInput | TUpdateInput | QueryParams) => unknown;
62
+ /** Disable if your backend already returns camelCase. Defaults to true. */
63
+ mapResponseKeys?: boolean;
64
+ /** Disable if your backend accepts camelCase. Defaults to true. */
65
+ mapRequestKeys?: boolean;
66
+ }
67
+ declare class GenericService<TEntity = any, TCreateInput = Partial<TEntity>, TUpdateInput = Partial<TCreateInput>, TBackendEntity = unknown> {
68
+ private client;
69
+ private endpoint;
70
+ private options;
71
+ constructor(client: HttpClient, endpoint: string, options?: GenericServiceOptions<TEntity, TCreateInput, TUpdateInput, TBackendEntity>);
72
+ findAll(params?: QueryParams): Promise<TEntity[]>;
73
+ findById(id: Id, params?: QueryParams): Promise<TEntity>;
74
+ create(payload: TCreateInput): Promise<TEntity>;
75
+ update(id: Id, payload: TUpdateInput): Promise<TEntity>;
76
+ patch(id: Id, payload: TUpdateInput): Promise<TEntity>;
77
+ delete<TDeleteResponse = unknown>(id: Id): Promise<TDeleteResponse>;
78
+ upload(file: FormData): Promise<TEntity>;
79
+ private extractData;
80
+ private parseEntity;
81
+ private mapEntity;
82
+ private mapRequest;
83
+ }
84
+
85
+ declare function createComposables(service: GenericService, resourceKey: string): {
86
+ useList: (params?: Record<string, any>) => _tanstack_vue_query.UseQueryReturnType<any[], Error>;
87
+ useDetail: (id: string | number) => _tanstack_vue_query.UseQueryReturnType<any, Error>;
88
+ useCreate: () => _tanstack_vue_query.UseMutationReturnType<any, Error, any, unknown, Omit<_tanstack_query_core.MutationObserverIdleResult<any, Error, any, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverLoadingResult<any, Error, any, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverErrorResult<any, Error, any, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverSuccessResult<any, Error, any, unknown>, "mutate" | "reset">>;
89
+ useUpdate: () => _tanstack_vue_query.UseMutationReturnType<any, Error, {
90
+ id: string | number;
91
+ data: any;
92
+ }, unknown, Omit<_tanstack_query_core.MutationObserverIdleResult<any, Error, {
93
+ id: string | number;
94
+ data: any;
95
+ }, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverLoadingResult<any, Error, {
96
+ id: string | number;
97
+ data: any;
98
+ }, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverErrorResult<any, Error, {
99
+ id: string | number;
100
+ data: any;
101
+ }, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverSuccessResult<any, Error, {
102
+ id: string | number;
103
+ data: any;
104
+ }, unknown>, "mutate" | "reset">>;
105
+ useDelete: () => _tanstack_vue_query.UseMutationReturnType<unknown, Error, string | number, unknown, Omit<_tanstack_query_core.MutationObserverIdleResult<unknown, Error, string | number, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverLoadingResult<unknown, Error, string | number, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverErrorResult<unknown, Error, string | number, unknown>, "mutate" | "reset"> | Omit<_tanstack_query_core.MutationObserverSuccessResult<unknown, Error, string | number, unknown>, "mutate" | "reset">>;
106
+ };
107
+
108
+ export { createComposables, createComposables as createVueComposables };