@bereasoftware/nexa 1.0.5 → 1.2.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 +296 -10
- package/README.md +378 -10
- package/dist/bereasoftware-nexa-1.2.0.tgz +0 -0
- package/dist/nexa.cjs.js +2 -1
- package/dist/nexa.cjs.js.map +1 -0
- package/dist/nexa.es.js +1246 -223
- package/dist/nexa.es.js.map +1 -0
- package/dist/nexa.iife.js +2 -1
- package/dist/nexa.iife.js.map +1 -0
- package/dist/nexa.umd.js +2 -1
- package/dist/nexa.umd.js.map +1 -0
- package/dist/types/http-client/http-client.d.ts +22 -10
- package/dist/types/http-client/index.d.ts +1 -1
- package/dist/types/http-client/node-http-adapter.d.ts +46 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/realtime/index.d.ts +9 -0
- package/dist/types/realtime/plugin.d.ts +12 -0
- package/dist/types/realtime/sse-client.d.ts +5 -0
- package/dist/types/realtime/websocket-client.d.ts +5 -0
- package/dist/types/testing/index.d.ts +5 -0
- package/dist/types/testing/mock-client.d.ts +152 -0
- package/dist/types/types/index.d.ts +348 -7
- package/dist/types/utils/index.d.ts +68 -0
- package/package.json +20 -8
package/README.en.md
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<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-
|
|
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>
|
|
11
11
|
<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
12
|
<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
13
|
<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
|
-
<img src="https://img.shields.io/badge/Node-
|
|
14
|
+
<img src="https://img.shields.io/badge/Node-20%2B-success?style=for-the-badge" alt="Node" />
|
|
15
15
|
<img src="https://img.shields.io/badge/TypeScript-5.x-3178C6?style=for-the-badge" alt="TypeScript" />
|
|
16
16
|
<img src="https://img.shields.io/badge/Dependencies-Zero-brightgreen?style=for-the-badge" alt="Dependencies" />
|
|
17
17
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge" alt="License" /></a>
|
|
@@ -48,6 +48,13 @@
|
|
|
48
48
|
| Validators & transformers | ❌ | ❌ | ✅ |
|
|
49
49
|
| Response duration tracking | ❌ | ❌ | ✅ |
|
|
50
50
|
| 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) | ❌ | ❌ | ✅ |
|
|
51
58
|
| Tree-shakeable | ✅ | ❌ | ✅ |
|
|
52
59
|
|
|
53
60
|
---
|
|
@@ -64,6 +71,10 @@
|
|
|
64
71
|
- [Path Parameters](#path-parameters)
|
|
65
72
|
- [Query Parameters](#query-parameters)
|
|
66
73
|
- [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)
|
|
67
78
|
- [Response Types](#response-types)
|
|
68
79
|
- [Timeout](#timeout)
|
|
69
80
|
- [Retry Strategies](#retry-strategies)
|
|
@@ -91,9 +102,12 @@
|
|
|
91
102
|
- [Streaming](#streaming)
|
|
92
103
|
- [Typed Generics](#typed-generics)
|
|
93
104
|
- [Error Handling](#error-handling)
|
|
105
|
+
- [Enhanced Error Context](#enhanced-error-context)
|
|
94
106
|
- [API Reference](#api-reference)
|
|
95
107
|
- [Build Formats](#build-formats)
|
|
96
108
|
- [Development](#development)
|
|
109
|
+
- [Contributing](#contributing)
|
|
110
|
+
- [Security](#security)
|
|
97
111
|
- [License](#license)
|
|
98
112
|
|
|
99
113
|
---
|
|
@@ -207,6 +221,8 @@ const client = createHttpClient({
|
|
|
207
221
|
| `maxConcurrent` | `number` | `0` (unlimited) | Max concurrent requests |
|
|
208
222
|
| `defaultResponseType` | `ResponseType` | `'auto'` | Default response parsing strategy |
|
|
209
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. |
|
|
210
226
|
|
|
211
227
|
---
|
|
212
228
|
|
|
@@ -289,6 +305,8 @@ Nexa automatically detects and serializes the request body:
|
|
|
289
305
|
| `ArrayBuffer` | Passed as-is | `application/octet-stream` |
|
|
290
306
|
| `ReadableStream` | Passed as-is | `application/octet-stream` |
|
|
291
307
|
|
|
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
|
+
|
|
292
310
|
```typescript
|
|
293
311
|
// JSON (automatic)
|
|
294
312
|
await client.post("/users", { name: "John" });
|
|
@@ -305,6 +323,172 @@ await client.post(
|
|
|
305
323
|
);
|
|
306
324
|
```
|
|
307
325
|
|
|
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
|
+
|
|
308
492
|
### Response Types
|
|
309
493
|
|
|
310
494
|
Control how the response body is parsed:
|
|
@@ -349,10 +533,66 @@ const result = await client.get("/slow-endpoint", { timeout: 5000 });
|
|
|
349
533
|
// Timeout produces a specific error code
|
|
350
534
|
if (!result.ok && result.error.code === "TIMEOUT") {
|
|
351
535
|
console.log("Request timed out");
|
|
352
|
-
}
|
|
353
536
|
```
|
|
354
537
|
|
|
355
|
-
|
|
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
|
+
---
|
|
356
596
|
|
|
357
597
|
## Retry Strategies
|
|
358
598
|
|
|
@@ -367,6 +607,27 @@ const result = await client.get("/unstable-api", {
|
|
|
367
607
|
// Retries up to 3 times with exponential backoff + jitter
|
|
368
608
|
```
|
|
369
609
|
|
|
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
|
+
|
|
370
631
|
### AggressiveRetry
|
|
371
632
|
|
|
372
633
|
Retries all errors up to max attempts with minimal delay:
|
|
@@ -1088,6 +1349,22 @@ deferred.reject(new Error("failed"));
|
|
|
1088
1349
|
| `MAX_RETRIES` | All retry attempts exhausted |
|
|
1089
1350
|
| `UNKNOWN_ERROR` | Unclassified error |
|
|
1090
1351
|
|
|
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
|
+
|
|
1091
1368
|
### HttpError Class
|
|
1092
1369
|
|
|
1093
1370
|
```typescript
|
|
@@ -1199,7 +1476,7 @@ Nexa ships in multiple module formats:
|
|
|
1199
1476
|
|
|
1200
1477
|
### Tests
|
|
1201
1478
|
|
|
1202
|
-
**
|
|
1479
|
+
**205 tests in total**: 88 HTTP Client tests + 69 utilities tests + 24 mock tests + 4 Node adapter tests + 20 additional tests
|
|
1203
1480
|
|
|
1204
1481
|
```bash
|
|
1205
1482
|
# Run all tests
|
|
@@ -1242,13 +1519,14 @@ dist/
|
|
|
1242
1519
|
|
|
1243
1520
|
### Test Coverage
|
|
1244
1521
|
|
|
1245
|
-
**Overall Coverage:
|
|
1522
|
+
**Overall Coverage: 71.4%** — solid unit test coverage with mock-based HTTP testing
|
|
1246
1523
|
|
|
1247
1524
|
| Component | Coverage | Details |
|
|
1248
1525
|
| ----------- | ---------- | --------------------------------- |
|
|
1249
|
-
| HTTP Client | **
|
|
1526
|
+
| HTTP Client | **66.7%** | 67.3% branches, 67.0% functions |
|
|
1250
1527
|
| Types | **100%** | Perfect type coverage |
|
|
1251
|
-
|
|
|
1528
|
+
| Testing | **93.7%** | 85.1% branches, 95.8% functions |
|
|
1529
|
+
| Utils | **71.8%** | 66.7% branches, 81.1% functions |
|
|
1252
1530
|
|
|
1253
1531
|
**HTTP Client** (`test/http-client.test.ts`) — **88 tests**:
|
|
1254
1532
|
|
|
@@ -1279,10 +1557,18 @@ dist/
|
|
|
1279
1557
|
- \u2713 Cache Middleware: GET caching, POST bypass (2 tests)
|
|
1280
1558
|
- \u2713 Dedup Middleware: GET dedup, POST bypass (2 tests)
|
|
1281
1559
|
- \u2713 Typed Generics: TypedResponse, TypedObservable (map/filter), Defer, type guards, branded types (9 tests)
|
|
1282
|
-
- \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:
|
|
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.
|
|
1283
1561
|
|
|
1284
1562
|
---
|
|
1285
1563
|
|
|
1564
|
+
## Contributing
|
|
1565
|
+
|
|
1566
|
+
Want to contribute to Nexa? Please read our [contributing guide](CONTRIBUTING.md) and [code of conduct](CODE_OF_CONDUCT.md).
|
|
1567
|
+
|
|
1568
|
+
## Security
|
|
1569
|
+
|
|
1570
|
+
To report security vulnerabilities, see our [security policy](SECURITY.md).
|
|
1571
|
+
|
|
1286
1572
|
## License
|
|
1287
1573
|
|
|
1288
1574
|
MIT © [John Andrade](mailto:johnandrade@bereasoft.com) — [@bereasoftware](https://github.com/Berea-Soft)
|