@bereasoftware/nexa 1.2.0 β 1.3.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.en.md +179 -335
- package/README.md +177 -22
- package/dist/bereasoftware-nexa-1.3.0.tgz +0 -0
- package/dist/nexa.cjs.js +462 -1
- package/dist/nexa.cjs.js.map +1 -1
- package/dist/nexa.es.js +1103 -890
- package/dist/nexa.es.js.map +1 -1
- package/dist/nexa.iife.js +462 -1
- package/dist/nexa.iife.js.map +1 -1
- package/dist/nexa.umd.js +462 -1
- package/dist/nexa.umd.js.map +1 -1
- package/dist/types/dev-overlay/index.d.ts +15 -0
- package/dist/types/dev-overlay/overlay.d.ts +26 -0
- package/dist/types/dev-overlay/tracker.d.ts +16 -0
- package/dist/types/dev-overlay/types.d.ts +33 -0
- package/dist/types/http-client/http-client.d.ts +51 -23
- package/dist/types/http-client/index.d.ts +2 -2
- package/dist/types/http-client/node-http-adapter.d.ts +7 -2
- package/dist/types/index.d.ts +6 -3
- package/dist/types/realtime/plugin.d.ts +5 -1
- package/dist/types/realtime/sse-client.d.ts +4 -1
- package/dist/types/realtime/websocket-client.d.ts +4 -1
- package/dist/types/types/index.d.ts +50 -44
- package/dist/types/utils/index.d.ts +7 -71
- package/package.json +31 -25
- package/dist/bereasoftware-nexa-1.2.0.tgz +0 -0
- package/dist/types/testing/index.d.ts +0 -5
- package/dist/types/testing/mock-client.d.ts +0 -152
package/README.en.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
+
<img src="assets/Logo.png" alt="Nexa Logo" width="120" height="120" />
|
|
2
3
|
<h1 align="center">@bereasoftware/nexa</h1>
|
|
3
4
|
<p align="center">
|
|
4
5
|
A modern, type-safe HTTP client that combines the power of <code>fetch</code> with the convenience of <code>axios</code> β built on SOLID principles.
|
|
@@ -6,15 +7,16 @@
|
|
|
6
7
|
</p>
|
|
7
8
|
|
|
8
9
|
<p align="center">
|
|
9
|
-
<a href="#tests"><img src="https://img.shields.io/badge/Tests-
|
|
10
|
-
<a href="#test-coverage"><img src="https://img.shields.io/badge/Coverage-
|
|
10
|
+
<a href="#tests"><img src="https://img.shields.io/badge/Tests-213_passing-brightgreen?style=for-the-badge" alt="Tests" /></a>
|
|
11
|
+
<a href="#test-coverage"><img src="https://img.shields.io/badge/Coverage-See_report-orange?style=for-the-badge" alt="Coverage" /></a>
|
|
11
12
|
<a href="https://www.npmjs.com/package/@bereasoftware/nexa"><img src="https://img.shields.io/npm/v/@bereasoftware/nexa?style=for-the-badge" alt="NPM Version" /></a>
|
|
12
13
|
<a href="https://bundlephobia.com/package/@bereasoftware/nexa"><img src="https://img.shields.io/bundlephobia/minzip/@bereasoftware/nexa?label=Bundle&style=for-the-badge" alt="Bundle Size" /></a>
|
|
13
14
|
<a href="https://www.npmjs.com/package/@bereasoftware/nexa"><img src="https://img.shields.io/npm/dm/@bereasoftware/nexa?style=for-the-badge" alt="NPM Downloads" /></a>
|
|
14
15
|
<img src="https://img.shields.io/badge/Node-20%2B-success?style=for-the-badge" alt="Node" />
|
|
15
|
-
<img src="https://img.shields.io/badge/TypeScript-
|
|
16
|
+
<img src="https://img.shields.io/badge/TypeScript-6.x-3178C6?style=for-the-badge" alt="TypeScript" />
|
|
16
17
|
<img src="https://img.shields.io/badge/Dependencies-Zero-brightgreen?style=for-the-badge" alt="Dependencies" />
|
|
17
18
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge" alt="License" /></a>
|
|
19
|
+
<img src="https://img.shields.io/badge/Maintenance-Active-success?style=for-the-badge" alt="Maintenance" />
|
|
18
20
|
<a href="https://github.com/Berea-Soft/nexa"><img src="https://img.shields.io/badge/github-Repository-blue?logo=github&style=for-the-badge" alt="Repository" /></a>
|
|
19
21
|
</p>
|
|
20
22
|
|
|
@@ -23,6 +25,16 @@
|
|
|
23
25
|
> - πͺπΈ [EspaΓ±ol](README.md)
|
|
24
26
|
> - π¬π§ **English** (this file - README.en.md)
|
|
25
27
|
|
|
28
|
+
> π **Additional documentation:**
|
|
29
|
+
>
|
|
30
|
+
> - [Architecture](ARCHITECTURE.md) β System design
|
|
31
|
+
> - [Design](DESIGN.md) β Philosophy and design decisions
|
|
32
|
+
> - [Security](SECURITY.md) β Security policy
|
|
33
|
+
> - [Contributing](CONTRIBUTING.md) β Contributor guide
|
|
34
|
+
> - [Migration](MIGRATION.md) β From axios/fetch
|
|
35
|
+
> - [Support](SUPPORT.md) β How to get help
|
|
36
|
+
> - [Skills](SKILLS.md) β Project competencies
|
|
37
|
+
|
|
26
38
|
---
|
|
27
39
|
|
|
28
40
|
## Why Nexa?
|
|
@@ -48,14 +60,8 @@
|
|
|
48
60
|
| Validators & transformers | β | β | β
|
|
|
49
61
|
| Response duration tracking | β | β | β
|
|
|
50
62
|
| Smart response type detection | β | β
| β
|
|
|
51
|
-
| Request transformation (transformRequest) | β | β
| β
|
|
|
52
|
-
| Credentials policy | β
| β
| β
|
|
|
53
|
-
| Adapter pattern (mocking/testing) | β | β
| β
|
|
|
54
|
-
| Automatic FormData conversion | β | β | β
|
|
|
55
|
-
| Enhanced error context (request/response/config) | β | β
| β
|
|
|
56
|
-
| Differentiated timeouts (connection vs response) | β | β | β
|
|
|
57
|
-
| Debug logging (development mode) | β | β | β
|
|
|
58
63
|
| Tree-shakeable | β
| β | β
|
|
|
64
|
+
| Dev Overlay (dev tools) | β | β | β
|
|
|
59
65
|
|
|
60
66
|
---
|
|
61
67
|
|
|
@@ -71,10 +77,6 @@
|
|
|
71
77
|
- [Path Parameters](#path-parameters)
|
|
72
78
|
- [Query Parameters](#query-parameters)
|
|
73
79
|
- [Auto Body Serialization](#auto-body-serialization)
|
|
74
|
-
- [Request Transformation (transformRequest)](#request-transformation-transformrequest)
|
|
75
|
-
- [Credentials Policy](#credentials-policy)
|
|
76
|
-
- [Adapter Pattern](#adapter-pattern)
|
|
77
|
-
- [Transport Configuration](#transport-configuration)
|
|
78
80
|
- [Response Types](#response-types)
|
|
79
81
|
- [Timeout](#timeout)
|
|
80
82
|
- [Retry Strategies](#retry-strategies)
|
|
@@ -99,15 +101,13 @@
|
|
|
99
101
|
- [Transformers](#transformers)
|
|
100
102
|
- [Middleware Pipeline](#middleware-pipeline)
|
|
101
103
|
- [Plugin System](#plugin-system)
|
|
104
|
+
- [Dev Overlay](#dev-overlay)
|
|
102
105
|
- [Streaming](#streaming)
|
|
103
106
|
- [Typed Generics](#typed-generics)
|
|
104
107
|
- [Error Handling](#error-handling)
|
|
105
|
-
- [Enhanced Error Context](#enhanced-error-context)
|
|
106
108
|
- [API Reference](#api-reference)
|
|
107
109
|
- [Build Formats](#build-formats)
|
|
108
110
|
- [Development](#development)
|
|
109
|
-
- [Contributing](#contributing)
|
|
110
|
-
- [Security](#security)
|
|
111
111
|
- [License](#license)
|
|
112
112
|
|
|
113
113
|
---
|
|
@@ -221,8 +221,6 @@ const client = createHttpClient({
|
|
|
221
221
|
| `maxConcurrent` | `number` | `0` (unlimited) | Max concurrent requests |
|
|
222
222
|
| `defaultResponseType` | `ResponseType` | `'auto'` | Default response parsing strategy |
|
|
223
223
|
| `defaultHooks` | `RequestHooks` | `{}` | Default lifecycle hooks for all requests |
|
|
224
|
-
| `debug` | `boolean \| 'verbose'` | `undefined` | Enable debug logging for requests/responses. `true` for basic logs, `'verbose'` for detailed logs. |
|
|
225
|
-
| `logger` | `(message: string, data?: unknown) => void` | `undefined` | Custom logger function. If provided, replaces the default console.log with custom logging. |
|
|
226
224
|
|
|
227
225
|
---
|
|
228
226
|
|
|
@@ -305,8 +303,6 @@ Nexa automatically detects and serializes the request body:
|
|
|
305
303
|
| `ArrayBuffer` | Passed as-is | `application/octet-stream` |
|
|
306
304
|
| `ReadableStream` | Passed as-is | `application/octet-stream` |
|
|
307
305
|
|
|
308
|
-
When `autoFormData` is enabled (default), objects containing `File` or `Blob` instances are automatically converted to `FormData`. This is useful for uploading files without manually creating `FormData` instances.
|
|
309
|
-
|
|
310
306
|
```typescript
|
|
311
307
|
// JSON (automatic)
|
|
312
308
|
await client.post("/users", { name: "John" });
|
|
@@ -323,172 +319,6 @@ await client.post(
|
|
|
323
319
|
);
|
|
324
320
|
```
|
|
325
321
|
|
|
326
|
-
### Request Transformation (transformRequest)
|
|
327
|
-
|
|
328
|
-
Nexa supports request transformation similar to axios's `transformRequest`. You can provide a function or array of functions that transform the request body before serialization. The transformation is applied after interceptors and before serialization.
|
|
329
|
-
|
|
330
|
-
```typescript
|
|
331
|
-
// Global configuration
|
|
332
|
-
const client = createHttpClient({
|
|
333
|
-
transformRequest: [(data, headers) => {
|
|
334
|
-
// Add timestamp to all requests
|
|
335
|
-
if (data && typeof data === 'object') {
|
|
336
|
-
return { ...data, timestamp: Date.now() };
|
|
337
|
-
}
|
|
338
|
-
return data;
|
|
339
|
-
}]
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
// Per-request configuration
|
|
343
|
-
await client.post('/api', { foo: 'bar' }, {
|
|
344
|
-
transformRequest: [(data) => ({ ...data, extra: 'value' })]
|
|
345
|
-
});
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
### Credentials Policy
|
|
349
|
-
|
|
350
|
-
Nexa supports the standard fetch `credentials` option as well as axios-compatible `withCredentials` boolean. This controls whether cookies and other credentials are sent with cross-origin requests.
|
|
351
|
-
|
|
352
|
-
```typescript
|
|
353
|
-
// Using fetch-style credentials
|
|
354
|
-
await client.get('/api', { credentials: 'include' });
|
|
355
|
-
|
|
356
|
-
// Using axios-style withCredentials (true = 'include', false = 'same-origin')
|
|
357
|
-
await client.post('/api', data, { withCredentials: true });
|
|
358
|
-
|
|
359
|
-
// Global configuration
|
|
360
|
-
const client = createHttpClient({
|
|
361
|
-
credentials: 'same-origin', // Default: 'omit'
|
|
362
|
-
// or
|
|
363
|
-
withCredentials: true, // Equivalent to credentials: 'include'
|
|
364
|
-
});
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
Priority: `credentials` overrides `withCredentials` if both are specified.
|
|
368
|
-
|
|
369
|
-
### Adapter Pattern
|
|
370
|
-
|
|
371
|
-
Nexa supports custom adapters for mocking, testing, or integrating with different environments (Cloudflare Workers, Node.js, etc.). The adapter has the same signature as the global `fetch` function.
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
// Mock adapter for testing
|
|
375
|
-
const mockAdapter = async (input: RequestInfo, init?: RequestInit) => {
|
|
376
|
-
return new Response(JSON.stringify({ mock: true }), { status: 200 });
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
// Global adapter
|
|
380
|
-
const client = createHttpClient({
|
|
381
|
-
adapter: mockAdapter,
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// Per-request adapter (overrides global)
|
|
385
|
-
await client.get('/api', { adapter: mockAdapter });
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
Adapters can be used to intercept requests before they reach the network, enabling easy testing without actual HTTP calls.
|
|
389
|
-
|
|
390
|
-
### Mocking Utilities (axios-mock-adapter style)
|
|
391
|
-
|
|
392
|
-
For more sophisticated testing scenarios, Nexa provides a mocking utility similar to `axios-mock-adapter`. This allows you to configure mock responses for specific routes and HTTP methods.
|
|
393
|
-
|
|
394
|
-
```typescript
|
|
395
|
-
import { createHttpClient } from '@bereasoftware/nexa';
|
|
396
|
-
import { createMockClient } from '@bereasoftware/nexa/testing';
|
|
397
|
-
|
|
398
|
-
const client = createHttpClient({ baseURL: 'https://api.example.com' });
|
|
399
|
-
const mockClient = createMockClient(client);
|
|
400
|
-
|
|
401
|
-
// Configure mock responses
|
|
402
|
-
mockClient.onGet('/users').reply(200, [{ id: 1, name: 'John' }]);
|
|
403
|
-
mockClient.onPost('/users').reply(201, { id: 2, name: 'Jane' });
|
|
404
|
-
mockClient.onPut('/users/1').reply(200, { id: 1, name: 'Updated' });
|
|
405
|
-
mockClient.onDelete('/users/1').reply(204);
|
|
406
|
-
|
|
407
|
-
// Network error simulation
|
|
408
|
-
mockClient.onGet('/error').networkError('Network failure');
|
|
409
|
-
|
|
410
|
-
// Use the mock-enabled client for requests
|
|
411
|
-
const result = await mockClient.client.get('/users');
|
|
412
|
-
if (result.ok) {
|
|
413
|
-
console.log(result.value.data); // [{ id: 1, name: 'John' }]
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Advanced: reply once, then fall through
|
|
417
|
-
mockClient.onGet('/once').replyOnce(200, { data: 'first' });
|
|
418
|
-
// Second call to same route will not match (unless passthrough enabled)
|
|
419
|
-
|
|
420
|
-
// Advanced: function-based responses
|
|
421
|
-
mockClient.onGet('/dynamic').reply((config) => ({
|
|
422
|
-
status: 200,
|
|
423
|
-
data: { url: config.url, query: config.query },
|
|
424
|
-
}));
|
|
425
|
-
|
|
426
|
-
// Reset mock routes between tests
|
|
427
|
-
mockClient.reset();
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
**Key features:**
|
|
431
|
-
- **Fluent API**: `onGet(url).reply(status, data)` similar to axios-mock-adapter
|
|
432
|
-
- **URL patterns**: Support string exact match or RegExp
|
|
433
|
-
- **Response functions**: Dynamic responses based on request config
|
|
434
|
-
- **Call limits**: `replyOnce()` for one-time mock
|
|
435
|
-
- **Network errors**: Simulate network failures with `networkError()`
|
|
436
|
-
- **Timeout simulation**: `timeout()` method for timeout testing
|
|
437
|
-
- **Passthrough mode**: Optionally forward unmatched requests to real adapter
|
|
438
|
-
- **Base URL support**: Automatically strips baseURL when matching
|
|
439
|
-
|
|
440
|
-
**Options** for `createMockClient`:
|
|
441
|
-
- `passthrough`: Forward unmatched requests to original adapter (default: `false`)
|
|
442
|
-
- `baseURL`: Base URL to strip when matching routes
|
|
443
|
-
- `delay`: Default delay for all responses (ms)
|
|
444
|
-
|
|
445
|
-
### Transport Configuration
|
|
446
|
-
|
|
447
|
-
Nexa supports multiple transport layers for different environments. By default, it uses the global `fetch` API (available in browsers and Node.js 18+). For advanced Node.js scenarios, you can use the native HTTP/1.1 or HTTP/2 modules with keep-alive, connection pooling, and other Node-specific optimizations.
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
import { createHttpClient } from '@bereasoftware/nexa';
|
|
451
|
-
|
|
452
|
-
// Use Node.js HTTP/1.1 with keep-alive and connection pooling
|
|
453
|
-
const client = createHttpClient({
|
|
454
|
-
transport: 'node', // 'fetch' (default), 'node', or 'http2'
|
|
455
|
-
nodeOptions: {
|
|
456
|
-
keepAlive: true,
|
|
457
|
-
maxSockets: 50,
|
|
458
|
-
maxFreeSockets: 10,
|
|
459
|
-
maxRequestsPerSocket: 0, // unlimited
|
|
460
|
-
timeout: 60000,
|
|
461
|
-
},
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
// Use HTTP/2 for better performance with multiplexing
|
|
465
|
-
const http2Client = createHttpClient({
|
|
466
|
-
transport: 'http2',
|
|
467
|
-
nodeOptions: {
|
|
468
|
-
http2Settings: {
|
|
469
|
-
enablePush: false,
|
|
470
|
-
},
|
|
471
|
-
},
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// Override transport per request
|
|
475
|
-
await client.get('/api', {
|
|
476
|
-
transport: 'http2',
|
|
477
|
-
nodeOptions: { keepAlive: true },
|
|
478
|
-
});
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
**Note:** Node transports (`'node'` and `'http2'`) are only available in Node.js environments. In browsers, they will fall back to `'fetch'` automatically.
|
|
482
|
-
|
|
483
|
-
**Available options in `nodeOptions`:**
|
|
484
|
-
- `keepAlive` (boolean): Enable keep-alive connections (default: `true`)
|
|
485
|
-
- `maxSockets` (number): Maximum sockets per host (default: `50`)
|
|
486
|
-
- `maxFreeSockets` (number): Maximum free sockets to keep open (default: `10`)
|
|
487
|
-
- `maxRequestsPerSocket` (number): Maximum requests per socket (default: `0` = unlimited)
|
|
488
|
-
- `timeout` (number): Socket timeout in milliseconds (default: `60000`)
|
|
489
|
-
- `http2` (boolean): Deprecated - use `transport: 'http2'` instead
|
|
490
|
-
- `http2Settings` (Record<string, unknown>): HTTP/2 specific settings (only for `transport: 'http2'`)
|
|
491
|
-
|
|
492
322
|
### Response Types
|
|
493
323
|
|
|
494
324
|
Control how the response body is parsed:
|
|
@@ -533,66 +363,10 @@ const result = await client.get("/slow-endpoint", { timeout: 5000 });
|
|
|
533
363
|
// Timeout produces a specific error code
|
|
534
364
|
if (!result.ok && result.error.code === "TIMEOUT") {
|
|
535
365
|
console.log("Request timed out");
|
|
366
|
+
}
|
|
536
367
|
```
|
|
537
368
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
```typescript
|
|
541
|
-
// Differentiated timeouts
|
|
542
|
-
await client.get('/api', {
|
|
543
|
-
timeout: {
|
|
544
|
-
connection: 3000, // 3 seconds to establish connection
|
|
545
|
-
response: 10000, // 10 seconds to receive complete response
|
|
546
|
-
total: 15000 // Optional total timeout (overrides connection/response)
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
// Backward compatibility: number still works (total timeout)
|
|
551
|
-
await client.get('/api', { timeout: 5000 });
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
Error codes: `TIMEOUT` for connection/total timeouts, `RESPONSE_TIMEOUT` for response phase timeouts.
|
|
555
|
-
|
|
556
|
-
### Debug Logging
|
|
557
|
-
|
|
558
|
-
Nexa provides axios-like debug logging for development. Enable it globally or per-request:
|
|
559
|
-
|
|
560
|
-
```typescript
|
|
561
|
-
// Global debug logging
|
|
562
|
-
const client = createHttpClient({
|
|
563
|
-
debug: true, // basic logs
|
|
564
|
-
// debug: 'verbose', // detailed logs
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
// Per-request override
|
|
568
|
-
await client.get('/api', { debug: 'verbose' });
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
Logs include request method/URL, response status, duration, errors, retries, and cache hits. With `verbose` mode, you also see transformed requests, interceptor outputs, and response data.
|
|
572
|
-
|
|
573
|
-
#### Custom Logger
|
|
574
|
-
|
|
575
|
-
You can provide a custom logger function to redirect logs to your preferred logging system:
|
|
576
|
-
|
|
577
|
-
```typescript
|
|
578
|
-
const client = createHttpClient({
|
|
579
|
-
debug: true,
|
|
580
|
-
logger: (message, data) => {
|
|
581
|
-
// Send to your logging service
|
|
582
|
-
myLogger.info(message, data);
|
|
583
|
-
},
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
// Request-level logger override
|
|
587
|
-
await client.post('/api', data, {
|
|
588
|
-
debug: true,
|
|
589
|
-
logger: (msg, data) => console.warn(msg, data)
|
|
590
|
-
});
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
The logger receives two arguments: the log message (prefixed with `[Nexa HTTP]`) and optional data object.
|
|
594
|
-
|
|
595
|
-
---
|
|
369
|
+
---
|
|
596
370
|
|
|
597
371
|
## Retry Strategies
|
|
598
372
|
|
|
@@ -607,27 +381,6 @@ const result = await client.get("/unstable-api", {
|
|
|
607
381
|
// Retries up to 3 times with exponential backoff + jitter
|
|
608
382
|
```
|
|
609
383
|
|
|
610
|
-
### Custom Retry Condition
|
|
611
|
-
|
|
612
|
-
You can provide a custom condition function to decide which errors should be retried:
|
|
613
|
-
|
|
614
|
-
```typescript
|
|
615
|
-
const result = await client.get('/api', {
|
|
616
|
-
retry: {
|
|
617
|
-
maxAttempts: 3,
|
|
618
|
-
backoffMs: 1000,
|
|
619
|
-
on: (error) => {
|
|
620
|
-
// Retry only on network errors or 5xx status
|
|
621
|
-
return error.code === 'NETWORK_ERROR' ||
|
|
622
|
-
(error.status >= 500 && error.status < 600) ||
|
|
623
|
-
error.message.includes('ECONNRESET')
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
```
|
|
628
|
-
|
|
629
|
-
The `on` function receives the `HttpErrorDetails` and attempt number, and returns `true` if the request should be retried. If `on` is not provided, the default condition (5xx status, network errors, timeouts) is used.
|
|
630
|
-
|
|
631
384
|
### AggressiveRetry
|
|
632
385
|
|
|
633
386
|
Retries all errors up to max attempts with minimal delay:
|
|
@@ -1201,6 +954,120 @@ manager.register(rateLimitPlugin);
|
|
|
1201
954
|
|
|
1202
955
|
---
|
|
1203
956
|
|
|
957
|
+
## Dev Overlay
|
|
958
|
+
|
|
959
|
+
Built-in visual development tool for debugging and monitoring HTTP requests in real-time.
|
|
960
|
+
|
|
961
|
+
```typescript
|
|
962
|
+
import { createHttpClient, createDevOverlay } from "@bereasoftware/nexa";
|
|
963
|
+
|
|
964
|
+
// Create the Dev Overlay - appears automatically
|
|
965
|
+
const { tracker, ui } = createDevOverlay({
|
|
966
|
+
position: "bottom-right", // top-right, top-left, bottom-left
|
|
967
|
+
theme: "dark",
|
|
968
|
+
maxHistory: 200,
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
// Pass the tracker to the HTTP client
|
|
972
|
+
const client = createHttpClient({
|
|
973
|
+
baseURL: "https://api.example.com",
|
|
974
|
+
devTracker: tracker,
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
// That's it! All requests will appear in the overlay
|
|
978
|
+
const result = await client.get("/users");
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
### Features
|
|
982
|
+
|
|
983
|
+
- **Keyboard toggle** β Press `Ctrl+Shift+N` or `Cmd+Shift+N` to show/hide
|
|
984
|
+
- **Request list** β Shows method, status, URL, duration and badges (cache, retries)
|
|
985
|
+
- **Real-time metrics** β Total requests, average, throughput, success, failed
|
|
986
|
+
- **Search and filter** β Filter by URL, method or status (`Ctrl+F`)
|
|
987
|
+
- **Request detail** β Click any request to see headers, body and timing
|
|
988
|
+
- **Quick retry** β Replays the selected URL with `fetch` for fast verification
|
|
989
|
+
- **Keyboard shortcuts** β `Ctrl+Shift+N` / `Cmd+Shift+N` (toggle), `Escape` (close), `Ctrl+F` / `Cmd+F` (search)
|
|
990
|
+
|
|
991
|
+
### API
|
|
992
|
+
|
|
993
|
+
```typescript
|
|
994
|
+
createDevOverlay(config?: DevOverlayConfig): { tracker: RequestTracker; ui: DevOverlayUI }
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
**Configuration options:**
|
|
998
|
+
|
|
999
|
+
| Option | Type | Default | Description |
|
|
1000
|
+
|--------|------|---------|-------------|
|
|
1001
|
+
| `enabled` | `boolean` | `true` | Tracker-level flag you can use to enable or disable the overlay flow in your app |
|
|
1002
|
+
| `position` | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left'` | `'bottom-right'` | Overlay position |
|
|
1003
|
+
| `theme` | `'dark' \| 'light'` | `'dark'` | Typed visual theme; the current UI is optimized for dark mode |
|
|
1004
|
+
| `maxHistory` | `number` | `500` | Maximum requests in history |
|
|
1005
|
+
| `keyboardShortcut` | `string` | `'ctrl+shift+n'` | Keyboard shortcut |
|
|
1006
|
+
|
|
1007
|
+
**Behavior notes:**
|
|
1008
|
+
|
|
1009
|
+
- `createDevOverlay()` is a singleton. Multiple calls reuse the same instance.
|
|
1010
|
+
- `theme` is typed as `'dark' | 'light'`, but the current UI is primarily designed for dark mode.
|
|
1011
|
+
- The retry button uses direct `fetch()` for quick revalidation; it does not automatically replay interceptors or advanced `HttpClient` configuration.
|
|
1012
|
+
|
|
1013
|
+
**Note:** Advanced request options such as `transformRequest`, `credentials`, `withCredentials`, `debug`, `logger`, `transport`, `nodeOptions`, and `autoFormData` are already typed, but not all of them are fully wired into the main `HttpClient` pipeline in this release.
|
|
1014
|
+
|
|
1015
|
+
**UI methods:**
|
|
1016
|
+
|
|
1017
|
+
```typescript
|
|
1018
|
+
ui.show() // Show overlay
|
|
1019
|
+
ui.hide() // Hide overlay
|
|
1020
|
+
ui.toggle() // Toggle visibility
|
|
1021
|
+
ui.destroy() // Remove the panel from the DOM
|
|
1022
|
+
|
|
1023
|
+
tracker.clear() // Clear history
|
|
1024
|
+
tracker.getHistory() // Get all requests
|
|
1025
|
+
tracker.getMetrics() // Get metrics
|
|
1026
|
+
tracker.getConfig() // Get resolved config
|
|
1027
|
+
tracker.onChange((request) => {}) // Listen for new tracked requests
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
**Module methods:**
|
|
1031
|
+
|
|
1032
|
+
```typescript
|
|
1033
|
+
getDevOverlay() // Get current { tracker, ui } or null if not created yet
|
|
1034
|
+
destroyDevOverlay() // Destroy the current singleton instance
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
**Exposed data shapes:**
|
|
1038
|
+
|
|
1039
|
+
```typescript
|
|
1040
|
+
type TrackedRequest = {
|
|
1041
|
+
id: string;
|
|
1042
|
+
method: string;
|
|
1043
|
+
url: string;
|
|
1044
|
+
status?: number;
|
|
1045
|
+
duration: number;
|
|
1046
|
+
timestamp: number;
|
|
1047
|
+
cached: boolean;
|
|
1048
|
+
ok: boolean;
|
|
1049
|
+
code?: string;
|
|
1050
|
+
headers: Record<string, string>;
|
|
1051
|
+
body?: unknown;
|
|
1052
|
+
responseHeaders?: Record<string, string>;
|
|
1053
|
+
retryCount: number;
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
type DevMetrics = {
|
|
1057
|
+
totalRequests: number;
|
|
1058
|
+
successfulRequests: number;
|
|
1059
|
+
failedRequests: number;
|
|
1060
|
+
cachedRequests: number;
|
|
1061
|
+
avgDuration: number;
|
|
1062
|
+
maxDuration: number;
|
|
1063
|
+
minDuration: number;
|
|
1064
|
+
requestsPerSecond: number;
|
|
1065
|
+
slowestRequests: TrackedRequest[];
|
|
1066
|
+
};
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
---
|
|
1070
|
+
|
|
1204
1071
|
## Streaming
|
|
1205
1072
|
|
|
1206
1073
|
Handle large files and streaming responses:
|
|
@@ -1349,22 +1216,6 @@ deferred.reject(new Error("failed"));
|
|
|
1349
1216
|
| `MAX_RETRIES` | All retry attempts exhausted |
|
|
1350
1217
|
| `UNKNOWN_ERROR` | Unclassified error |
|
|
1351
1218
|
|
|
1352
|
-
### Enhanced Error Context
|
|
1353
|
-
|
|
1354
|
-
Nexa provides rich error context similar to axios, including the original request, response, and configuration in error objects. This makes debugging easier.
|
|
1355
|
-
|
|
1356
|
-
```typescript
|
|
1357
|
-
const result = await client.get('/api');
|
|
1358
|
-
if (!result.ok) {
|
|
1359
|
-
const { request, response, config } = result.error;
|
|
1360
|
-
console.log('Failed request URL:', request?.url);
|
|
1361
|
-
console.log('Response status:', response?.status);
|
|
1362
|
-
console.log('Config timeout:', config?.timeout);
|
|
1363
|
-
}
|
|
1364
|
-
```
|
|
1365
|
-
|
|
1366
|
-
All errors now include `request`, `response` (if available), and `config` properties.
|
|
1367
|
-
|
|
1368
1219
|
### HttpError Class
|
|
1369
1220
|
|
|
1370
1221
|
```typescript
|
|
@@ -1474,23 +1325,40 @@ Nexa ships in multiple module formats:
|
|
|
1474
1325
|
|
|
1475
1326
|
## Development
|
|
1476
1327
|
|
|
1477
|
-
### Tests
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
# Run all tests
|
|
1483
|
-
npm test
|
|
1328
|
+
### Tests
|
|
1329
|
+
|
|
1330
|
+
```bash
|
|
1331
|
+
# Run all tests
|
|
1332
|
+
npm test
|
|
1484
1333
|
|
|
1485
1334
|
# Watch mode
|
|
1486
1335
|
npm run test:watch
|
|
1487
1336
|
|
|
1488
1337
|
# Coverage report
|
|
1489
1338
|
npm run test:coverage
|
|
1339
|
+
|
|
1340
|
+
# Test UI
|
|
1341
|
+
npm run test:ui
|
|
1490
1342
|
```
|
|
1491
1343
|
|
|
1492
1344
|
Tests use **Vitest** (globals mode) with BDD style (`describe`/`it`/`expect`).
|
|
1493
1345
|
|
|
1346
|
+
### Linting & Formatting
|
|
1347
|
+
|
|
1348
|
+
```bash
|
|
1349
|
+
# Type check
|
|
1350
|
+
npm run lint
|
|
1351
|
+
|
|
1352
|
+
# Fix style issues
|
|
1353
|
+
npm run lint:fix
|
|
1354
|
+
|
|
1355
|
+
# Format code
|
|
1356
|
+
npm run format
|
|
1357
|
+
|
|
1358
|
+
# Check formatting
|
|
1359
|
+
npm run format:check
|
|
1360
|
+
```
|
|
1361
|
+
|
|
1494
1362
|
### Build
|
|
1495
1363
|
|
|
1496
1364
|
```bash
|
|
@@ -1517,57 +1385,33 @@ dist/
|
|
|
1517
1385
|
βββ types/ (TypeScript .d.ts declarations)
|
|
1518
1386
|
```
|
|
1519
1387
|
|
|
1520
|
-
###
|
|
1521
|
-
|
|
1522
|
-
**Overall Coverage: 71.4%** β solid unit test coverage with mock-based HTTP testing
|
|
1523
|
-
|
|
1524
|
-
| Component | Coverage | Details |
|
|
1525
|
-
| ----------- | ---------- | --------------------------------- |
|
|
1526
|
-
| HTTP Client | **66.7%** | 67.3% branches, 67.0% functions |
|
|
1527
|
-
| Types | **100%** | Perfect type coverage |
|
|
1528
|
-
| Testing | **93.7%** | 85.1% branches, 95.8% functions |
|
|
1529
|
-
| Utils | **71.8%** | 66.7% branches, 81.1% functions |
|
|
1530
|
-
|
|
1531
|
-
**HTTP Client** (`test/http-client.test.ts`) β **88 tests**:
|
|
1532
|
-
|
|
1533
|
-
- β Core methods: create, GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS (7 tests)
|
|
1534
|
-
- β Retry strategies & timeouts (3 tests)
|
|
1535
|
-
- β Interceptors & disposal (5 tests)
|
|
1536
|
-
- β Caching & validation (4 tests)
|
|
1537
|
-
- β Type safety & extensions (3 tests)
|
|
1538
|
-
- β Pagination & polling (5 tests)
|
|
1539
|
-
- β Response type handling: all 8+ types + auto-detection (13 tests)
|
|
1540
|
-
- β Binary content-type detection: image/_, audio/_, video/\*, octet-stream (5 tests)
|
|
1541
|
-
- β Body serialization: JSON, null, strings, Blob, URLSearchParams, ArrayBuffer, TypedArray, FormData, ReadableStream (7 tests)
|
|
1542
|
-
- β Error normalization: TimeoutError, AbortError, TypeError, unknown, NETWORK_ERROR (5+ tests)
|
|
1543
|
-
- β Request management: activeRequests, cancelAll, clearCache (2 tests)
|
|
1544
|
-
- β Module exports verification: all 8 export categories (8 tests)
|
|
1545
|
-
- β Plugin integration: LoggerPlugin, MetricsPlugin event handlers (7 tests)
|
|
1546
|
-
- β Advanced configuration: null body, direct Blob, abort messages (5+ tests)
|
|
1547
|
-
|
|
1548
|
-
**Utilities** (`test/utils.test.ts`) \u2014 **69 tests**:
|
|
1549
|
-
|
|
1550
|
-
- \u2713 Validators: schema, required fields, type checks (4 tests)
|
|
1551
|
-
- \u2713 Transformers: snakeβcamel case, flatten, projection, wrapper (5 tests)
|
|
1552
|
-
- \u2713 Retry Strategies: Aggressive, Conservative, Circuit Breaker (10 tests)
|
|
1553
|
-
- \u2713 Timeout & Retry: withTimeout, retry function (6 tests)
|
|
1554
|
-
- \u2713 Cache: CacheStore CRUD, TTL expiry (5 tests)
|
|
1555
|
-
- \u2713 Deduplication: RequestDeduplicator sharing, cleanup (3 tests)
|
|
1556
|
-
- \u2713 Middleware Pipeline: ordering, next() guard, legacy pipeline (3 tests)
|
|
1557
|
-
- \u2713 Cache Middleware: GET caching, POST bypass (2 tests)
|
|
1558
|
-
- \u2713 Dedup Middleware: GET dedup, POST bypass (2 tests)
|
|
1559
|
-
- \u2713 Typed Generics: TypedResponse, TypedObservable (map/filter), Defer, type guards, branded types (9 tests)
|
|
1560
|
-
- \u2713 Plugins: PluginManager, LoggerPlugin, MetricsPlugin, CachePlugin, DedupePlugin (5 tests)\n\n### Coverage Limitations & Realistic Ceiling\n\nUnit test coverage plateaus around **75-80%** due to inherent mock-based testing limitations:\n\n**Why not 95%?**\n- **Streaming features** (~3-5% gap): Download progress tracking uses `ReadableStream.getReader()` which requires real HTTP streamsβnot mockable with `fetch-mock`\n- **Utility examples** (~5-10% gap): Middleware patterns and reference code are intentionally not exercised in production \n- **Export-only files** (~2-3% gap): `http-client/index.ts` verified via import validation, not unit testable\n\n**Realistic maximums:**\n- Unit tests + mocks: **~80-85%** ceiling (current: 71.4%)\n- Integration tests required: Would reach 90%+ but beyond this project\u2019s scope\n\nThe 71.4% coverage represents comprehensive testing of all **production code paths** that can be reached via HTTP mocks.
|
|
1388
|
+
### Benchmark
|
|
1561
1389
|
|
|
1562
|
-
|
|
1390
|
+
```bash
|
|
1391
|
+
# Run performance benchmark
|
|
1392
|
+
npm run benchmark
|
|
1393
|
+
```
|
|
1563
1394
|
|
|
1564
|
-
|
|
1395
|
+
### Examples
|
|
1565
1396
|
|
|
1566
|
-
|
|
1397
|
+
See the `examples/` folder for usage examples with different frameworks:
|
|
1567
1398
|
|
|
1568
|
-
|
|
1399
|
+
- `react-example.ts` β Usage with React
|
|
1400
|
+
- `vue-example.ts` β Usage with Vue
|
|
1401
|
+
- `svelte-example.ts` β Usage with Svelte
|
|
1402
|
+
- `node-example.ts` β Usage with Node.js
|
|
1569
1403
|
|
|
1570
|
-
|
|
1404
|
+
### Test Coverage
|
|
1405
|
+
|
|
1406
|
+
Generate the latest report locally:
|
|
1407
|
+
|
|
1408
|
+
```bash
|
|
1409
|
+
npm run test:coverage
|
|
1410
|
+
```
|
|
1411
|
+
|
|
1412
|
+
Coverage changes as features evolve, so the generated report should be treated as the source of truth instead of hardcoded percentages in the README.
|
|
1413
|
+
|
|
1414
|
+
---
|
|
1571
1415
|
|
|
1572
1416
|
## License
|
|
1573
1417
|
|