@0xshariq/voxa-core 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/CHANGELOG.md +250 -0
- package/LICENSE +21 -0
- package/README.md +782 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/client/http-methods.d.ts +1 -0
- package/dist/lib/client/http-methods.js +1 -0
- package/dist/lib/client/logging.d.ts +1 -0
- package/dist/lib/client/logging.js +1 -0
- package/dist/lib/client/request.d.ts +1 -0
- package/dist/lib/client/request.js +1 -0
- package/dist/lib/client/security.d.ts +1 -0
- package/dist/lib/client/security.js +1 -0
- package/dist/lib/client/utils.d.ts +1 -0
- package/dist/lib/client/utils.js +1 -0
- package/dist/lib/client/voxa.d.ts +1 -0
- package/dist/lib/client/voxa.js +1 -0
- package/dist/lib/features/cache/file-storage.d.ts +1 -0
- package/dist/lib/features/cache/file-storage.js +1 -0
- package/dist/lib/features/cache/manager.d.ts +1 -0
- package/dist/lib/features/cache/manager.js +1 -0
- package/dist/lib/features/deduplication/manager.d.ts +1 -0
- package/dist/lib/features/deduplication/manager.js +1 -0
- package/dist/lib/features/errors/classifier.d.ts +1 -0
- package/dist/lib/features/errors/classifier.js +1 -0
- package/dist/lib/features/interceptors/manager.d.ts +1 -0
- package/dist/lib/features/interceptors/manager.js +1 -0
- package/dist/lib/features/logging/file-logger.d.ts +1 -0
- package/dist/lib/features/logging/file-logger.js +1 -0
- package/dist/lib/features/metadata/manager.d.ts +1 -0
- package/dist/lib/features/metadata/manager.js +1 -0
- package/dist/lib/features/queue/manager.d.ts +1 -0
- package/dist/lib/features/queue/manager.js +1 -0
- package/dist/lib/features/rate/limiter.d.ts +1 -0
- package/dist/lib/features/rate/limiter.js +1 -0
- package/dist/lib/features/retry/manager.d.ts +1 -0
- package/dist/lib/features/retry/manager.js +1 -0
- package/dist/lib/features/schema/validator.d.ts +1 -0
- package/dist/lib/features/schema/validator.js +1 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/types/client-types.d.ts +1 -0
- package/dist/lib/types/client-types.js +1 -0
- package/dist/lib/version.d.ts +1 -0
- package/dist/lib/version.js +1 -0
- package/package.json +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
# Voxa HTTP Client
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<em>Modern, feature-rich HTTP client for Node.js and browsers, built on the native Fetch API. Modular architecture with separate feature packages for optimal bundle size.</em>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/@0xshariq/voxa-core"><img src="https://img.shields.io/npm/v/@0xshariq/voxa-core.svg" alt="npm version"></a>
|
|
9
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## π Why Voxa?
|
|
15
|
+
|
|
16
|
+
Voxa isn't just another HTTP clientβit's a **complete request management system** with a **modular architecture**. Install only the features you need, keeping your bundle size minimal while having access to advanced capabilities that Axios, Fetch, and other popular clients simply don't have.
|
|
17
|
+
|
|
18
|
+
### π― Modular Design
|
|
19
|
+
|
|
20
|
+
Voxa is split into multiple packages:
|
|
21
|
+
|
|
22
|
+
- **@0xshariq/voxa-core** (~140KB) - Essential HTTP client with caching, retry, queue, rate limiting, and deduplication
|
|
23
|
+
- **@0xshariq/voxa-streaming-images** - Image streaming with progress tracking
|
|
24
|
+
- **@0xshariq/voxa-streaming-videos** - Video streaming with progress tracking
|
|
25
|
+
- **@0xshariq/voxa-http2** - HTTP/2 Server Push support
|
|
26
|
+
- **@0xshariq/voxa-graphql** - GraphQL query support
|
|
27
|
+
- **@0xshariq/voxa-batch** - Request batching
|
|
28
|
+
- **@0xshariq/voxa-offline** - Offline queue management
|
|
29
|
+
- **@0xshariq/voxa-circuit-breaker** - Circuit breaker pattern
|
|
30
|
+
- **@0xshariq/voxa-token** - OAuth/JWT token management
|
|
31
|
+
- **@0xshariq/voxa-metrics** - Performance metrics tracking
|
|
32
|
+
- **@0xshariq/voxa-cancel** - Advanced request cancellation
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Future Plans
|
|
37
|
+
|
|
38
|
+
- Voxa CLI (Under Development)
|
|
39
|
+
- Voxa API (Private API only for trusted users and organizations) (Done)
|
|
40
|
+
- Voxa SDKs (Go,Rust,Python and Ruby) (Not Planned Yet)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Feature Comparison
|
|
45
|
+
|
|
46
|
+
| Feature | Voxa | Axios | Fetch | ky | Got | node-fetch |
|
|
47
|
+
| --------------------------------- | :---------: | :------: | :------: | :------: | :---------: | :--------: |
|
|
48
|
+
| **Bundle Size** | 140KB-372KB | ~13KB | 0KB | ~11KB | ~45KB | ~6KB |
|
|
49
|
+
| **Dependencies** | 0 | 4 | 0 | 0 | 15 | 0 |
|
|
50
|
+
| **Modular Architecture** | β
| β | β | β | β | β |
|
|
51
|
+
| **TypeScript First** | β
| β
| β
| β
| β
| β οΈ |
|
|
52
|
+
| **Browser + Node.js** | β
| β
| β
| β
| β | β |
|
|
53
|
+
| **Automatic Retry** | β
| β | β | β
| β
| β |
|
|
54
|
+
| **Response Caching** | β
Advanced | β | β | β
Basic | β
Advanced | β |
|
|
55
|
+
| **Request Deduplication** | β
| β | β | β | β | β |
|
|
56
|
+
| **Priority Queue** | β
| β | β | β | β | β |
|
|
57
|
+
| **Batch Requests** | β
| β | β | β | β | β |
|
|
58
|
+
| **Token Management** | β
Advanced | β | β | β | β | β |
|
|
59
|
+
| **Offline Queue** | β
| β | β | β | β | β |
|
|
60
|
+
| **GraphQL Support** | β
| β | β | β | β | β |
|
|
61
|
+
| **Streaming Progress** | β
Advanced | β
Basic | β
Basic | β
| β
| β
|
|
|
62
|
+
| **Request/Response Interceptors** | β
| β
| β | β
| β
| β |
|
|
63
|
+
| **Circuit Breaker** | β
| β | β | β | β | β |
|
|
64
|
+
| **Rate Limiting** | β
| β | β | β | β | β |
|
|
65
|
+
| **SSRF Protection** | β
| β | β | β | β | β |
|
|
66
|
+
| **Debug Mode** | β
| β οΈ | β | β οΈ | β
| β |
|
|
67
|
+
| **Cancel Requests** | β
| β
| β
| β
| β
| β
|
|
|
68
|
+
| **Schema Validation** | β
| β | β | β | β | β |
|
|
69
|
+
| **Error Classification** | β
| β οΈ | β | β οΈ | β
| β |
|
|
70
|
+
| **Metadata Tracking** | β
| β | β | β | β | β |
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## β¨ Core Features (in @0xshariq/voxa-core)
|
|
75
|
+
|
|
76
|
+
- π **Automatic Retry** (exponential backoff)
|
|
77
|
+
- πΎ **Response Caching** (memory/file/custom)
|
|
78
|
+
- π― **Request Deduplication**
|
|
79
|
+
- β‘ **Priority Queue**
|
|
80
|
+
- π **TypeScript First**
|
|
81
|
+
- π **Interceptors**
|
|
82
|
+
- π **Request Tracking & Metadata**
|
|
83
|
+
- β±οΈ **Timeout Control**
|
|
84
|
+
- π **Static Methods**
|
|
85
|
+
- π‘ **Schema Validation**
|
|
86
|
+
- π **SSRF Protection**
|
|
87
|
+
- π **Debug Mode**
|
|
88
|
+
- π§ͺ **Automatic JSON Parsing**
|
|
89
|
+
- βοΈ **Rate Limiting**
|
|
90
|
+
|
|
91
|
+
## π Optional Feature Packages
|
|
92
|
+
|
|
93
|
+
Install only what you need:
|
|
94
|
+
|
|
95
|
+
- πΉ **@0xshariq/voxa-streaming-images** - Image upload/download with progress
|
|
96
|
+
- π¬ **@0xshariq/voxa-streaming-videos** - Video upload/download with progress
|
|
97
|
+
- β‘ **@0xshariq/voxa-http2** - HTTP/2 Server Push
|
|
98
|
+
- π **@0xshariq/voxa-graphql** - GraphQL queries
|
|
99
|
+
- π¦ **@0xshariq/voxa-batch** - Request batching
|
|
100
|
+
- π΄ **@0xshariq/voxa-offline** - Offline queue
|
|
101
|
+
- π **@0xshariq/voxa-circuit-breaker** - Circuit breaker pattern
|
|
102
|
+
- π« **@0xshariq/voxa-token** - OAuth/JWT token management
|
|
103
|
+
- π **@0xshariq/voxa-metrics** - Performance metrics
|
|
104
|
+
- π **@0xshariq/voxa-cancel** - Advanced cancellation
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## π¦ Installation
|
|
109
|
+
|
|
110
|
+
### Core Package (Required)
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm install @0xshariq/voxa-core
|
|
114
|
+
# or
|
|
115
|
+
pnpm install @0xshariq/voxa-core
|
|
116
|
+
# or
|
|
117
|
+
yarn add @0xshariq/voxa-core
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Feature Packages (Optional)
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Install only the features you need
|
|
124
|
+
npm install @0xshariq/voxa-graphql @0xshariq/voxa-streaming-images
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## π Quick Start
|
|
130
|
+
|
|
131
|
+
### Basic Usage (Core Only)
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { Voxa } from "@0xshariq/voxa-core";
|
|
135
|
+
|
|
136
|
+
const client = new Voxa({
|
|
137
|
+
baseURL: "https://api.example.com",
|
|
138
|
+
timeout: 5000,
|
|
139
|
+
cache: {
|
|
140
|
+
enabled: true,
|
|
141
|
+
ttl: 60000,
|
|
142
|
+
},
|
|
143
|
+
retry: {
|
|
144
|
+
enabled: true,
|
|
145
|
+
count: 3,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const response = await client.get("/users");
|
|
150
|
+
console.log(response.data);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### With Feature Packages
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { Voxa } from "@0xshariq/voxa-core";
|
|
157
|
+
import "@0xshariq/voxa-graphql"; // Types auto-merge
|
|
158
|
+
import "@0xshariq/voxa-batch";
|
|
159
|
+
import { StreamingImageManager } from "@0xshariq/voxa-streaming-images";
|
|
160
|
+
|
|
161
|
+
const client = new Voxa({
|
|
162
|
+
baseURL: "https://api.example.com",
|
|
163
|
+
graphql: {
|
|
164
|
+
// TypeScript knows about this!
|
|
165
|
+
enabled: true,
|
|
166
|
+
endpoint: "/graphql",
|
|
167
|
+
},
|
|
168
|
+
batch: {
|
|
169
|
+
enabled: true,
|
|
170
|
+
wait: 100,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Use streaming
|
|
175
|
+
const imageManager = new StreamingImageManager();
|
|
176
|
+
await imageManager.upload("/upload", imageFile, {}, (sent, total) => {
|
|
177
|
+
console.log(`Progress: ${((sent / total) * 100).toFixed(2)}%`);
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### Complete Instance Example
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { Voxa } from "@0xshariq/voxa-core";
|
|
187
|
+
|
|
188
|
+
const api = new Voxa({
|
|
189
|
+
baseURL: "https://api.example.com",
|
|
190
|
+
timeout: 5000,
|
|
191
|
+
headers: {
|
|
192
|
+
"Content-Type": "application/json", // string
|
|
193
|
+
Authorization: "Bearer <token>", // string
|
|
194
|
+
},
|
|
195
|
+
priority: "high", // 'critical' | 'high' | 'normal' | 'low'
|
|
196
|
+
retry: {
|
|
197
|
+
enabled: true, // boolean (default: true)
|
|
198
|
+
count: 5, // number (max: 5)
|
|
199
|
+
delay: 1000, // number (ms)
|
|
200
|
+
exponentialBackoff: true, // boolean
|
|
201
|
+
maxRetry: 10000, // number (ms)
|
|
202
|
+
statusCodes: [429, 500, 502, 503, 504], // number[]
|
|
203
|
+
},
|
|
204
|
+
deduplication: {
|
|
205
|
+
enabled: true, // boolean (default: true)
|
|
206
|
+
ttl: 300000, // number (ms)
|
|
207
|
+
},
|
|
208
|
+
cache: {
|
|
209
|
+
enabled: true, // boolean (default: true)
|
|
210
|
+
ttl: 300000, // number (ms, default: 5 min)
|
|
211
|
+
storage: "memory", // 'memory' | 'custom'
|
|
212
|
+
adapter: undefined, // custom cache adapter (optional)
|
|
213
|
+
},
|
|
214
|
+
queue: {
|
|
215
|
+
enabled: true, // boolean
|
|
216
|
+
maxConcurrent: 5, // number
|
|
217
|
+
},
|
|
218
|
+
batch: {
|
|
219
|
+
enabled: true, // boolean
|
|
220
|
+
endpoint: "/batch", // string (optional)
|
|
221
|
+
wait: 100, // number (ms, optional)
|
|
222
|
+
maxBatchSize: 10, // number (optional)
|
|
223
|
+
},
|
|
224
|
+
token: {
|
|
225
|
+
enabled: true, // boolean
|
|
226
|
+
type: "bearer", // 'bearer' | 'oauth2' | 'jwt'
|
|
227
|
+
tokenEndpoint: "/auth/token", // string
|
|
228
|
+
clientId: "client-id", // string
|
|
229
|
+
clientSecret: "client-secret", // string
|
|
230
|
+
refreshEndpoint: "/auth/refresh", // string
|
|
231
|
+
storage: "memory", // 'memory' | 'localStorage'
|
|
232
|
+
getToken: async () => "token", // function
|
|
233
|
+
setToken: (token: string) => {}, // function
|
|
234
|
+
refreshToken: async () => "new-token", // function
|
|
235
|
+
},
|
|
236
|
+
// Token refresh failure hook
|
|
237
|
+
onTokenRefreshFailure: (error, context) => {
|
|
238
|
+
// Custom logic: log, alert, or trigger re-authentication
|
|
239
|
+
console.error("Token refresh failed:", error, context);
|
|
240
|
+
// Optionally, redirect user or clear session
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
// Multi-tenant/multi-user support example
|
|
244
|
+
getToken: async (userId) => {
|
|
245
|
+
// Fetch token for a specific user/tenant
|
|
246
|
+
return await fetchTokenForUser(userId);
|
|
247
|
+
},
|
|
248
|
+
setToken: (token, userId) => {
|
|
249
|
+
// Store token for a specific user/tenant
|
|
250
|
+
saveTokenForUser(token, userId);
|
|
251
|
+
},
|
|
252
|
+
refreshToken: async (userId) => {
|
|
253
|
+
// Refresh token for a specific user/tenant
|
|
254
|
+
return await refreshUserToken(userId);
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// Usage:
|
|
258
|
+
// await api.get('/resource', { userId: 'user-42' });
|
|
259
|
+
offline: {
|
|
260
|
+
enabled: true, // boolean (default: true)
|
|
261
|
+
storage: "localStorage", // 'localStorage' | 'indexedDB'
|
|
262
|
+
},
|
|
263
|
+
circuitBreaker: {
|
|
264
|
+
enabled: true, // boolean
|
|
265
|
+
threshold: 5, // number
|
|
266
|
+
timeout: 10000, // number (ms)
|
|
267
|
+
onOpen: () => {}, // function (optional)
|
|
268
|
+
},
|
|
269
|
+
metrics: {
|
|
270
|
+
enabled: true, // boolean
|
|
271
|
+
},
|
|
272
|
+
errors: {
|
|
273
|
+
enabled: true, // boolean (default: true)
|
|
274
|
+
},
|
|
275
|
+
rate: {
|
|
276
|
+
enabled: true, // boolean (default: true)
|
|
277
|
+
maxRequests: 100, // number
|
|
278
|
+
perMilliseconds: 60000, // number (ms)
|
|
279
|
+
},
|
|
280
|
+
schema: {
|
|
281
|
+
enabled: true, // boolean
|
|
282
|
+
requestSchema: undefined, // any (optional)
|
|
283
|
+
responseSchema: undefined, // any (optional)
|
|
284
|
+
library: "zod", // 'zod' | 'yup' (optional)
|
|
285
|
+
},
|
|
286
|
+
cancel: {
|
|
287
|
+
enabled: true, // boolean (default: true)
|
|
288
|
+
},
|
|
289
|
+
graphql: {
|
|
290
|
+
enabled: true, // boolean
|
|
291
|
+
endpoint: "https://graphqlzero.almansi.me/api", // string
|
|
292
|
+
logErrors: true, // boolean
|
|
293
|
+
headers: undefined, // Record<string, string> (optional)
|
|
294
|
+
timeout: undefined, // number (optional)
|
|
295
|
+
cache: undefined, // boolean (optional)
|
|
296
|
+
},
|
|
297
|
+
interceptors: {
|
|
298
|
+
request: [
|
|
299
|
+
(config) => {
|
|
300
|
+
/* modify config */ return config;
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
response: [
|
|
304
|
+
(response) => {
|
|
305
|
+
/* log/modify response */ return response;
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
},
|
|
309
|
+
metadata: {
|
|
310
|
+
enabled: true, // boolean (default: true)
|
|
311
|
+
log: true, // boolean (log metadata events)
|
|
312
|
+
fields: [
|
|
313
|
+
"id",
|
|
314
|
+
"method",
|
|
315
|
+
"endpoint",
|
|
316
|
+
"priority",
|
|
317
|
+
"timestamp",
|
|
318
|
+
"startTime",
|
|
319
|
+
"endTime",
|
|
320
|
+
], // string[] (fields to track)
|
|
321
|
+
maxEntries: 100, // number (max entries to keep)
|
|
322
|
+
customHandler: (meta) => {}, // function (custom handler)
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## π Usage
|
|
330
|
+
|
|
331
|
+
### π§© Feature Modularity & Extensibility
|
|
332
|
+
|
|
333
|
+
Voxa's features (cache, retry, queue, batch, deduplication, interceptors, etc.) are fully modular and can be enabled, disabled, or extended at runtime.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
### Enabling/Disabling Features at Runtime
|
|
338
|
+
|
|
339
|
+
You can toggle features on/off by updating the instance configuration:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
// Disable cache and queue at runtime
|
|
343
|
+
api.updateConfig({
|
|
344
|
+
cache: { enabled: false },
|
|
345
|
+
queue: { enabled: false },
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Enable retry and set new retry count
|
|
349
|
+
api.updateConfig({
|
|
350
|
+
retry: { enabled: true, count: 10 },
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
> **Note:** Not all features support dynamic reconfiguration in-flight. For critical changes, create a new instance with the desired config.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
### Extending with Plugins (Custom Features)
|
|
359
|
+
|
|
360
|
+
You can add your own features or override built-in ones by attaching custom managers or hooks:
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// Example: Add a custom logging plugin
|
|
364
|
+
api.usePlugin({
|
|
365
|
+
onRequest(config) {
|
|
366
|
+
console.log("Request:", config.url);
|
|
367
|
+
return config;
|
|
368
|
+
},
|
|
369
|
+
onResponse(response) {
|
|
370
|
+
console.log("Response:", response.status);
|
|
371
|
+
return response;
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
#### Plugin Interface
|
|
379
|
+
|
|
380
|
+
A plugin is an object with any of these hooks:
|
|
381
|
+
|
|
382
|
+
- `onRequest(config)`
|
|
383
|
+
- `onResponse(response)`
|
|
384
|
+
- `onError(error)`
|
|
385
|
+
- `onBatch(batch)`
|
|
386
|
+
- `onCacheEvent(event)`
|
|
387
|
+
|
|
388
|
+
You can register multiple plugins. They are called in the order added.
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
#### Disabling a Feature for a Single Request
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
// Disable cache for a single request
|
|
396
|
+
await api.get('/users', { cache: { enabled: false } });
|
|
397
|
+
|
|
398
|
+
// Set custom queue priority for a single request
|
|
399
|
+
await api.post('/orders', { ... }, { priority: 'critical' });
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
See [Advanced Features](./docs/ADVANCED.md) for more on feature modularity and plugins.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
### Using Voxa with an Instance
|
|
407
|
+
|
|
408
|
+
### Injecting Custom requestId & Metadata
|
|
409
|
+
|
|
410
|
+
RequestId will generate automatically and will use in features (like retry,batching,etc..):
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
// Provide a custom requestId and metadata for tracing
|
|
414
|
+
const response = await api.get("/users/1", {
|
|
415
|
+
requestId: "trace-abc-123", // requestid will generate per request
|
|
416
|
+
metadata: {
|
|
417
|
+
traceId: "trace-abc-123",
|
|
418
|
+
userId: "user-42",
|
|
419
|
+
customField: "my-value",
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
console.log(response.metadata); // includes your custom fields
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
> **Note:** The `requestId` generates automatically and use in all features.
|
|
426
|
+
|
|
427
|
+
Create a client instance to reuse configuration and advanced features:
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
import voxa from "@0xshariq/voxa";
|
|
431
|
+
|
|
432
|
+
const api = voxa.create({
|
|
433
|
+
baseURL: "https://api.example.com",
|
|
434
|
+
timeout: 5000,
|
|
435
|
+
// ...other options
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// Make requests (response is always parsed JSON)
|
|
439
|
+
const response = await api.get<User>("/users/1");
|
|
440
|
+
console.log(response.data); // { id, name, ... }
|
|
441
|
+
|
|
442
|
+
// You can also use other HTTP methods:
|
|
443
|
+
await api.post("/users", { name: "John" });
|
|
444
|
+
await api.put("/users/1", { name: "Jane" });
|
|
445
|
+
await api.delete("/users/1");
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
### Using Voxa Without an Instance (Static Methods)
|
|
451
|
+
|
|
452
|
+
Call static methods directly for one-off requests:
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
import { Voxa } from "@0xshariq/voxa";
|
|
456
|
+
|
|
457
|
+
const response = await Voxa.get<User>("https://api.example.com/users/1");
|
|
458
|
+
console.log(response.data);
|
|
459
|
+
|
|
460
|
+
// Other static methods:
|
|
461
|
+
await Voxa.post("https://api.example.com/users", { name: "John" });
|
|
462
|
+
await Voxa.put("https://api.example.com/users/1", { name: "Jane" });
|
|
463
|
+
await Voxa.delete("https://api.example.com/users/1");
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Use an instance for advanced features (caching, queueing, interceptors, GraphQL, etc.), or static methods for simple requests.
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
### TypeScript Generics
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
interface User {
|
|
474
|
+
id: number;
|
|
475
|
+
name: string;
|
|
476
|
+
email: string;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Type-safe responses
|
|
480
|
+
const response = await api.get<User>("/users/1");
|
|
481
|
+
const user: User = await response.json();
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
### Streaming Upload/Download
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
import { StreamingImageManager, StreamingVideoManager } from "@0xshariq/voxa";
|
|
490
|
+
|
|
491
|
+
// Upload image with progress tracking
|
|
492
|
+
const streamingImages = new StreamingImageManager({});
|
|
493
|
+
|
|
494
|
+
const fileInput = document.querySelector('input[type="file"]');
|
|
495
|
+
const file = fileInput.files[0];
|
|
496
|
+
|
|
497
|
+
await streamingImages.upload(
|
|
498
|
+
"https://api.example.com/images/upload",
|
|
499
|
+
file,
|
|
500
|
+
{ "Content-Type": "image/jpeg" },
|
|
501
|
+
(sentBytes, totalBytes) => {
|
|
502
|
+
const percentage = (sentBytes / totalBytes) * 100;
|
|
503
|
+
console.log(`Upload progress: ${percentage.toFixed(2)}%`);
|
|
504
|
+
}
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
// Download video with progress
|
|
508
|
+
const streamingVideos = new StreamingVideoManager({});
|
|
509
|
+
|
|
510
|
+
const response = await streamingVideos.download(
|
|
511
|
+
"https://api.example.com/videos/12345",
|
|
512
|
+
{},
|
|
513
|
+
(receivedBytes, totalBytes) => {
|
|
514
|
+
console.log(`Downloaded: ${receivedBytes}/${totalBytes} bytes`);
|
|
515
|
+
}
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
const blob = await response.blob();
|
|
519
|
+
const videoUrl = URL.createObjectURL(blob);
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
See [Streaming Guide](./docs/STREAMING.md) for complete examples and advanced usage.
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## π Documentation
|
|
527
|
+
|
|
528
|
+
### Core Documentation
|
|
529
|
+
|
|
530
|
+
**Documentation:**
|
|
531
|
+
|
|
532
|
+
- [Configuration Guide](./docs/CONFIGURATION.md) β All config options.
|
|
533
|
+
- [Advanced Features](./docs/ADVANCED.md) β Caching, deduplication, queueing, interceptors, GraphQL, etc.
|
|
534
|
+
- [Streaming Guide](./docs/STREAMING.md) β Image/video upload/download with progress tracking.
|
|
535
|
+
- [Developer Experience](./docs/DEVELOPER_EXPERIENCE.md) β Debug mode, logging, public getters, type safety.
|
|
536
|
+
- [Troubleshooting](./docs/TROUBLESHOOTING.md) β Common issues and solutions.
|
|
537
|
+
- [Custom Cache](./docs/CUSTOM_CACHE.md) β Custom cache implementation.
|
|
538
|
+
- [Batch Usage](./docs/BATCH_USAGE.md) β Batch request patterns.
|
|
539
|
+
- [Examples](./docs/EXAMPLES.md) β Usage patterns and code samples.
|
|
540
|
+
- [Migration](./docs/MIGRATION.md) - Migration from axios to voxa.
|
|
541
|
+
|
|
542
|
+
See docs/ for full details and up-to-date usage.
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## π― Key Features
|
|
547
|
+
|
|
548
|
+
### Automatic Retry with Exponential Backoff
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
const api = voxa.create({
|
|
552
|
+
baseURL: "https://api.example.com",
|
|
553
|
+
retry: {
|
|
554
|
+
count: 3, // Max 3 retries
|
|
555
|
+
delay: 1000, // Initial delay: 1s
|
|
556
|
+
exponentialBackoff: true, // 1s β 2s β 4s
|
|
557
|
+
maxRetry: 10000, // Max delay: 10s
|
|
558
|
+
},
|
|
559
|
+
});
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
|
|
564
|
+
### Response Structure
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
{
|
|
568
|
+
response: Response, // Http Response object
|
|
569
|
+
data: {}, // Api Data will store here
|
|
570
|
+
metadata: {
|
|
571
|
+
id: "string", // requestId used everywhere
|
|
572
|
+
method: "'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'",
|
|
573
|
+
endpoint: "string",
|
|
574
|
+
priority: "'critical' | 'high' | 'normal' | 'low'",
|
|
575
|
+
timestamp: 2000,
|
|
576
|
+
startTime: 2000,
|
|
577
|
+
endTime: 2000
|
|
578
|
+
},
|
|
579
|
+
requestId: string, // Unique requestId, used in all features (queue, batch, cache, metrics, etc.)
|
|
580
|
+
status: 200,
|
|
581
|
+
statusText: "Ok"
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
See [Response Structure](./src/lib/types/client-types.ts) for detailed info.
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
### Response Caching
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
// Memory cache (default)
|
|
593
|
+
const api = voxa.create({
|
|
594
|
+
cache: {
|
|
595
|
+
enabled: true,
|
|
596
|
+
type: "memory",
|
|
597
|
+
ttl: 300000, // 5 minutes
|
|
598
|
+
},
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
// Redis cache
|
|
602
|
+
const api = voxa.create({
|
|
603
|
+
cache: {
|
|
604
|
+
enabled: true,
|
|
605
|
+
type: "redis",
|
|
606
|
+
ttl: 300000,
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
### Request Prioritization
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
// High priority request
|
|
617
|
+
await api.get("/critical-data", { priority: "critical" });
|
|
618
|
+
|
|
619
|
+
// Normal priority (default)
|
|
620
|
+
await api.get("/regular-data");
|
|
621
|
+
|
|
622
|
+
// Low priority
|
|
623
|
+
await api.get("/background-data", { priority: "low" });
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
---
|
|
627
|
+
|
|
628
|
+
### Request Interceptors
|
|
629
|
+
|
|
630
|
+
```typescript
|
|
631
|
+
// Add authentication header
|
|
632
|
+
api.interceptors.request.use((config) => {
|
|
633
|
+
config.headers = {
|
|
634
|
+
...config.headers,
|
|
635
|
+
Authorization: `Bearer ${getToken()}`,
|
|
636
|
+
};
|
|
637
|
+
return config;
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
// Log responses
|
|
641
|
+
api.interceptors.response.use((response) => {
|
|
642
|
+
console.log("Response:", response.status);
|
|
643
|
+
return response;
|
|
644
|
+
});
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
---
|
|
648
|
+
|
|
649
|
+
## π§ Configuration
|
|
650
|
+
|
|
651
|
+
### Environment Variables
|
|
652
|
+
|
|
653
|
+
Create a `.env` file in your project root:
|
|
654
|
+
|
|
655
|
+
```env
|
|
656
|
+
# Cache Configuration (Generic - works with any cache backend)
|
|
657
|
+
CACHE_URL=redis://localhost:6379
|
|
658
|
+
CACHE_PASSWORD=your-password
|
|
659
|
+
CACHE_HOST=localhost
|
|
660
|
+
CACHE_PORT=6379
|
|
661
|
+
CACHE_DB=0
|
|
662
|
+
|
|
663
|
+
# HTTP Configuration
|
|
664
|
+
HTTP_TIMEOUT=5000
|
|
665
|
+
HTTP_BASE_URL=https://api.example.com
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
See [Configuration Guide](./docs/CONFIGURATION.md) for detailed options.
|
|
669
|
+
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
## π Monitoring & Statistics
|
|
673
|
+
|
|
674
|
+
```typescript
|
|
675
|
+
// Get cache statistics
|
|
676
|
+
const cacheStats = api.getCacheStats();
|
|
677
|
+
console.log(cacheStats); // { storage: 'memory', size: 10, entries: [...] }
|
|
678
|
+
|
|
679
|
+
// Get queue statistics
|
|
680
|
+
const queueStats = api.getQueueStats();
|
|
681
|
+
console.log(queueStats); // { queueSize: 2, activeRequests: 3, maxConcurrent: 5 }
|
|
682
|
+
|
|
683
|
+
// Get request metadata
|
|
684
|
+
const metadata = api.getRequestMetadata("request-id-123");
|
|
685
|
+
console.log(metadata); // { id, method, endpoint, duration, ... }
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## Request ID Format and Expiry
|
|
691
|
+
|
|
692
|
+
Each request is assigned a unique `requestId` for tracking and feature management. The format is:
|
|
693
|
+
|
|
694
|
+
```
|
|
695
|
+
<timestamp>-<expiry>-<random>
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
- `timestamp`: When the request was created
|
|
699
|
+
- `expiry`: When the requestId expires (used for cache/batch conflict avoidance)
|
|
700
|
+
- `random`: Random string for uniqueness
|
|
701
|
+
|
|
702
|
+
- **Cache feature** uses a 5 minute expiry (default).
|
|
703
|
+
- **Batch feature** uses a 15 minute expiry.
|
|
704
|
+
|
|
705
|
+
This prevents conflicts when the same request is sent at different times. Always use the generated requestId for all features (cache, batch, queue, etc.).
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
## Error Classification & Debugging
|
|
710
|
+
|
|
711
|
+
Voxa now provides detailed error classification and debugging messages for every request. Use `api.classifyError(error)` to get both the error category and a helpful message for easier troubleshooting.
|
|
712
|
+
|
|
713
|
+
---
|
|
714
|
+
|
|
715
|
+
### Feature Manager Access
|
|
716
|
+
|
|
717
|
+
All feature managers (cache, queue, deduplication, metadata, circuit breaker, batch, rate limiter, metrics, schema, error classifier) are accessible via public methods or stats getters:
|
|
718
|
+
|
|
719
|
+
- `api.getCacheStats()`
|
|
720
|
+
- `api.getQueueStats()`
|
|
721
|
+
- `api.getDeduplicationStats()`
|
|
722
|
+
- `api.getMetadataStats()`
|
|
723
|
+
- `api.circuitBreaker()`
|
|
724
|
+
- `api.batch()`
|
|
725
|
+
- `api.rate()`
|
|
726
|
+
- `api.metrics()`
|
|
727
|
+
- `api.schema()`
|
|
728
|
+
- `api.classifyError(error)`
|
|
729
|
+
|
|
730
|
+
---
|
|
731
|
+
|
|
732
|
+
## π§ͺ Testing
|
|
733
|
+
|
|
734
|
+
```bash
|
|
735
|
+
# Run test file directly
|
|
736
|
+
pnpm test
|
|
737
|
+
|
|
738
|
+
# Or using development mode
|
|
739
|
+
pnpm dev
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
## π HTTP Methods
|
|
745
|
+
|
|
746
|
+
Voxa supports all standard HTTP methods:
|
|
747
|
+
|
|
748
|
+
```typescript
|
|
749
|
+
await api.get("/users");
|
|
750
|
+
await api.post("/users", { name: "John" });
|
|
751
|
+
await api.put("/users/1", { name: "Jane" });
|
|
752
|
+
await api.patch("/users/1", { email: "new@example.com" });
|
|
753
|
+
await api.delete("/users/1");
|
|
754
|
+
await api.head("/users");
|
|
755
|
+
await api.options("/users");
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
---
|
|
759
|
+
|
|
760
|
+
## π€ Contributing
|
|
761
|
+
|
|
762
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
763
|
+
|
|
764
|
+
See the [Contributing Guide](./CONTRIBUTING.md)
|
|
765
|
+
|
|
766
|
+
---
|
|
767
|
+
|
|
768
|
+
## π License
|
|
769
|
+
|
|
770
|
+
MIT Β© [Sharique Chaudhary](https://github.com/0xshariq)
|
|
771
|
+
|
|
772
|
+
---
|
|
773
|
+
|
|
774
|
+
## π Links
|
|
775
|
+
|
|
776
|
+
- [GitHub Repository](https://github.com/0xshariq/voxa)
|
|
777
|
+
- [npm Package](https://www.npmjs.com/package/@0xshariq/voxa)
|
|
778
|
+
- [Issue Tracker](https://github.com/0xshariq/voxa/issues)
|
|
779
|
+
|
|
780
|
+
---
|
|
781
|
+
|
|
782
|
+
**Note:** This project is built on the native Fetch API and requires Node.js 18+ or a modern browser environment.
|