@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 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-233_passing-brightgreen?style=for-the-badge" alt="Tests" /></a>
10
- <a href="#test-coverage"><img src="https://img.shields.io/badge/Coverage-72%25-orange?style=for-the-badge" alt="Coverage" /></a>
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-5.x-3178C6?style=for-the-badge" alt="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
- Nexa also supports differentiated timeouts for connection and response phases, going beyond axios/fetch's single timeout. You can specify timeouts as an object:
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
- **205 tests in total**: 88 HTTP Client tests + 69 utilities tests + 24 mock tests + 4 Node adapter tests + 20 additional tests
1480
-
1481
- ```bash
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
- ### Test Coverage
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
- ## Contributing
1395
+ ### Examples
1565
1396
 
1566
- Want to contribute to Nexa? Please read our [contributing guide](CONTRIBUTING.md) and [code of conduct](CODE_OF_CONDUCT.md).
1397
+ See the `examples/` folder for usage examples with different frameworks:
1567
1398
 
1568
- ## Security
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
- To report security vulnerabilities, see our [security policy](SECURITY.md).
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