opticore-cache 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,39 +1,34 @@
1
1
  # opticore-cache
2
2
 
3
- ### Enterprise‑grade Universal HTTP Cache for OptiCoreJs and also Node.js
3
+ ### Enterprise‑grade Universal HTTP Cache for OptiCoreJs and Node.js
4
4
 
5
5
  [![npm
6
- version](https://img.shields.io/npm/v/@opticore/http-cache.svg)](https://www.npmjs.com/package/opticore-cache)
7
- [![license](https://img.shields.io/npm/l/@opticore/http-cache.svg)](LICENSE)
6
+ version](https://img.shields.io/npm/v/opticore-cache.svg)](https://www.npmjs.com/package/opticore-cache)
7
+ [![license](https://img.shields.io/npm/l/opticore-cache.svg)](#license)
8
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
9
9
  [![Node](https://img.shields.io/badge/Node.js-18%2B-green.svg)](https://nodejs.org/)
10
10
 
11
- High‑performance, flexible and production‑ready HTTP caching layer
12
- compatible with\
13
- **Express · Fetch · Axios · Native Node HTTP · cURL**
14
- :::
11
+ High‑performance, flexible and production‑ready HTTP caching layer\
12
+ compatible with **Express · Fetch · Axios · Native Node HTTP · cURL**
15
13
 
16
14
 
17
-
18
- # 📑 Table of Contents
15
+ # Table of Contents
19
16
 
20
17
  - [Why opticore-cache ?](#why-opticore-cache)
21
18
  - [Key Features](#key-features)
22
19
  - [Installation](#installation)
23
20
  - [Quick Start](#quick-start)
24
- - [Configuration](#configuration)
25
- - [Usage Examples](#usage-examples)
21
+ - [HttpCacheFactory Usage](#HttpCacheFactory-Usage)
22
+ - [Configuration Options](#Configuration-Options)
23
+ - [Advanced Usage](#Advanced-Usage)
26
24
  - [API Reference](#api-reference)
27
- - [Cache Strategy & Architecture](#cache-strategy--architecture)
28
- - [Performance Considerations](#performance-considerations)
29
25
  - [Testing](#testing)
30
26
  - [Security](#security)
31
27
  - [License](#license)
32
28
  - [Contributing](#contributing)
33
29
 
34
30
 
35
-
36
- # Why opticore-cache?
31
+ # Why opticore-cache?
37
32
 
38
33
  Modern applications require:
39
34
 
@@ -43,23 +38,21 @@ Modern applications require:
43
38
  - Observability & metrics
44
39
  - Flexible storage strategies
45
40
 
46
- `@opticore/http-cache` provides a **unified abstraction layer** over
47
- multiple HTTP clients with an extensible and configurable caching
48
- engine.
49
-
41
+ `opticore-cache` provides a unified abstraction layer over multiple HTTP
42
+ clients with an extensible and configurable caching engine.
50
43
 
51
44
 
52
45
  # Key Features
53
46
 
54
- - Automatic HTTP response caching
55
- - Multi-client support (Fetch, Axios, Node HTTP, cURL, Express middleware)
56
- - Memory, Disk or Hybrid storage
57
- - Per-request TTL override
58
- - Pattern-based invalidation (wildcards supported)
59
- - Built-in statistics & hit-rate tracking
60
- - Namespaces for multi-tenant systems
61
- - TypeScript-first design
62
- - Production-safe retry & timeout handling
47
+ - Automatic HTTP response caching
48
+ - Multi-client support (Fetch, Axios, Node HTTP, cURL)
49
+ - Memory, Disk or Hybrid storage
50
+ - Per-request TTL override
51
+ - Pattern-based invalidation (wildcards supported)
52
+ - Built-in statistics & hit-rate tracking
53
+ - Namespaces for multi-tenant systems
54
+ - TypeScript-first design
55
+ - Instance lifecycle management (create / destroy)
63
56
 
64
57
 
65
58
 
@@ -69,7 +62,7 @@ engine.
69
62
  npm install opticore-cache
70
63
  ```
71
64
 
72
- ### Optional Dependencies
65
+ Optional dependencies:
73
66
 
74
67
  ``` bash
75
68
  npm install axios express
@@ -78,212 +71,515 @@ npm install axios express
78
71
 
79
72
  # Quick Start
80
73
 
81
- ## Express Middleware (Zero‑Config)
82
-
83
74
  ``` ts
84
- import express from "express";
85
- import { createExpressMiddleware } from "opticore-cache";
75
+ import { HttpCacheFactory } from "opticore-cache";
86
76
 
87
- const app = express();
77
+ const cache = HttpCacheFactory.create("my-app");
88
78
 
89
- app.use(createExpressMiddleware({
90
- cache: { ttl: 60000 }
91
- }));
79
+ const data = await cache.getWithCache("https://api.example.com/users");
92
80
 
93
- app.get("/users/:id", (req, res) => {
94
- res.json({ id: req.params.id, name: "John Doe" });
95
- });
96
-
97
- app.listen(3000);
81
+ console.log(data);
98
82
  ```
99
83
 
100
84
 
85
+ # HttpCacheFactory Usage
101
86
 
102
- # Configuration
103
-
104
- ## Default Configuration
105
-
106
- ``` ts
107
- const defaultConfig = {
108
- cache: {
109
- enabled: true,
110
- ttl: 300000,
111
- maxSize: 10000,
112
- storage: "disk", // memory | disk | hybrid
113
- storageOptions: {
114
- diskDir: "./storage/cache/http"
115
- }
116
- },
117
- http: {
118
- defaultClient: "fetch",
119
- timeout: 30000,
120
- retries: 3,
121
- retryDelay: 1000
122
- },
123
- clients: {
124
- fetch: { credentials: "omit" },
125
- axios: { baseURL: "" },
126
- nodeHttp: { keepAlive: true },
127
- curl: { binaryPath: "/usr/bin/curl" }
128
- }
129
- };
130
- ```
131
-
87
+ The `HttpCacheFactory` is responsible for:
132
88
 
89
+ - Creating configured cache instances
90
+ - Managing instance reuse
91
+ - Supporting multiple HTTP clients
92
+ - Destroying instances when needed
93
+ - Adapting existing cache services
133
94
 
134
- # Usage Examples
135
95
 
136
- ## Fetch Client
96
+ ## Create with Fetch (default)
137
97
 
138
98
  ``` ts
139
- import { HttpCacheFactory } from "@opticore/http-cache";
140
-
141
- const cache = HttpCacheFactory.create("my-app", {
99
+ const cache = HttpCacheFactory.create("app-fetch", {
142
100
  clientType: "fetch",
143
101
  storageType: "disk",
144
- diskDir: "./storage/cache/my-app",
102
+ diskDir: "./storage/cache",
145
103
  defaultTTL: 60000
146
104
  });
147
-
148
- const user = await cache.getWithCache(
149
- "https://jsonplaceholder.typicode.com/users/1",
150
- {},
151
- { timeToLive: 30000 }
152
- );
153
-
154
- console.log(user._metadata.cached ? "From cache" : "From API");
155
105
  ```
156
106
 
157
107
 
158
-
159
- ## Axios Client
108
+ ## Create with Axios
160
109
 
161
110
  ``` ts
162
- const cache = HttpCacheFactory.create("axios-app", {
111
+ const axiosCache = HttpCacheFactory.create("app-axios", {
163
112
  clientType: "axios",
164
113
  clientOptions: {
165
114
  baseURL: "https://jsonplaceholder.typicode.com",
166
115
  timeout: 5000
167
116
  }
168
117
  });
169
-
170
- const data = await cache.getWithCache("/users/1");
171
118
  ```
172
119
 
173
120
 
121
+ ## Create with Node HTTP
174
122
 
175
- # API Reference
123
+ ``` ts
124
+ const nodeCache = HttpCacheFactory.create("app-node", {
125
+ clientType: "node-http",
126
+ clientOptions: {
127
+ timeout: 8000
128
+ }
129
+ });
130
+ ```
176
131
 
177
- ## Factory
132
+
133
+ ## Create with cURL
178
134
 
179
135
  ``` ts
180
- HttpCacheFactory.create(appName: string, config?: HttpCacheConfig)
136
+ const curlCache = HttpCacheFactory.create("app-curl", {
137
+ clientType: "curl",
138
+ clientOptions: {
139
+ timeout: 10000
140
+ }
141
+ });
181
142
  ```
182
143
 
183
- Returns: `IHttpCacheService`
184
144
 
145
+ ## Destroy an Instance
185
146
 
147
+ ``` ts
148
+ HttpCacheFactory.destroy("app-fetch", "fetch");
149
+ ```
186
150
 
187
- ## Core Methods
188
151
 
189
- ### getWithCache`<T>`()
152
+ ## Adapt Existing Cache
190
153
 
191
154
  ``` ts
192
- getWithCache<T>(
193
- url: string,
194
- options?: RequestInit,
195
- cacheOptions?: {
196
- timeToLive?: number;
197
- customKey?: string;
198
- bypassCache?: boolean;
199
- namespace?: string;
200
- }
201
- ): Promise<T>
155
+ const adapted = HttpCacheFactory.createFromExistingCache(
156
+ customCacheService,
157
+ "custom-namespace"
158
+ );
202
159
  ```
203
160
 
204
161
 
162
+ ## Configuration Options
205
163
 
206
- ### postWithCache`<T>`()
164
+ When creating an instance of the HTTP cache service using `HttpCacheFactory.create()`, you can pass an options object to customize its behavior. Below are the available options:
207
165
 
208
- Optional response caching for POST requests.
166
+ | Option | Type | Description | Exemple |
167
+ |-------------|--------------------|-------------------------------------------------------------------------------------------------------------|----------------------------------------------------------|
168
+ | storageType | 'memory' ou 'disk' | Defines the storage type: in-memory (faster, volatile) or on disk (persistent). | 'disk' |
169
+ | diskDir | string | Required if `storageType: 'disk'`. Path to the directory where cache files will be stored. | 'src/core/cache/app' |
170
+ | defaultTTL | number | Default time-to-live for cache entries (in milliseconds). Used if no `timeToLive` is provided in a request. | 60000 (1 minute) |
171
+ | maxSize | number | Maximum number of entries in the cache. When exceeded, the oldest entries are removed (LRU). | 100 |
172
+ | namespace | string | (Optional) Namespace used to isolate cache keys. Useful when using multiple instances. | 'external-api' |
173
+ | serializer | object | (Optional) Overrides the default serialization/deserialization functions (JSON by default). | {{ serialize: JSON.stringify, deserialize: JSON.parse }} |
209
174
 
210
175
 
176
+ ### Example initialization with all options:
211
177
 
212
- ### invalidateCache(pattern: string)
178
+ ```typescript
179
+ const httpCache = HttpCacheFactory.create('external-api', {
180
+ storageType: 'disk',
181
+ diskDir: 'src/core/cache/demo',
182
+ defaultTTL: 10000, // 10 secondes
183
+ maxSize: 100,
184
+ namespace: 'api-v1'
185
+ });
186
+ ```
213
187
 
214
- Wildcard invalidation:
215
188
 
216
- ``` ts
217
- await cache.invalidateCache("users:*");
189
+ # Advanced Usage
190
+
191
+ The `IHttpCacheService` service offers several advanced features for fine-grained HTTP cache control.
192
+
193
+ ---
194
+
195
+ ## 1. Per-Request Cache Bypass
196
+
197
+ You can bypass the cache for a specific request using the `bypassCache` option. This forces an API call and updates the cache with the new response.
198
+
199
+ ```typescript
200
+ const cacheOptions = {
201
+ bypassCache: req.query.bypass === 'true', // e.g. ?bypass=true
202
+ timeToLive: 60000
203
+ };
204
+ const data = await httpCache.getWithCache(url, {}, cacheOptions);
205
+ ```
206
+
207
+ ---
208
+
209
+ ## 2. Custom Time-To-Live
210
+
211
+ The `timeToLive` option lets you define a custom expiration duration for a specific request, overriding the default value.
212
+
213
+ ---
214
+
215
+ ## 3. Caching POST Requests
216
+
217
+ The `postWithCache` method allows you to cache the response of a POST request. This can be useful when the API always returns the same response for identical input data.
218
+
219
+ ```typescript
220
+ const response = await httpCache.postWithCache(
221
+ url,
222
+ { title, body, userId }, // request body
223
+ {}, // additional headers
224
+ true, // cache the response
225
+ { timeToLive: 30000 } // cache options
226
+ );
227
+ ```
228
+
229
+ ---
230
+
231
+ ## 4. Pattern-Based Invalidation
232
+
233
+ You can invalidate all cache entries whose key matches a given pattern. This is useful for refreshing a set of related resources.
234
+
235
+ ```typescript
236
+ const countExact = await httpCache.invalidateCache('/posts/42'); // Invalidates an exact key
237
+ const countPattern = await httpCache.invalidateCache('/posts/*'); // Invalidates all keys starting with /posts/
238
+ ```
239
+
240
+ ---
241
+
242
+ ## 5. Cache Statistics
243
+
244
+ Retrieve information about the current state of the cache (number of entries, size, etc.).
245
+
246
+ ```typescript
247
+ const stats = await httpCache.getStats();
248
+ console.log(stats);
249
+ // Example output:
250
+ // { size: 45, maxSize: 100, hits: 120, misses: 30, ... }
251
+ ```
252
+
253
+ ---
254
+
255
+ ## 6. Full Cache Flush
256
+
257
+ To completely clear the cache (all entries), use:
258
+
259
+ ```typescript
260
+ await httpCache.clearHttpCache();
218
261
  ```
219
262
 
263
+ ---
264
+
265
+ ## 7. Response Metadata
266
+
267
+ Every object returned by `getWithCache` or `postWithCache` includes a `_metadata` property containing information about the origin of the data.
268
+
269
+ ```typescript
270
+ const data = await httpCache.getWithCache(url);
271
+ console.log(data._metadata);
272
+ // {
273
+ // source: 'cache' | 'api',
274
+ // cached: boolean,
275
+ // createdAt: timestamp,
276
+ // ttl: number
277
+ // }
278
+ ```
220
279
 
280
+ ---
221
281
 
222
- ### clearHttpCache()
282
+ ## 8. Error Handling
223
283
 
224
- Clears all entries.
284
+ The service encapsulates both network errors and cache errors. You can intercept them as shown in the controller example.
225
285
 
286
+ ---
226
287
 
288
+ ## 9. Usage with Dynamic Parameters
227
289
 
228
- ### getStats()
290
+ Build the URL with variable parameters (such as `postId`) to create unique cache keys per resource.
291
+
292
+ ```typescript
293
+ const url = `https://api.example.com/posts/${postId}`;
294
+ ```
295
+
296
+ ## Complete example
297
+ ```typescript
298
+ export class UserController {
299
+ private httpCache: IHttpCacheService;
300
+
301
+ constructor() {
302
+ this.httpCache = HttpCacheFactory.create('external-api', {
303
+ storageType: 'disk',
304
+ diskDir: 'src/core/cache/demo',
305
+ defaultTTL: 10000,
306
+ maxSize: 100
307
+ });
308
+ }
309
+
310
+ public async getPostById(req: Request, res: Response) {
311
+ const postId = req.params.id;
312
+ const bypassCache = req.query.bypass === 'true'; // Allows bypassing the cache with ?bypass=true
313
+
314
+ try {
315
+ const url = `https://jsonplaceholder.typicode.com/posts/${postId}`;
316
+
317
+ const cacheOptions = {
318
+ bypassCache: bypassCache,
319
+ timeToLive: 60000 // 1 minute (or use the default value)
320
+ };
321
+
322
+ console.log(`Requête pour le post ${postId}, bypass: ${bypassCache}`);
323
+
324
+ const post = await this.httpCache.getWithCache(url, {}, cacheOptions);
325
+
326
+ // Add information about the source (cache or API)
327
+ const source = post?._metadata?.source || 'unknown';
328
+ const cached = post?._metadata?.cached || false;
329
+
330
+ res.json({
331
+ success: true,
332
+ data: post,
333
+ source: source,
334
+ cached: cached,
335
+ timestamp: new Date().toISOString()
336
+ });
337
+
338
+ } catch (error: any) {
339
+ console.error(`Erreur lors de la récupération du post ${postId}:`, error);
340
+ res.status(500).json({
341
+ success: false,
342
+ error: error.message
343
+ });
344
+ }
345
+ }
346
+
347
+ // Method to retrieve comments for a post, using cache
348
+ public async getCommentsByPostId(req: Request, res: Response) {
349
+ const postId = req.params.id;
350
+ const bypassCache = req.query.bypass === 'true';
351
+
352
+ try {
353
+ const url = `https://jsonplaceholder.typicode.com/posts/${postId}/comments`;
354
+
355
+ const cacheOptions = {
356
+ bypassCache: bypassCache,
357
+ timeToLive: 300000 // 5 minutes
358
+ };
359
+
360
+ const comments: unknown = await this.httpCache.getWithCache(url, {}, cacheOptions);
361
+
362
+ const source: any = comments?._metadata ? comments?._metadata.source : 'unknown';
363
+ const cached: any = comments?._metadata ? comments?._metadata?.cached : false;
364
+
365
+ res.json({
366
+ success: true,
367
+ data: comments,
368
+ source: source,
369
+ cached: cached,
370
+ count: Array.isArray(comments) ? comments.length : 1,
371
+ timestamp: new Date().toISOString()
372
+ });
373
+
374
+ } catch (error: any) {
375
+ console.error(`Erreur pour les commentaires du post ${postId}:`, error);
376
+ res.status(500).json({
377
+ success: false,
378
+ error: error.message
379
+ });
380
+ }
381
+ }
382
+
383
+ // Method for making a POST request with optional caching
384
+ public async createPost(req: Request, res: Response) {
385
+ const { title, body, userId, cacheResponse } = req.body;
386
+ try {
387
+ const url = 'https://jsonplaceholder.typicode.com/posts';
388
+ const response = await this.httpCache.postWithCache(
389
+ url,
390
+ { title, body, userId },
391
+ {},
392
+ cacheResponse || true,
393
+ { timeToLive: 30000 }
394
+ );
395
+ res.json({
396
+ success: true,
397
+ data: response,
398
+ cached: cacheResponse || false,
399
+ timestamp: new Date().toISOString()
400
+ });
401
+ } catch (error: any) {
402
+ console.error('Erreur création de post:', error);
403
+ res.status(500).json({
404
+ success: false,
405
+ error: error.message
406
+ });
407
+ }
408
+ }
409
+
410
+ // Method for obtaining HTTP cache statistics
411
+ public async getCacheStats(req: Request, res: Response) {
412
+ try {
413
+ const stats = await this.httpCache.getStats();
414
+ res.json(stats);
415
+ } catch (error: any) {
416
+ res.status(500).json({
417
+ success: false,
418
+ error: error.message
419
+ });
420
+ }
421
+ }
422
+
423
+ // Method to clear the HTTP cache
424
+ public async clearHttpCache(req: Request, res: Response) {
425
+ try {
426
+ await this.httpCache.clearHttpCache();
427
+ res.json({
428
+ success: true,
429
+ message: 'Cache HTTP vidé avec succès',
430
+ timestamp: new Date().toISOString()
431
+ });
432
+ } catch (error: any) {
433
+ res.status(500).json({
434
+ success: false,
435
+ error: error.message
436
+ });
437
+ }
438
+ }
439
+
440
+ // Method to invalidate a pattern in the HTTP cache
441
+ public async invalidateCachePattern(req: Request, res: Response) {
442
+ const pattern = req.query.pattern as string;
443
+ if (!pattern) {
444
+ return res.status(400).json({
445
+ success: false,
446
+ error: 'Le paramètre "pattern" est requis'
447
+ });
448
+ }
449
+
450
+ try {
451
+ const count = await this.httpCache.invalidateCache(pattern);
452
+ res.json({
453
+ success: true,
454
+ message: `Cache invalidé pour le pattern: ${pattern}`,
455
+ invalidatedCount: count,
456
+ timestamp: new Date().toISOString()
457
+ });
458
+ } catch (error: any) {
459
+ res.status(500).json({
460
+ success: false,
461
+ error: error.message
462
+ });
463
+ }
464
+ }
465
+ }
466
+ ```
467
+
468
+
469
+
470
+ # API Reference
471
+
472
+ ## Factory
229
473
 
230
474
  ``` ts
231
- interface HttpCacheStats {
232
- totalRequests: number;
233
- cacheHits: number;
234
- cacheMisses: number;
235
- cacheSize: number;
236
- hitRate: number;
237
- cachedUrls: string[];
238
- timestamp: string;
239
- enabled: boolean;
240
- }
475
+ HttpCacheFactory.create(appName?: string, config?)
241
476
  ```
242
477
 
478
+ Returns: `IHttpCacheService`
243
479
 
244
480
 
245
- # Cache Strategy & Architecture
246
481
 
247
- Supported storage strategies:
482
+ ## Core Methods
248
483
 
249
- Strategy Use Case
250
- ---------- ----------------------------------
251
- memory High-speed, short-lived cache
252
- disk Persistent cache across restarts
253
- hybrid Memory-first with disk fallback
484
+ ### getWithCache
254
485
 
255
- ### Hybrid Mode
486
+ ``` ts
487
+ await cache.getWithCache("https://api.example.com/data");
488
+ ```
256
489
 
257
- - Memory lookup first
258
- - Disk fallback
259
- - Background refresh supported
490
+ ### postWithCache
260
491
 
492
+ ``` ts
493
+ await cache.postWithCache("https://api.example.com/data", payload, {}, true);
494
+ ```
261
495
 
496
+ ### invalidateCache
262
497
 
263
- # Performance Considerations
498
+ ``` ts
499
+ await cache.invalidateCache("users:*");
500
+ ```
264
501
 
265
- - Reduces outbound API calls
266
- - Improves P95 & P99 latency
267
- - Reduces infrastructure cost
268
- - Prevents API rate-limit exhaustion
502
+ ### clearHttpCache
269
503
 
270
- Recommended for:
504
+ ``` ts
505
+ await cache.clearHttpCache();
506
+ ```
271
507
 
272
- - Microservices
273
- - SaaS platforms
274
- - Multi-tenant APIs
275
- - Serverless environments
508
+ ### getStats
276
509
 
510
+ ``` ts
511
+ const stats = await cache.getStats();
512
+ console.log(stats.hitRate);
513
+ ```
277
514
 
278
515
 
279
516
  # Testing
280
517
 
281
518
  ``` bash
282
- npm test
519
+ Run the example project by this command :
520
+ tsx example/src/bootstrap.ts
521
+
522
+ then run :
523
+
524
+ Method POST :
525
+ curl -X POST "http://localhost:4200/api/users/posts" \
526
+ -H "Content-Type: application/json" \
527
+ -d '{
528
+ "url": "https://jsonplaceholder.typicode.com/posts",
529
+ "data": {
530
+ "title": "test",
531
+ "body": "content",
532
+ "userId": 2
533
+ },
534
+ "cacheResponse": true
535
+ }'
536
+
537
+
538
+ Response :
539
+ {
540
+ "success":true,
541
+ "data": {
542
+ "id": 101,
543
+ "_metadata": {
544
+ "cached": true,
545
+ "timestamp": "2026-02-20T10:56:25.444Z",
546
+ "url": "https://jsonplaceholder.typicode.com/posts",
547
+ "method": "POST",
548
+ "responseTime":433
549
+ }
550
+ },
551
+ "cached": true,
552
+ "timestamp": "2026-02-20T10:56:25.444Z"
553
+ }
554
+
555
+
556
+ Method GET
557
+ curl -X GET "http://localhost:4200/api/users/posts/1"
558
+
559
+ Response :
560
+ {
561
+ "success": true,
562
+ "data": {
563
+ "userId": 1,
564
+ "id": 1,
565
+ "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
566
+ "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto",
567
+ "_metadata": {
568
+ "cached": false,
569
+ "timestamp": "2026-02-20T14:20:38.751Z",
570
+ "url": "https://jsonplaceholder.typicode.com/posts/1",
571
+ "method": "GET",
572
+ "responseTime": 103,
573
+ "cacheHit": false
574
+ }
575
+ },
576
+ "source": "unknown",
577
+ "cached": false,
578
+ "timestamp": "2026-02-20T14:20:38.751Z"
579
+ }
283
580
  ```
284
581
 
285
582
 
286
-
287
583
  # Security
288
584
 
289
585
  - No automatic caching of sensitive headers
@@ -292,13 +588,11 @@ npm test
292
588
  - Safe retry mechanism
293
589
 
294
590
 
295
-
296
591
  # License
297
592
 
298
593
  MIT
299
594
 
300
595
 
301
-
302
596
  # Contributing
303
597
 
304
598
  Contributions are welcome.
@@ -308,7 +602,4 @@ Contributions are welcome.
308
602
  3. Submit a Pull Request
309
603
 
310
604
 
311
-
312
- :::
313
- Built for scalable OptiCoreJs and any Node.js applications
314
- :::
605
+ Built for scalable OptiCoreJs and Node.js applications.