@tahanabavi/typefetch 1.0.3 → 1.1.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 +124 -10
- package/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +72 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +72 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TypeFetch
|
|
2
2
|
|
|
3
|
-
TypeFetch is a type-safe client for working with APIs, built with TypeScript and Zod. This project allows you to define API contracts and safely use types, while also supporting middlewares, error handling, and response
|
|
3
|
+
TypeFetch is a type-safe client for working with APIs, built with TypeScript and Zod. This project allows you to define API contracts and safely use types, while also supporting middlewares, error handling, response transformation, mock data, and response wrappers.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -12,6 +12,8 @@ TypeFetch is a type-safe client for working with APIs, built with TypeScript and
|
|
|
12
12
|
- Error handling with the `RichError` class
|
|
13
13
|
- Ability to transform responses using a response transformer
|
|
14
14
|
- Authentication support via token
|
|
15
|
+
- Mock data support for development and testing
|
|
16
|
+
- Response wrapper for consistent API response formats
|
|
15
17
|
|
|
16
18
|
---
|
|
17
19
|
|
|
@@ -25,9 +27,32 @@ yarn add @tahanabavi/typefetch
|
|
|
25
27
|
|
|
26
28
|
---
|
|
27
29
|
|
|
30
|
+
## What's New in v1.1.0
|
|
31
|
+
|
|
32
|
+
### 🎯 Mock Data Support
|
|
33
|
+
|
|
34
|
+
- Add mock data to endpoints for development and testing
|
|
35
|
+
- Configurable random delays to simulate network latency
|
|
36
|
+
- Support for both static data and dynamic functions
|
|
37
|
+
- Runtime toggle between mock and real API modes
|
|
38
|
+
|
|
39
|
+
### 🔄 Response Wrapper
|
|
40
|
+
|
|
41
|
+
- Consistent API response format handling
|
|
42
|
+
- Automatic validation of wrapped responses
|
|
43
|
+
- Support for success/error response patterns
|
|
44
|
+
- Seamless integration with existing contracts
|
|
45
|
+
|
|
46
|
+
### 🚀 Enhanced Error Handling
|
|
47
|
+
|
|
48
|
+
- Better Zod error wrapping and reporting
|
|
49
|
+
- Improved type safety for response wrappers
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
28
53
|
## Defining Contracts
|
|
29
54
|
|
|
30
|
-
Contracts are defined using the `Contracts` and `EndpointDef` types
|
|
55
|
+
Contracts are defined using the `Contracts` and `EndpointDef` types.
|
|
31
56
|
|
|
32
57
|
```ts
|
|
33
58
|
import { z } from "zod";
|
|
@@ -40,12 +65,16 @@ const contracts = {
|
|
|
40
65
|
auth: true,
|
|
41
66
|
request: z.object({ id: z.string() }),
|
|
42
67
|
response: z.object({ id: z.string(), name: z.string() }),
|
|
68
|
+
// Optional mock data
|
|
69
|
+
mockData: { id: "1", name: "John Doe" },
|
|
43
70
|
},
|
|
44
71
|
createUser: {
|
|
45
72
|
method: "POST",
|
|
46
73
|
path: "/user",
|
|
47
74
|
request: z.object({ name: z.string() }),
|
|
48
75
|
response: z.object({ id: z.string(), name: z.string() }),
|
|
76
|
+
// Dynamic mock data function
|
|
77
|
+
mockData: () => ({ id: Math.random().toString(), name: "Dynamic User" }),
|
|
49
78
|
},
|
|
50
79
|
},
|
|
51
80
|
} as const;
|
|
@@ -62,6 +91,8 @@ const client = new ApiClient(
|
|
|
62
91
|
{
|
|
63
92
|
baseUrl: "https://api.example.com",
|
|
64
93
|
token: "your-auth-token",
|
|
94
|
+
useMockData: true,
|
|
95
|
+
mockDelay: { min: 100, max: 1000 },
|
|
65
96
|
},
|
|
66
97
|
contracts
|
|
67
98
|
);
|
|
@@ -82,7 +113,7 @@ All errors are provided via the `RichError` class. You can define a custom error
|
|
|
82
113
|
|
|
83
114
|
```ts
|
|
84
115
|
client.onError((error: RichError) => {
|
|
85
|
-
console.error("API Error:", error.message, error.status);
|
|
116
|
+
console.error("API Error:", error.message, error.status, error.code);
|
|
86
117
|
});
|
|
87
118
|
```
|
|
88
119
|
|
|
@@ -140,11 +171,61 @@ client.useResponseTransform((data) => {
|
|
|
140
171
|
|
|
141
172
|
---
|
|
142
173
|
|
|
174
|
+
## Mock Data Features
|
|
175
|
+
|
|
176
|
+
Static Mock Data:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
mockData: { id: "1", name: "Static User" }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Dynamic Mock Data:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
mockData: () => ({ id: Math.random().toString(), name: `User-${Date.now()}` });
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Runtime Control:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
client.setMockMode(true, { min: 100, max: 2000 });
|
|
192
|
+
client.setMockMode(false);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Response Wrapper Features
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
const apiResponseWrapper = (successResponse: z.ZodTypeAny) =>
|
|
201
|
+
z.union([
|
|
202
|
+
z.object({
|
|
203
|
+
success: z.literal(true),
|
|
204
|
+
data: successResponse,
|
|
205
|
+
timestamp: z.string(),
|
|
206
|
+
requestId: z.string(),
|
|
207
|
+
}),
|
|
208
|
+
z.object({
|
|
209
|
+
success: z.literal(false),
|
|
210
|
+
message: z.string(),
|
|
211
|
+
code: z.number(),
|
|
212
|
+
timestamp: z.string(),
|
|
213
|
+
requestId: z.string(),
|
|
214
|
+
}),
|
|
215
|
+
]);
|
|
216
|
+
|
|
217
|
+
client.setResponseWrapper(apiResponseWrapper);
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
143
222
|
## Important Notes
|
|
144
223
|
|
|
145
224
|
- Always call `client.init()` before using endpoints.
|
|
146
225
|
- Types are automatically inferred from Zod, making inputs and outputs type-safe.
|
|
147
226
|
- Middleware execution order: first added middleware runs last, last added middleware runs first.
|
|
227
|
+
- Mock data is used only when `useMockData` is true and mock data is defined.
|
|
228
|
+
- Response wrapper automatically handles success/error patterns.
|
|
148
229
|
|
|
149
230
|
---
|
|
150
231
|
|
|
@@ -156,23 +237,50 @@ import { ApiClient, RichError } from "typefetch";
|
|
|
156
237
|
import { LoggingMiddleware, RetryMiddleware } from "typefetch/middlewares";
|
|
157
238
|
|
|
158
239
|
const contracts = {
|
|
159
|
-
|
|
160
|
-
|
|
240
|
+
user: {
|
|
241
|
+
getUser: {
|
|
161
242
|
method: "GET",
|
|
162
|
-
path: "/
|
|
243
|
+
path: "/users/:id",
|
|
163
244
|
auth: true,
|
|
164
245
|
request: z.object({ id: z.string() }),
|
|
165
|
-
response: z.object({
|
|
246
|
+
response: z.object({
|
|
247
|
+
id: z.string(),
|
|
248
|
+
name: z.string(),
|
|
249
|
+
email: z.string(),
|
|
250
|
+
}),
|
|
251
|
+
mockData: { id: "1", name: "John Doe", email: "john@example.com" },
|
|
166
252
|
},
|
|
167
253
|
},
|
|
168
254
|
} as const;
|
|
169
255
|
|
|
256
|
+
const apiResponseWrapper = (successResponse: z.ZodTypeAny) =>
|
|
257
|
+
z.union([
|
|
258
|
+
z.object({
|
|
259
|
+
success: z.literal(true),
|
|
260
|
+
data: successResponse,
|
|
261
|
+
timestamp: z.string(),
|
|
262
|
+
requestId: z.string(),
|
|
263
|
+
}),
|
|
264
|
+
z.object({
|
|
265
|
+
success: z.literal(false),
|
|
266
|
+
message: z.string(),
|
|
267
|
+
code: z.number(),
|
|
268
|
+
timestamp: z.string(),
|
|
269
|
+
requestId: z.string(),
|
|
270
|
+
}),
|
|
271
|
+
]);
|
|
272
|
+
|
|
170
273
|
const client = new ApiClient(
|
|
171
|
-
{
|
|
274
|
+
{
|
|
275
|
+
baseUrl: "https://api.example.com",
|
|
276
|
+
token: "abc123",
|
|
277
|
+
useMockData: process.env.NODE_ENV === "development",
|
|
278
|
+
},
|
|
172
279
|
contracts
|
|
173
280
|
);
|
|
174
281
|
|
|
175
282
|
client.init();
|
|
283
|
+
client.setResponseWrapper(apiResponseWrapper);
|
|
176
284
|
client.use(LoggingMiddleware);
|
|
177
285
|
client.use(RetryMiddleware, { maxRetries: 2 });
|
|
178
286
|
|
|
@@ -183,7 +291,13 @@ client.onError((err: RichError) => {
|
|
|
183
291
|
const { modules: api } = client;
|
|
184
292
|
|
|
185
293
|
(async () => {
|
|
186
|
-
const
|
|
187
|
-
console.log(
|
|
294
|
+
const user = await api.user.getUser({ id: "1" });
|
|
295
|
+
console.log(user);
|
|
188
296
|
})();
|
|
189
297
|
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## License
|
|
302
|
+
|
|
303
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -6,6 +6,7 @@ type EndpointDef<TReq extends z.ZodTypeAny, TRes extends z.ZodTypeAny> = {
|
|
|
6
6
|
auth?: boolean;
|
|
7
7
|
request: TReq;
|
|
8
8
|
response: TRes;
|
|
9
|
+
mockData?: (() => z.infer<TRes>) | z.infer<TRes>;
|
|
9
10
|
};
|
|
10
11
|
type Contracts = {
|
|
11
12
|
[ModuleName: string]: {
|
|
@@ -45,17 +46,32 @@ declare class ApiClient<C extends Contracts, E extends ErrorLike = RichError> {
|
|
|
45
46
|
private middlewares;
|
|
46
47
|
private errorHandler?;
|
|
47
48
|
private responseTransform;
|
|
49
|
+
private useMockData;
|
|
50
|
+
private mockDelay;
|
|
51
|
+
private responseWrapper?;
|
|
48
52
|
private _modules;
|
|
49
53
|
constructor(config: {
|
|
50
54
|
baseUrl: string;
|
|
51
55
|
token?: string;
|
|
56
|
+
useMockData?: boolean;
|
|
57
|
+
mockDelay?: {
|
|
58
|
+
min: number;
|
|
59
|
+
max: number;
|
|
60
|
+
};
|
|
52
61
|
}, contracts: C);
|
|
53
62
|
init(): void;
|
|
54
63
|
get modules(): { [M in keyof C]: EndpointMethods<C[M]>; };
|
|
55
64
|
use<T>(middleware: Middleware<T>, options?: T): void;
|
|
56
65
|
onError(handler: (error: E) => void): void;
|
|
57
66
|
useResponseTransform(fn: (data: any) => any): void;
|
|
67
|
+
setMockMode(enabled: boolean, delay?: {
|
|
68
|
+
min: number;
|
|
69
|
+
max: number;
|
|
70
|
+
}): void;
|
|
71
|
+
setResponseWrapper(wrapper: (successResponse: z.ZodTypeAny) => z.ZodTypeAny): void;
|
|
58
72
|
private request;
|
|
73
|
+
private handleMockRequest;
|
|
74
|
+
private getRandomDelay;
|
|
59
75
|
private createError;
|
|
60
76
|
private normalizeError;
|
|
61
77
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ type EndpointDef<TReq extends z.ZodTypeAny, TRes extends z.ZodTypeAny> = {
|
|
|
6
6
|
auth?: boolean;
|
|
7
7
|
request: TReq;
|
|
8
8
|
response: TRes;
|
|
9
|
+
mockData?: (() => z.infer<TRes>) | z.infer<TRes>;
|
|
9
10
|
};
|
|
10
11
|
type Contracts = {
|
|
11
12
|
[ModuleName: string]: {
|
|
@@ -45,17 +46,32 @@ declare class ApiClient<C extends Contracts, E extends ErrorLike = RichError> {
|
|
|
45
46
|
private middlewares;
|
|
46
47
|
private errorHandler?;
|
|
47
48
|
private responseTransform;
|
|
49
|
+
private useMockData;
|
|
50
|
+
private mockDelay;
|
|
51
|
+
private responseWrapper?;
|
|
48
52
|
private _modules;
|
|
49
53
|
constructor(config: {
|
|
50
54
|
baseUrl: string;
|
|
51
55
|
token?: string;
|
|
56
|
+
useMockData?: boolean;
|
|
57
|
+
mockDelay?: {
|
|
58
|
+
min: number;
|
|
59
|
+
max: number;
|
|
60
|
+
};
|
|
52
61
|
}, contracts: C);
|
|
53
62
|
init(): void;
|
|
54
63
|
get modules(): { [M in keyof C]: EndpointMethods<C[M]>; };
|
|
55
64
|
use<T>(middleware: Middleware<T>, options?: T): void;
|
|
56
65
|
onError(handler: (error: E) => void): void;
|
|
57
66
|
useResponseTransform(fn: (data: any) => any): void;
|
|
67
|
+
setMockMode(enabled: boolean, delay?: {
|
|
68
|
+
min: number;
|
|
69
|
+
max: number;
|
|
70
|
+
}): void;
|
|
71
|
+
setResponseWrapper(wrapper: (successResponse: z.ZodTypeAny) => z.ZodTypeAny): void;
|
|
58
72
|
private request;
|
|
73
|
+
private handleMockRequest;
|
|
74
|
+
private getRandomDelay;
|
|
59
75
|
private createError;
|
|
60
76
|
private normalizeError;
|
|
61
77
|
}
|
package/dist/index.js
CHANGED
|
@@ -79,6 +79,10 @@ var ApiClient = class {
|
|
|
79
79
|
this.contracts = contracts;
|
|
80
80
|
this.middlewares = [];
|
|
81
81
|
this.responseTransform = (d) => d;
|
|
82
|
+
this.useMockData = false;
|
|
83
|
+
this.mockDelay = { min: 100, max: 1e3 };
|
|
84
|
+
this.useMockData = config.useMockData || false;
|
|
85
|
+
this.mockDelay = config.mockDelay || { min: 100, max: 1e3 };
|
|
82
86
|
}
|
|
83
87
|
init() {
|
|
84
88
|
const modules = {};
|
|
@@ -104,10 +108,22 @@ var ApiClient = class {
|
|
|
104
108
|
useResponseTransform(fn) {
|
|
105
109
|
this.responseTransform = fn;
|
|
106
110
|
}
|
|
111
|
+
setMockMode(enabled, delay) {
|
|
112
|
+
this.useMockData = enabled;
|
|
113
|
+
if (delay) {
|
|
114
|
+
this.mockDelay = delay;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
setResponseWrapper(wrapper) {
|
|
118
|
+
this.responseWrapper = wrapper;
|
|
119
|
+
}
|
|
107
120
|
request(endpoint, input) {
|
|
108
121
|
return __async(this, null, function* () {
|
|
109
|
-
var _a, _b, _c;
|
|
122
|
+
var _a, _b, _c, _d;
|
|
110
123
|
endpoint.request.parse(input);
|
|
124
|
+
if (this.useMockData && endpoint.mockData) {
|
|
125
|
+
return this.handleMockRequest(endpoint);
|
|
126
|
+
}
|
|
111
127
|
if (endpoint.auth && !this.config.token) {
|
|
112
128
|
const error = this.createError({
|
|
113
129
|
message: `Missing token for ${endpoint.path}`,
|
|
@@ -134,28 +150,73 @@ var ApiClient = class {
|
|
|
134
150
|
);
|
|
135
151
|
try {
|
|
136
152
|
const res = yield runner();
|
|
153
|
+
const json = yield res.json();
|
|
154
|
+
let responseData = json;
|
|
155
|
+
if (this.responseWrapper) {
|
|
156
|
+
const wrappedSchema = this.responseWrapper(endpoint.response);
|
|
157
|
+
const parsedResponse = wrappedSchema.parse(json);
|
|
158
|
+
if (parsedResponse.success === false) {
|
|
159
|
+
const error = this.createError({
|
|
160
|
+
message: parsedResponse.message || "Request failed",
|
|
161
|
+
status: parsedResponse.code || res.status,
|
|
162
|
+
code: `API_ERROR_${parsedResponse.code}`
|
|
163
|
+
});
|
|
164
|
+
(_b = this.errorHandler) == null ? void 0 : _b.call(this, error);
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
responseData = parsedResponse.data;
|
|
168
|
+
}
|
|
137
169
|
if (!res.ok) {
|
|
138
|
-
const errorData = yield res.json().catch(() => ({}));
|
|
139
170
|
const error = this.createError({
|
|
140
|
-
message:
|
|
171
|
+
message: json.message || res.statusText,
|
|
141
172
|
status: res.status,
|
|
142
|
-
code:
|
|
143
|
-
title:
|
|
144
|
-
detail:
|
|
145
|
-
errors:
|
|
173
|
+
code: json.code,
|
|
174
|
+
title: json.title,
|
|
175
|
+
detail: json.detail,
|
|
176
|
+
errors: json.errors
|
|
146
177
|
});
|
|
147
|
-
(
|
|
178
|
+
(_c = this.errorHandler) == null ? void 0 : _c.call(this, error);
|
|
148
179
|
throw error;
|
|
149
180
|
}
|
|
150
|
-
|
|
151
|
-
return this.responseTransform(endpoint.response.parse(json));
|
|
181
|
+
return this.responseTransform(endpoint.response.parse(responseData));
|
|
152
182
|
} catch (err) {
|
|
153
183
|
const error = this.normalizeError(err);
|
|
154
|
-
(
|
|
184
|
+
(_d = this.errorHandler) == null ? void 0 : _d.call(this, error);
|
|
155
185
|
throw error;
|
|
156
186
|
}
|
|
157
187
|
});
|
|
158
188
|
}
|
|
189
|
+
handleMockRequest(endpoint) {
|
|
190
|
+
return __async(this, null, function* () {
|
|
191
|
+
const delay = this.getRandomDelay();
|
|
192
|
+
yield new Promise((resolve) => setTimeout(resolve, delay));
|
|
193
|
+
let mockData;
|
|
194
|
+
if (typeof endpoint.mockData === "function") {
|
|
195
|
+
mockData = endpoint.mockData();
|
|
196
|
+
} else {
|
|
197
|
+
mockData = endpoint.mockData;
|
|
198
|
+
}
|
|
199
|
+
if (this.responseWrapper) {
|
|
200
|
+
const wrappedSchema = this.responseWrapper(endpoint.response);
|
|
201
|
+
const mockWrappedResponse = {
|
|
202
|
+
success: true,
|
|
203
|
+
data: mockData,
|
|
204
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
205
|
+
requestId: `mock-${Math.random().toString(36).substr(2, 9)}`
|
|
206
|
+
};
|
|
207
|
+
const parsedWrappedResponse = wrappedSchema.parse(mockWrappedResponse);
|
|
208
|
+
return this.responseTransform(
|
|
209
|
+
endpoint.response.parse(parsedWrappedResponse.data)
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
return this.responseTransform(endpoint.response.parse(mockData));
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
getRandomDelay() {
|
|
216
|
+
return Math.floor(
|
|
217
|
+
Math.random() * (this.mockDelay.max - this.mockDelay.min + 1)
|
|
218
|
+
) + this.mockDelay.min;
|
|
219
|
+
}
|
|
159
220
|
createError(error) {
|
|
160
221
|
return new RichError(error);
|
|
161
222
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/middlewares/logging.ts","../src/middlewares/retry.ts","../src/middlewares/auth.ts","../src/middlewares/cache.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./client\";\nexport * from \"./middlewares/logging\";\nexport * from \"./middlewares/retry\";\nexport * from \"./middlewares/auth\";\nexport * from \"./middlewares/cache\";\n","import {\n Contracts,\n EndpointDef,\n EndpointDefZ,\n Middleware,\n ErrorLike,\n EndpointMethods,\n} from \"@/types\";\nimport { z } from \"zod\";\n\nexport class RichError extends Error implements ErrorLike {\n status?: number;\n code?: string;\n title?: string;\n detail?: string;\n errors?: Record<string, string[]>;\n\n constructor(error: Partial<ErrorLike> & { message: string }) {\n super(error.message);\n Object.assign(this, error);\n }\n}\n\nexport class ApiClient<C extends Contracts, E extends ErrorLike = RichError> {\n private middlewares: Array<{ fn: Middleware; options?: any }> = [];\n private errorHandler?: (error: E) => void;\n private responseTransform: (data: any) => any = (d) => d;\n\n private _modules!: {\n [M in keyof C]: EndpointMethods<C[M]>;\n };\n\n constructor(\n private config: { baseUrl: string; token?: string },\n private contracts: C\n ) {}\n\n init() {\n const modules = {} as {\n [M in keyof C]: EndpointMethods<C[M]>;\n };\n\n for (const moduleName in this.contracts) {\n const module = this.contracts[moduleName];\n (modules as any)[moduleName] = {} as EndpointMethods<typeof module>;\n\n for (const endpointName in module) {\n const endpoint = module[endpointName] as EndpointDefZ;\n\n (modules as any)[moduleName][endpointName] = (\n input: z.infer<(typeof endpoint)[\"request\"]>\n ) => this.request(endpoint, input);\n }\n }\n\n this._modules = modules;\n }\n\n get modules() {\n return this._modules;\n }\n\n use<T>(middleware: Middleware<T>, options?: T) {\n this.middlewares.push({ fn: middleware, options });\n }\n\n onError(handler: (error: E) => void) {\n this.errorHandler = handler;\n }\n\n useResponseTransform(fn: (data: any) => any) {\n this.responseTransform = fn;\n }\n\n private async request<TReq extends z.ZodTypeAny, TRes extends z.ZodTypeAny>(\n endpoint: EndpointDef<TReq, TRes>,\n input: z.infer<TReq>\n ): Promise<z.infer<TRes>> {\n endpoint.request.parse(input);\n\n if (endpoint.auth && !this.config.token) {\n const error = this.createError({\n message: `Missing token for ${endpoint.path}`,\n status: 401,\n code: \"NO_TOKEN\",\n });\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n\n const headers: HeadersInit = { \"Content-Type\": \"application/json\" };\n if (endpoint.auth && this.config.token)\n headers[\"Authorization\"] = `Bearer ${this.config.token}`;\n\n const ctx = {\n url: this.config.baseUrl + endpoint.path,\n init: {\n method: endpoint.method,\n headers,\n body: endpoint.method !== \"GET\" ? JSON.stringify(input) : undefined,\n },\n };\n\n const runner = this.middlewares.reduceRight(\n (next, mw) => () => mw.fn(ctx, next, mw.options),\n () => fetch(ctx.url, ctx.init)\n );\n\n try {\n const res = await runner();\n if (!res.ok) {\n const errorData = await res.json().catch(() => ({}));\n const error = this.createError({\n message: errorData.message || res.statusText,\n status: res.status,\n code: errorData.code,\n title: errorData.title,\n detail: errorData.detail,\n errors: errorData.errors,\n });\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n\n const json = await res.json();\n return this.responseTransform(endpoint.response.parse(json));\n } catch (err: any) {\n const error = this.normalizeError(err);\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n }\n\n private createError(error: Partial<RichError> & { message: string }) {\n return new RichError(error);\n }\n\n private normalizeError(err: any) {\n if (err instanceof RichError) return err;\n return this.createError({ message: err.message || \"Unknown error\" });\n }\n}\n","import { Middleware } from \"@/types\";\n\nexport type LoggingOptions = {\n logRequest?: boolean;\n logResponse?: boolean;\n debug?: boolean;\n};\n\nexport const loggingMiddleware: Middleware<LoggingOptions> = async (\n ctx,\n next,\n options\n) => {\n const { logRequest = true, logResponse = true, debug = true } = options || {};\n\n if (debug && logRequest) console.log(\"➡️ Request:\", ctx.url, ctx.init);\n\n const res = await next();\n\n if (debug && logResponse) console.log(\"⬅️ Response:\", res.status);\n\n return res;\n};\n","import { Middleware } from \"@/types\";\n\n\nexport type RetryOptions = {\n maxRetries?: number;\n delay?: number; // ms\n};\n\nexport const retryMiddleware = (options?: RetryOptions): Middleware => {\n const { maxRetries = 3, delay = 500 } = options || {};\n\n const middleware: Middleware = async (ctx, next) => {\n let attempt = 0;\n while (true) {\n try {\n return await next();\n } catch (err) {\n if (attempt >= maxRetries) throw err;\n attempt++;\n await new Promise((r) => setTimeout(r, delay * 2 ** attempt));\n }\n }\n };\n\n return middleware;\n};\n","import { Middleware } from \"@/types\";\n\nexport type AuthOptions = {\n refreshToken?: () => Promise<string>;\n};\n\nexport const authMiddleware: Middleware<AuthOptions> = async (\n ctx,\n next,\n options\n) => {\n if (options?.refreshToken) {\n try {\n const newToken = await options.refreshToken();\n ctx.init.headers = {\n ...ctx.init.headers,\n Authorization: `Bearer ${newToken}`,\n };\n } catch {}\n }\n\n return next();\n};\n","import { MiddlewareContext, MiddlewareNext } from \"@/types\";\n\nexport type CacheOptions = { ttl?: number };\n\nexport const cacheMiddleware = (options: CacheOptions = {}) => {\n const { ttl = 60000 } = options;\n const cache = new Map<string, { data: any; expires: number }>();\n\n return async (ctx: MiddlewareContext, next: MiddlewareNext) => {\n if (ctx.init.method === \"GET\") {\n const cached = cache.get(ctx.url);\n const now = Date.now();\n if (cached && cached.expires > now)\n return new Response(JSON.stringify(cached.data));\n\n const res = await next();\n const data = await res\n .clone()\n .json()\n .catch(() => null);\n if (data) cache.set(ctx.url, { data, expires: now + ttl });\n return res;\n }\n return next();\n };\n};\n"],"mappings":"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","names":["module"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/middlewares/logging.ts","../src/middlewares/retry.ts","../src/middlewares/auth.ts","../src/middlewares/cache.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./client\";\nexport * from \"./middlewares/logging\";\nexport * from \"./middlewares/retry\";\nexport * from \"./middlewares/auth\";\nexport * from \"./middlewares/cache\";\n","import {\n Contracts,\n EndpointDef,\n EndpointDefZ,\n Middleware,\n ErrorLike,\n EndpointMethods,\n} from \"@/types\";\nimport { z } from \"zod\";\n\nexport class RichError extends Error implements ErrorLike {\n status?: number;\n code?: string;\n title?: string;\n detail?: string;\n errors?: Record<string, string[]>;\n\n constructor(error: Partial<ErrorLike> & { message: string }) {\n super(error.message);\n Object.assign(this, error);\n }\n}\n\nexport class ApiClient<C extends Contracts, E extends ErrorLike = RichError> {\n private middlewares: Array<{ fn: Middleware; options?: any }> = [];\n private errorHandler?: (error: E) => void;\n private responseTransform: (data: any) => any = (d) => d;\n private useMockData: boolean = false;\n private mockDelay: { min: number; max: number } = { min: 100, max: 1000 };\n private responseWrapper?: (successResponse: z.ZodTypeAny) => z.ZodTypeAny;\n\n private _modules!: {\n [M in keyof C]: EndpointMethods<C[M]>;\n };\n\n constructor(\n private config: {\n baseUrl: string;\n token?: string;\n useMockData?: boolean;\n mockDelay?: { min: number; max: number };\n },\n private contracts: C\n ) {\n this.useMockData = config.useMockData || false;\n this.mockDelay = config.mockDelay || { min: 100, max: 1000 };\n }\n\n init() {\n const modules = {} as {\n [M in keyof C]: EndpointMethods<C[M]>;\n };\n\n for (const moduleName in this.contracts) {\n const module = this.contracts[moduleName];\n (modules as any)[moduleName] = {} as EndpointMethods<typeof module>;\n\n for (const endpointName in module) {\n const endpoint = module[endpointName] as EndpointDefZ;\n\n (modules as any)[moduleName][endpointName] = (\n input: z.infer<(typeof endpoint)[\"request\"]>\n ) => this.request(endpoint, input);\n }\n }\n\n this._modules = modules;\n }\n\n get modules() {\n return this._modules;\n }\n\n use<T>(middleware: Middleware<T>, options?: T) {\n this.middlewares.push({ fn: middleware, options });\n }\n\n onError(handler: (error: E) => void) {\n this.errorHandler = handler;\n }\n\n useResponseTransform(fn: (data: any) => any) {\n this.responseTransform = fn;\n }\n\n setMockMode(enabled: boolean, delay?: { min: number; max: number }) {\n this.useMockData = enabled;\n if (delay) {\n this.mockDelay = delay;\n }\n }\n\n setResponseWrapper(wrapper: (successResponse: z.ZodTypeAny) => z.ZodTypeAny) {\n this.responseWrapper = wrapper;\n }\n\n private async request<TReq extends z.ZodTypeAny, TRes extends z.ZodTypeAny>(\n endpoint: EndpointDef<TReq, TRes>,\n input: z.infer<TReq>\n ): Promise<z.infer<TRes>> {\n endpoint.request.parse(input);\n\n if (this.useMockData && endpoint.mockData) {\n return this.handleMockRequest(endpoint);\n }\n\n if (endpoint.auth && !this.config.token) {\n const error = this.createError({\n message: `Missing token for ${endpoint.path}`,\n status: 401,\n code: \"NO_TOKEN\",\n });\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n\n const headers: HeadersInit = { \"Content-Type\": \"application/json\" };\n if (endpoint.auth && this.config.token)\n headers[\"Authorization\"] = `Bearer ${this.config.token}`;\n\n const ctx = {\n url: this.config.baseUrl + endpoint.path,\n init: {\n method: endpoint.method,\n headers,\n body: endpoint.method !== \"GET\" ? JSON.stringify(input) : undefined,\n },\n };\n\n const runner = this.middlewares.reduceRight(\n (next, mw) => () => mw.fn(ctx, next, mw.options),\n () => fetch(ctx.url, ctx.init)\n );\n\n try {\n const res = await runner();\n const json = await res.json();\n\n let responseData = json;\n if (this.responseWrapper) {\n const wrappedSchema = this.responseWrapper(endpoint.response);\n const parsedResponse = wrappedSchema.parse(json);\n\n if (parsedResponse.success === false) {\n const error = this.createError({\n message: parsedResponse.message || \"Request failed\",\n status: parsedResponse.code || res.status,\n code: `API_ERROR_${parsedResponse.code}`,\n });\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n\n responseData = parsedResponse.data;\n }\n\n if (!res.ok) {\n const error = this.createError({\n message: json.message || res.statusText,\n status: res.status,\n code: json.code,\n title: json.title,\n detail: json.detail,\n errors: json.errors,\n });\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n\n return this.responseTransform(endpoint.response.parse(responseData));\n } catch (err: any) {\n const error = this.normalizeError(err);\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n }\n\n private async handleMockRequest<\n TReq extends z.ZodTypeAny,\n TRes extends z.ZodTypeAny\n >(endpoint: EndpointDef<TReq, TRes>): Promise<z.infer<TRes>> {\n const delay = this.getRandomDelay();\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n let mockData: z.infer<TRes>;\n if (typeof endpoint.mockData === \"function\") {\n mockData = (endpoint.mockData as () => z.infer<TRes>)();\n } else {\n mockData = endpoint.mockData as z.infer<TRes>;\n }\n\n if (this.responseWrapper) {\n const wrappedSchema = this.responseWrapper(endpoint.response);\n\n const mockWrappedResponse = {\n success: true,\n data: mockData,\n timestamp: new Date().toISOString(),\n requestId: `mock-${Math.random().toString(36).substr(2, 9)}`,\n };\n\n const parsedWrappedResponse = wrappedSchema.parse(mockWrappedResponse);\n\n return this.responseTransform(\n endpoint.response.parse(parsedWrappedResponse.data)\n );\n }\n\n return this.responseTransform(endpoint.response.parse(mockData));\n }\n\n private getRandomDelay(): number {\n return (\n Math.floor(\n Math.random() * (this.mockDelay.max - this.mockDelay.min + 1)\n ) + this.mockDelay.min\n );\n }\n\n private createError(error: Partial<RichError> & { message: string }) {\n return new RichError(error);\n }\n\n private normalizeError(err: any) {\n if (err instanceof RichError) return err;\n return this.createError({ message: err.message || \"Unknown error\" });\n }\n}\n","import { Middleware } from \"@/types\";\n\nexport type LoggingOptions = {\n logRequest?: boolean;\n logResponse?: boolean;\n debug?: boolean;\n};\n\nexport const loggingMiddleware: Middleware<LoggingOptions> = async (\n ctx,\n next,\n options\n) => {\n const { logRequest = true, logResponse = true, debug = true } = options || {};\n\n if (debug && logRequest) console.log(\"➡️ Request:\", ctx.url, ctx.init);\n\n const res = await next();\n\n if (debug && logResponse) console.log(\"⬅️ Response:\", res.status);\n\n return res;\n};\n","import { Middleware } from \"@/types\";\n\n\nexport type RetryOptions = {\n maxRetries?: number;\n delay?: number; // ms\n};\n\nexport const retryMiddleware = (options?: RetryOptions): Middleware => {\n const { maxRetries = 3, delay = 500 } = options || {};\n\n const middleware: Middleware = async (ctx, next) => {\n let attempt = 0;\n while (true) {\n try {\n return await next();\n } catch (err) {\n if (attempt >= maxRetries) throw err;\n attempt++;\n await new Promise((r) => setTimeout(r, delay * 2 ** attempt));\n }\n }\n };\n\n return middleware;\n};\n","import { Middleware } from \"@/types\";\n\nexport type AuthOptions = {\n refreshToken?: () => Promise<string>;\n};\n\nexport const authMiddleware: Middleware<AuthOptions> = async (\n ctx,\n next,\n options\n) => {\n if (options?.refreshToken) {\n try {\n const newToken = await options.refreshToken();\n ctx.init.headers = {\n ...ctx.init.headers,\n Authorization: `Bearer ${newToken}`,\n };\n } catch {}\n }\n\n return next();\n};\n","import { MiddlewareContext, MiddlewareNext } from \"@/types\";\n\nexport type CacheOptions = { ttl?: number };\n\nexport const cacheMiddleware = (options: CacheOptions = {}) => {\n const { ttl = 60000 } = options;\n const cache = new Map<string, { data: any; expires: number }>();\n\n return async (ctx: MiddlewareContext, next: MiddlewareNext) => {\n if (ctx.init.method === \"GET\") {\n const cached = cache.get(ctx.url);\n const now = Date.now();\n if (cached && cached.expires > now)\n return new Response(JSON.stringify(cached.data));\n\n const res = await next();\n const data = await res\n .clone()\n .json()\n .catch(() => null);\n if (data) cache.set(ctx.url, { data, expires: now + ttl });\n return res;\n }\n return next();\n };\n};\n"],"mappings":"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","names":["module"]}
|
package/dist/index.mjs
CHANGED
|
@@ -52,6 +52,10 @@ var ApiClient = class {
|
|
|
52
52
|
this.contracts = contracts;
|
|
53
53
|
this.middlewares = [];
|
|
54
54
|
this.responseTransform = (d) => d;
|
|
55
|
+
this.useMockData = false;
|
|
56
|
+
this.mockDelay = { min: 100, max: 1e3 };
|
|
57
|
+
this.useMockData = config.useMockData || false;
|
|
58
|
+
this.mockDelay = config.mockDelay || { min: 100, max: 1e3 };
|
|
55
59
|
}
|
|
56
60
|
init() {
|
|
57
61
|
const modules = {};
|
|
@@ -77,10 +81,22 @@ var ApiClient = class {
|
|
|
77
81
|
useResponseTransform(fn) {
|
|
78
82
|
this.responseTransform = fn;
|
|
79
83
|
}
|
|
84
|
+
setMockMode(enabled, delay) {
|
|
85
|
+
this.useMockData = enabled;
|
|
86
|
+
if (delay) {
|
|
87
|
+
this.mockDelay = delay;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
setResponseWrapper(wrapper) {
|
|
91
|
+
this.responseWrapper = wrapper;
|
|
92
|
+
}
|
|
80
93
|
request(endpoint, input) {
|
|
81
94
|
return __async(this, null, function* () {
|
|
82
|
-
var _a, _b, _c;
|
|
95
|
+
var _a, _b, _c, _d;
|
|
83
96
|
endpoint.request.parse(input);
|
|
97
|
+
if (this.useMockData && endpoint.mockData) {
|
|
98
|
+
return this.handleMockRequest(endpoint);
|
|
99
|
+
}
|
|
84
100
|
if (endpoint.auth && !this.config.token) {
|
|
85
101
|
const error = this.createError({
|
|
86
102
|
message: `Missing token for ${endpoint.path}`,
|
|
@@ -107,28 +123,73 @@ var ApiClient = class {
|
|
|
107
123
|
);
|
|
108
124
|
try {
|
|
109
125
|
const res = yield runner();
|
|
126
|
+
const json = yield res.json();
|
|
127
|
+
let responseData = json;
|
|
128
|
+
if (this.responseWrapper) {
|
|
129
|
+
const wrappedSchema = this.responseWrapper(endpoint.response);
|
|
130
|
+
const parsedResponse = wrappedSchema.parse(json);
|
|
131
|
+
if (parsedResponse.success === false) {
|
|
132
|
+
const error = this.createError({
|
|
133
|
+
message: parsedResponse.message || "Request failed",
|
|
134
|
+
status: parsedResponse.code || res.status,
|
|
135
|
+
code: `API_ERROR_${parsedResponse.code}`
|
|
136
|
+
});
|
|
137
|
+
(_b = this.errorHandler) == null ? void 0 : _b.call(this, error);
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
responseData = parsedResponse.data;
|
|
141
|
+
}
|
|
110
142
|
if (!res.ok) {
|
|
111
|
-
const errorData = yield res.json().catch(() => ({}));
|
|
112
143
|
const error = this.createError({
|
|
113
|
-
message:
|
|
144
|
+
message: json.message || res.statusText,
|
|
114
145
|
status: res.status,
|
|
115
|
-
code:
|
|
116
|
-
title:
|
|
117
|
-
detail:
|
|
118
|
-
errors:
|
|
146
|
+
code: json.code,
|
|
147
|
+
title: json.title,
|
|
148
|
+
detail: json.detail,
|
|
149
|
+
errors: json.errors
|
|
119
150
|
});
|
|
120
|
-
(
|
|
151
|
+
(_c = this.errorHandler) == null ? void 0 : _c.call(this, error);
|
|
121
152
|
throw error;
|
|
122
153
|
}
|
|
123
|
-
|
|
124
|
-
return this.responseTransform(endpoint.response.parse(json));
|
|
154
|
+
return this.responseTransform(endpoint.response.parse(responseData));
|
|
125
155
|
} catch (err) {
|
|
126
156
|
const error = this.normalizeError(err);
|
|
127
|
-
(
|
|
157
|
+
(_d = this.errorHandler) == null ? void 0 : _d.call(this, error);
|
|
128
158
|
throw error;
|
|
129
159
|
}
|
|
130
160
|
});
|
|
131
161
|
}
|
|
162
|
+
handleMockRequest(endpoint) {
|
|
163
|
+
return __async(this, null, function* () {
|
|
164
|
+
const delay = this.getRandomDelay();
|
|
165
|
+
yield new Promise((resolve) => setTimeout(resolve, delay));
|
|
166
|
+
let mockData;
|
|
167
|
+
if (typeof endpoint.mockData === "function") {
|
|
168
|
+
mockData = endpoint.mockData();
|
|
169
|
+
} else {
|
|
170
|
+
mockData = endpoint.mockData;
|
|
171
|
+
}
|
|
172
|
+
if (this.responseWrapper) {
|
|
173
|
+
const wrappedSchema = this.responseWrapper(endpoint.response);
|
|
174
|
+
const mockWrappedResponse = {
|
|
175
|
+
success: true,
|
|
176
|
+
data: mockData,
|
|
177
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
178
|
+
requestId: `mock-${Math.random().toString(36).substr(2, 9)}`
|
|
179
|
+
};
|
|
180
|
+
const parsedWrappedResponse = wrappedSchema.parse(mockWrappedResponse);
|
|
181
|
+
return this.responseTransform(
|
|
182
|
+
endpoint.response.parse(parsedWrappedResponse.data)
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
return this.responseTransform(endpoint.response.parse(mockData));
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
getRandomDelay() {
|
|
189
|
+
return Math.floor(
|
|
190
|
+
Math.random() * (this.mockDelay.max - this.mockDelay.min + 1)
|
|
191
|
+
) + this.mockDelay.min;
|
|
192
|
+
}
|
|
132
193
|
createError(error) {
|
|
133
194
|
return new RichError(error);
|
|
134
195
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/middlewares/logging.ts","../src/middlewares/retry.ts","../src/middlewares/auth.ts","../src/middlewares/cache.ts"],"sourcesContent":["import {\n Contracts,\n EndpointDef,\n EndpointDefZ,\n Middleware,\n ErrorLike,\n EndpointMethods,\n} from \"@/types\";\nimport { z } from \"zod\";\n\nexport class RichError extends Error implements ErrorLike {\n status?: number;\n code?: string;\n title?: string;\n detail?: string;\n errors?: Record<string, string[]>;\n\n constructor(error: Partial<ErrorLike> & { message: string }) {\n super(error.message);\n Object.assign(this, error);\n }\n}\n\nexport class ApiClient<C extends Contracts, E extends ErrorLike = RichError> {\n private middlewares: Array<{ fn: Middleware; options?: any }> = [];\n private errorHandler?: (error: E) => void;\n private responseTransform: (data: any) => any = (d) => d;\n\n private _modules!: {\n [M in keyof C]: EndpointMethods<C[M]>;\n };\n\n constructor(\n private config: { baseUrl: string; token?: string },\n private contracts: C\n ) {}\n\n init() {\n const modules = {} as {\n [M in keyof C]: EndpointMethods<C[M]>;\n };\n\n for (const moduleName in this.contracts) {\n const module = this.contracts[moduleName];\n (modules as any)[moduleName] = {} as EndpointMethods<typeof module>;\n\n for (const endpointName in module) {\n const endpoint = module[endpointName] as EndpointDefZ;\n\n (modules as any)[moduleName][endpointName] = (\n input: z.infer<(typeof endpoint)[\"request\"]>\n ) => this.request(endpoint, input);\n }\n }\n\n this._modules = modules;\n }\n\n get modules() {\n return this._modules;\n }\n\n use<T>(middleware: Middleware<T>, options?: T) {\n this.middlewares.push({ fn: middleware, options });\n }\n\n onError(handler: (error: E) => void) {\n this.errorHandler = handler;\n }\n\n useResponseTransform(fn: (data: any) => any) {\n this.responseTransform = fn;\n }\n\n private async request<TReq extends z.ZodTypeAny, TRes extends z.ZodTypeAny>(\n endpoint: EndpointDef<TReq, TRes>,\n input: z.infer<TReq>\n ): Promise<z.infer<TRes>> {\n endpoint.request.parse(input);\n\n if (endpoint.auth && !this.config.token) {\n const error = this.createError({\n message: `Missing token for ${endpoint.path}`,\n status: 401,\n code: \"NO_TOKEN\",\n });\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n\n const headers: HeadersInit = { \"Content-Type\": \"application/json\" };\n if (endpoint.auth && this.config.token)\n headers[\"Authorization\"] = `Bearer ${this.config.token}`;\n\n const ctx = {\n url: this.config.baseUrl + endpoint.path,\n init: {\n method: endpoint.method,\n headers,\n body: endpoint.method !== \"GET\" ? JSON.stringify(input) : undefined,\n },\n };\n\n const runner = this.middlewares.reduceRight(\n (next, mw) => () => mw.fn(ctx, next, mw.options),\n () => fetch(ctx.url, ctx.init)\n );\n\n try {\n const res = await runner();\n if (!res.ok) {\n const errorData = await res.json().catch(() => ({}));\n const error = this.createError({\n message: errorData.message || res.statusText,\n status: res.status,\n code: errorData.code,\n title: errorData.title,\n detail: errorData.detail,\n errors: errorData.errors,\n });\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n\n const json = await res.json();\n return this.responseTransform(endpoint.response.parse(json));\n } catch (err: any) {\n const error = this.normalizeError(err);\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n }\n\n private createError(error: Partial<RichError> & { message: string }) {\n return new RichError(error);\n }\n\n private normalizeError(err: any) {\n if (err instanceof RichError) return err;\n return this.createError({ message: err.message || \"Unknown error\" });\n }\n}\n","import { Middleware } from \"@/types\";\n\nexport type LoggingOptions = {\n logRequest?: boolean;\n logResponse?: boolean;\n debug?: boolean;\n};\n\nexport const loggingMiddleware: Middleware<LoggingOptions> = async (\n ctx,\n next,\n options\n) => {\n const { logRequest = true, logResponse = true, debug = true } = options || {};\n\n if (debug && logRequest) console.log(\"➡️ Request:\", ctx.url, ctx.init);\n\n const res = await next();\n\n if (debug && logResponse) console.log(\"⬅️ Response:\", res.status);\n\n return res;\n};\n","import { Middleware } from \"@/types\";\n\n\nexport type RetryOptions = {\n maxRetries?: number;\n delay?: number; // ms\n};\n\nexport const retryMiddleware = (options?: RetryOptions): Middleware => {\n const { maxRetries = 3, delay = 500 } = options || {};\n\n const middleware: Middleware = async (ctx, next) => {\n let attempt = 0;\n while (true) {\n try {\n return await next();\n } catch (err) {\n if (attempt >= maxRetries) throw err;\n attempt++;\n await new Promise((r) => setTimeout(r, delay * 2 ** attempt));\n }\n }\n };\n\n return middleware;\n};\n","import { Middleware } from \"@/types\";\n\nexport type AuthOptions = {\n refreshToken?: () => Promise<string>;\n};\n\nexport const authMiddleware: Middleware<AuthOptions> = async (\n ctx,\n next,\n options\n) => {\n if (options?.refreshToken) {\n try {\n const newToken = await options.refreshToken();\n ctx.init.headers = {\n ...ctx.init.headers,\n Authorization: `Bearer ${newToken}`,\n };\n } catch {}\n }\n\n return next();\n};\n","import { MiddlewareContext, MiddlewareNext } from \"@/types\";\n\nexport type CacheOptions = { ttl?: number };\n\nexport const cacheMiddleware = (options: CacheOptions = {}) => {\n const { ttl = 60000 } = options;\n const cache = new Map<string, { data: any; expires: number }>();\n\n return async (ctx: MiddlewareContext, next: MiddlewareNext) => {\n if (ctx.init.method === \"GET\") {\n const cached = cache.get(ctx.url);\n const now = Date.now();\n if (cached && cached.expires > now)\n return new Response(JSON.stringify(cached.data));\n\n const res = await next();\n const data = await res\n .clone()\n .json()\n .catch(() => null);\n if (data) cache.set(ctx.url, { data, expires: now + ttl });\n return res;\n }\n return next();\n };\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/middlewares/logging.ts","../src/middlewares/retry.ts","../src/middlewares/auth.ts","../src/middlewares/cache.ts"],"sourcesContent":["import {\n Contracts,\n EndpointDef,\n EndpointDefZ,\n Middleware,\n ErrorLike,\n EndpointMethods,\n} from \"@/types\";\nimport { z } from \"zod\";\n\nexport class RichError extends Error implements ErrorLike {\n status?: number;\n code?: string;\n title?: string;\n detail?: string;\n errors?: Record<string, string[]>;\n\n constructor(error: Partial<ErrorLike> & { message: string }) {\n super(error.message);\n Object.assign(this, error);\n }\n}\n\nexport class ApiClient<C extends Contracts, E extends ErrorLike = RichError> {\n private middlewares: Array<{ fn: Middleware; options?: any }> = [];\n private errorHandler?: (error: E) => void;\n private responseTransform: (data: any) => any = (d) => d;\n private useMockData: boolean = false;\n private mockDelay: { min: number; max: number } = { min: 100, max: 1000 };\n private responseWrapper?: (successResponse: z.ZodTypeAny) => z.ZodTypeAny;\n\n private _modules!: {\n [M in keyof C]: EndpointMethods<C[M]>;\n };\n\n constructor(\n private config: {\n baseUrl: string;\n token?: string;\n useMockData?: boolean;\n mockDelay?: { min: number; max: number };\n },\n private contracts: C\n ) {\n this.useMockData = config.useMockData || false;\n this.mockDelay = config.mockDelay || { min: 100, max: 1000 };\n }\n\n init() {\n const modules = {} as {\n [M in keyof C]: EndpointMethods<C[M]>;\n };\n\n for (const moduleName in this.contracts) {\n const module = this.contracts[moduleName];\n (modules as any)[moduleName] = {} as EndpointMethods<typeof module>;\n\n for (const endpointName in module) {\n const endpoint = module[endpointName] as EndpointDefZ;\n\n (modules as any)[moduleName][endpointName] = (\n input: z.infer<(typeof endpoint)[\"request\"]>\n ) => this.request(endpoint, input);\n }\n }\n\n this._modules = modules;\n }\n\n get modules() {\n return this._modules;\n }\n\n use<T>(middleware: Middleware<T>, options?: T) {\n this.middlewares.push({ fn: middleware, options });\n }\n\n onError(handler: (error: E) => void) {\n this.errorHandler = handler;\n }\n\n useResponseTransform(fn: (data: any) => any) {\n this.responseTransform = fn;\n }\n\n setMockMode(enabled: boolean, delay?: { min: number; max: number }) {\n this.useMockData = enabled;\n if (delay) {\n this.mockDelay = delay;\n }\n }\n\n setResponseWrapper(wrapper: (successResponse: z.ZodTypeAny) => z.ZodTypeAny) {\n this.responseWrapper = wrapper;\n }\n\n private async request<TReq extends z.ZodTypeAny, TRes extends z.ZodTypeAny>(\n endpoint: EndpointDef<TReq, TRes>,\n input: z.infer<TReq>\n ): Promise<z.infer<TRes>> {\n endpoint.request.parse(input);\n\n if (this.useMockData && endpoint.mockData) {\n return this.handleMockRequest(endpoint);\n }\n\n if (endpoint.auth && !this.config.token) {\n const error = this.createError({\n message: `Missing token for ${endpoint.path}`,\n status: 401,\n code: \"NO_TOKEN\",\n });\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n\n const headers: HeadersInit = { \"Content-Type\": \"application/json\" };\n if (endpoint.auth && this.config.token)\n headers[\"Authorization\"] = `Bearer ${this.config.token}`;\n\n const ctx = {\n url: this.config.baseUrl + endpoint.path,\n init: {\n method: endpoint.method,\n headers,\n body: endpoint.method !== \"GET\" ? JSON.stringify(input) : undefined,\n },\n };\n\n const runner = this.middlewares.reduceRight(\n (next, mw) => () => mw.fn(ctx, next, mw.options),\n () => fetch(ctx.url, ctx.init)\n );\n\n try {\n const res = await runner();\n const json = await res.json();\n\n let responseData = json;\n if (this.responseWrapper) {\n const wrappedSchema = this.responseWrapper(endpoint.response);\n const parsedResponse = wrappedSchema.parse(json);\n\n if (parsedResponse.success === false) {\n const error = this.createError({\n message: parsedResponse.message || \"Request failed\",\n status: parsedResponse.code || res.status,\n code: `API_ERROR_${parsedResponse.code}`,\n });\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n\n responseData = parsedResponse.data;\n }\n\n if (!res.ok) {\n const error = this.createError({\n message: json.message || res.statusText,\n status: res.status,\n code: json.code,\n title: json.title,\n detail: json.detail,\n errors: json.errors,\n });\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n\n return this.responseTransform(endpoint.response.parse(responseData));\n } catch (err: any) {\n const error = this.normalizeError(err);\n this.errorHandler?.(error as unknown as E);\n throw error;\n }\n }\n\n private async handleMockRequest<\n TReq extends z.ZodTypeAny,\n TRes extends z.ZodTypeAny\n >(endpoint: EndpointDef<TReq, TRes>): Promise<z.infer<TRes>> {\n const delay = this.getRandomDelay();\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n let mockData: z.infer<TRes>;\n if (typeof endpoint.mockData === \"function\") {\n mockData = (endpoint.mockData as () => z.infer<TRes>)();\n } else {\n mockData = endpoint.mockData as z.infer<TRes>;\n }\n\n if (this.responseWrapper) {\n const wrappedSchema = this.responseWrapper(endpoint.response);\n\n const mockWrappedResponse = {\n success: true,\n data: mockData,\n timestamp: new Date().toISOString(),\n requestId: `mock-${Math.random().toString(36).substr(2, 9)}`,\n };\n\n const parsedWrappedResponse = wrappedSchema.parse(mockWrappedResponse);\n\n return this.responseTransform(\n endpoint.response.parse(parsedWrappedResponse.data)\n );\n }\n\n return this.responseTransform(endpoint.response.parse(mockData));\n }\n\n private getRandomDelay(): number {\n return (\n Math.floor(\n Math.random() * (this.mockDelay.max - this.mockDelay.min + 1)\n ) + this.mockDelay.min\n );\n }\n\n private createError(error: Partial<RichError> & { message: string }) {\n return new RichError(error);\n }\n\n private normalizeError(err: any) {\n if (err instanceof RichError) return err;\n return this.createError({ message: err.message || \"Unknown error\" });\n }\n}\n","import { Middleware } from \"@/types\";\n\nexport type LoggingOptions = {\n logRequest?: boolean;\n logResponse?: boolean;\n debug?: boolean;\n};\n\nexport const loggingMiddleware: Middleware<LoggingOptions> = async (\n ctx,\n next,\n options\n) => {\n const { logRequest = true, logResponse = true, debug = true } = options || {};\n\n if (debug && logRequest) console.log(\"➡️ Request:\", ctx.url, ctx.init);\n\n const res = await next();\n\n if (debug && logResponse) console.log(\"⬅️ Response:\", res.status);\n\n return res;\n};\n","import { Middleware } from \"@/types\";\n\n\nexport type RetryOptions = {\n maxRetries?: number;\n delay?: number; // ms\n};\n\nexport const retryMiddleware = (options?: RetryOptions): Middleware => {\n const { maxRetries = 3, delay = 500 } = options || {};\n\n const middleware: Middleware = async (ctx, next) => {\n let attempt = 0;\n while (true) {\n try {\n return await next();\n } catch (err) {\n if (attempt >= maxRetries) throw err;\n attempt++;\n await new Promise((r) => setTimeout(r, delay * 2 ** attempt));\n }\n }\n };\n\n return middleware;\n};\n","import { Middleware } from \"@/types\";\n\nexport type AuthOptions = {\n refreshToken?: () => Promise<string>;\n};\n\nexport const authMiddleware: Middleware<AuthOptions> = async (\n ctx,\n next,\n options\n) => {\n if (options?.refreshToken) {\n try {\n const newToken = await options.refreshToken();\n ctx.init.headers = {\n ...ctx.init.headers,\n Authorization: `Bearer ${newToken}`,\n };\n } catch {}\n }\n\n return next();\n};\n","import { MiddlewareContext, MiddlewareNext } from \"@/types\";\n\nexport type CacheOptions = { ttl?: number };\n\nexport const cacheMiddleware = (options: CacheOptions = {}) => {\n const { ttl = 60000 } = options;\n const cache = new Map<string, { data: any; expires: number }>();\n\n return async (ctx: MiddlewareContext, next: MiddlewareNext) => {\n if (ctx.init.method === \"GET\") {\n const cached = cache.get(ctx.url);\n const now = Date.now();\n if (cached && cached.expires > now)\n return new Response(JSON.stringify(cached.data));\n\n const res = await next();\n const data = await res\n .clone()\n .json()\n .catch(() => null);\n if (data) cache.set(ctx.url, { data, expires: now + ttl });\n return res;\n }\n return next();\n };\n};\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tahanabavi/typefetch",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A fully type-safe, extensible API client for TypeScript projects, featuring global error handling, configurable middleware, automatic retries, auth refresh, response transforms, and seamless contract integration. Designed for large-scale applications and developer-friendly API interactions.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|