accessio 1.0.0 → 1.1.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.
Files changed (40) hide show
  1. package/README.md +121 -391
  2. package/cjs/accessio.cjs +23 -52
  3. package/cjs/accessio.cjs.map +1 -1
  4. package/cjs/core/accessioError.cjs +1 -7
  5. package/cjs/core/accessioError.cjs.map +1 -1
  6. package/cjs/core/buildURL.cjs +1 -5
  7. package/cjs/core/buildURL.cjs.map +1 -1
  8. package/cjs/core/mergeConfig.cjs +2 -4
  9. package/cjs/core/mergeConfig.cjs.map +1 -1
  10. package/cjs/core/request.cjs +44 -66
  11. package/cjs/core/request.cjs.map +1 -1
  12. package/cjs/core/retry.cjs +2 -8
  13. package/cjs/core/retry.cjs.map +1 -1
  14. package/cjs/defaults/index.cjs.map +1 -1
  15. package/cjs/defaults/transforms.cjs.map +1 -1
  16. package/cjs/helpers/debug.cjs +1 -3
  17. package/cjs/helpers/debug.cjs.map +1 -1
  18. package/cjs/helpers/parseHeaders.cjs.map +1 -1
  19. package/cjs/helpers/rateLimiter.cjs +22 -14
  20. package/cjs/helpers/rateLimiter.cjs.map +1 -1
  21. package/cjs/helpers/transformData.cjs.map +1 -1
  22. package/cjs/index.cjs.map +1 -1
  23. package/cjs/interceptors/interceptorManager.cjs +40 -3
  24. package/cjs/interceptors/interceptorManager.cjs.map +1 -1
  25. package/package.json +6 -14
  26. package/src/accessio.ts +28 -72
  27. package/src/core/accessioError.ts +1 -7
  28. package/src/core/buildURL.ts +4 -13
  29. package/src/core/mergeConfig.ts +6 -16
  30. package/src/core/request.ts +44 -74
  31. package/src/core/retry.ts +4 -15
  32. package/src/defaults/index.ts +1 -3
  33. package/src/defaults/transforms.ts +1 -4
  34. package/src/helpers/debug.ts +12 -22
  35. package/src/helpers/parseHeaders.ts +1 -3
  36. package/src/helpers/rateLimiter.ts +28 -23
  37. package/src/helpers/transformData.ts +1 -4
  38. package/src/index.ts +3 -11
  39. package/src/interceptors/interceptorManager.ts +50 -2
  40. package/src/types.ts +7 -68
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
- # accessio ðŸŽŊ
1
+ # Accessio ðŸŽŊ
2
2
 
3
- **Fast, flexible HTTP client for Node.js and browsers — simple, modular, and dependency-free** — lightweight, modern, zero dependencies.
3
+ **Fast, flexible HTTP client for Node.js and browsers — simple, modular, and dependency-free.**
4
+
5
+ `accessio` is a lightweight, modern HTTP client built on top of the native `fetch` API. It provides a familiar, Promise-based interface with advanced features like interceptors, automatic retries, rate limiting, and structured debug logging, all while maintaining **zero external dependencies**.
4
6
 
5
7
  ---
6
8
 
@@ -8,32 +10,32 @@
8
10
 
9
11
  - 🚀 **Promise-based** — works seamlessly with `async`/`await`
10
12
  - 🌐 **Isomorphic** — runs in both browser and Node.js (â‰Ĩ 18)
11
- - ðŸŠķ **Zero dependencies** — lightweight, built on native `fetch`
12
- - 🔄 **Interceptors** — transform requests and responses globally
13
+ - ðŸŠķ **Zero dependencies** — ultra-lightweight, built on native `fetch`
14
+ - 🔄 **Interceptors** — transform requests and responses globally or per instance
13
15
  - ⚙ïļ **Configurable instances** — create multiple API clients with custom defaults
14
- - ðŸ›Ąïļ **Error handling** — structured `AccessioError` with status, config, and response
15
- - ðŸ“Ķ **Dual format** — supports both ESM and CommonJS
16
- - 📐 **TypeScript support** — full type definitions included
17
- - ⏱ïļ **Timeout support** — built-in request timeout via `AbortController`
18
- - 🔧 **Transform pipelines** — customize request/response data transformation
19
- - â™ŧïļ **Automatic Retries** — built-in retry logic with exponential backoff and jitter
20
- - ⏱ïļ **Duration tracking** — every response includes a `duration` field in milliseconds
21
- - ðŸšĨ **Rate Limiter** — limit concurrent requests with acquire/release/destroy lifecycle
22
- - 🐞 **Debug Mode** — structured console logging for outgoing requests and responses
23
- - ðŸŽŊ **Familiar API** — intuitive, developer-friendly interface
16
+ - ðŸ›Ąïļ **Robust Error handling** — structured `AccessioError` with status, config, and response
17
+ - ðŸ“Ķ **Dual format** — full support for both ESM and CommonJS
18
+ - 📐 **TypeScript first** — written in TS with comprehensive type definitions
19
+ - ⏱ïļ **Timeout & Cancellation** — built-in support via `AbortController` and `signal`
20
+ - 🔧 **Transform pipelines** — flexible request/response data transformation
21
+ - â™ŧïļ **Automatic Retries** — smart retry logic with exponential backoff and jitter
22
+ - ðŸšĨ **Rate Limiter** — built-in concurrency control for high-throughput applications
23
+ - 🐞 **Debug Mode** — structured, beautiful console logging for easy development
24
+ - ⏱ïļ **Duration Tracking** — every response includes precise timing metadata
24
25
 
25
26
  ---
26
27
 
27
28
  ## ðŸ“Ķ Installation
28
29
 
29
- accessio is available on both the official npm registry (unscoped) and GitHub Packages (scoped).
30
-
31
30
  ```bash
32
- # Recommended: Standard npm registry
31
+ # Using npm
33
32
  npm install accessio
34
33
 
35
- # Alternative: GitHub Packages
36
- npm install @salvatorecorvaglia/accessio
34
+ # Using yarn
35
+ yarn add accessio
36
+
37
+ # Using pnpm
38
+ pnpm add accessio
37
39
  ```
38
40
 
39
41
  ---
@@ -41,18 +43,18 @@ npm install @salvatorecorvaglia/accessio
41
43
  ## 🚀 Quick Start
42
44
 
43
45
  ```typescript
44
- import accessio from "accessio";
46
+ import accessio from 'accessio';
45
47
 
46
- // GET request
47
- const response = await accessio.get("https://api.example.com/users");
48
- console.log(response.data);
48
+ // Simple GET request
49
+ const { data } = await accessio.get('https://api.example.com/users');
49
50
 
50
51
  // POST request with JSON body
51
- const newUser = await accessio.post("https://api.example.com/users", {
52
- name: "John Doe",
53
- email: "john@example.com",
52
+ const response = await accessio.post('https://api.example.com/users', {
53
+ name: 'John Doe',
54
+ role: 'Developer',
54
55
  });
55
- console.log(newUser.data);
56
+
57
+ console.log(`User created in ${response.duration}ms`);
56
58
  ```
57
59
 
58
60
  ---
@@ -61,427 +63,155 @@ console.log(newUser.data);
61
63
 
62
64
  ### Request Methods
63
65
 
64
- ```javascript
65
- accessio(config)
66
- accessio(url, config?)
67
-
68
- accessio.request(config)
69
- accessio.get(url, config?)
70
- accessio.delete(url, config?)
71
- accessio.head(url, config?)
72
- accessio.options(url, config?)
73
- accessio.post(url, data?, config?)
74
- accessio.put(url, data?, config?)
75
- accessio.patch(url, data?, config?)
76
- accessio.postForm(url, data?, config?)
77
- accessio.putForm(url, data?, config?)
78
- accessio.patchForm(url, data?, config?)
79
- accessio.getUri(config)
80
- ```
81
-
82
- ### Configuration
83
-
84
- ```javascript
85
- {
86
- // URL path (optional if baseURL is set)
87
- url: '/users',
88
-
89
- // HTTP method (default: 'get')
90
- method: 'get',
91
-
92
- // Base URL prepended to `url` unless `url` is absolute
93
- baseURL: 'https://api.example.com',
94
-
95
- // Request headers
96
- headers: {
97
- 'Authorization': 'Bearer token',
98
- 'X-Custom-Header': 'value'
99
- },
100
-
101
- // URL query parameters
102
- params: { page: 1, limit: 10 },
103
-
104
- // Custom params serializer
105
- paramsSerializer: (params) => Qs.stringify(params),
106
-
107
- // Request body (for POST, PUT, PATCH)
108
- data: { name: 'John' },
109
-
110
- // Timeout in milliseconds (0 = no timeout)
111
- timeout: 5000,
112
-
113
- // Expected response type: 'json' | 'text' | 'blob' | 'arraybuffer' | 'stream'
114
- responseType: 'json',
115
-
116
- // Include credentials in cross-site requests
117
- withCredentials: false,
118
-
119
- // Basic auth (sets Authorization header automatically)
120
- auth: { username: 'user', password: 'secret' },
121
-
122
- // Transform functions for request/response data
123
- transformRequest: [(data, headers) => { /* ... */ return data; }],
124
- transformResponse: [(data) => { /* ... */ return data; }],
125
-
126
- // Automatic retry strategy
127
- retry: 3, // Max retry attempts (default: 0)
128
- retryDelay: 1000, // Base delay in ms, doubles on each attempt (default: 1000)
129
- retryCondition: (error) => true, // Custom retry predicate
130
- onRetry: (attempt, error, config) => {}, // Called before each retry
131
-
132
- // Debug logging
133
- debug: true, // Enable request/response debug logger
134
-
135
- // Rate limiting
136
- rateLimiter: limiter, // Rate limiter instance for concurrency control
137
-
138
- // Determine which status codes resolve/reject the promise
139
- validateStatus: (status) => status >= 200 && status < 300,
140
-
141
- // AbortSignal for request cancellation
142
- signal: controller.signal
143
- }
144
- ```
66
+ | Method | Description |
67
+ | :--------------------------------------- | :-------------------------------------- |
68
+ | `accessio(config)` | Generic request using config object |
69
+ | `accessio.get(url, config?)` | GET request |
70
+ | `accessio.post(url, data?, config?)` | POST request |
71
+ | `accessio.put(url, data?, config?)` | PUT request |
72
+ | `accessio.patch(url, data?, config?)` | PATCH request |
73
+ | `accessio.delete(url, config?)` | DELETE request |
74
+ | `accessio.head(url, config?)` | HEAD request |
75
+ | `accessio.options(url, config?)` | OPTIONS request |
76
+ | `accessio.postForm(url, data?, config?)` | POST request with `multipart/form-data` |
145
77
 
146
- ### Response Object
78
+ ### Configuration Options
147
79
 
148
- ```javascript
80
+ ```typescript
149
81
  {
150
- data: {}, // Parsed response body
151
- status: 200, // HTTP status code
152
- statusText: 'OK', // HTTP status message
153
- headers: {}, // Response headers (lowercase keys)
154
- config: {}, // Request configuration used
155
- request: {}, // Underlying fetch Response object
156
- duration: 142 // Request duration in milliseconds
82
+ baseURL: 'https://api.example.com', // Base URL for all requests
83
+ url: '/users', // Relative or absolute URL
84
+ method: 'get', // HTTP method (default: get)
85
+ headers: { 'X-Custom': 'val' }, // Custom headers
86
+ params: { id: 123 }, // URL query parameters
87
+ data: { name: 'John' }, // Request body (JSON/FormData/etc)
88
+ timeout: 5000, // Timeout in ms (default: 0)
89
+ responseType: 'json', // Expected response: 'json', 'text', 'blob', 'stream'
90
+ auth: { username: '', password: '' }, // Basic auth credentials
91
+ retry: 3, // Max retry attempts
92
+ retryDelay: 1000, // Base delay for exponential backoff
93
+ debug: true, // Enable structured logging
94
+ rateLimiter: limiter, // Concurrency limiter instance
95
+ validateStatus: (s) => s < 400, // Resolve/reject predicate
96
+ signal: abortController.signal, // Custom AbortSignal
157
97
  }
158
98
  ```
159
99
 
160
100
  ---
161
101
 
162
- ## 🔧 Creating Instances
102
+ ## â™ŧïļ Advanced Usage
163
103
 
164
- ```typescript
165
- import accessio from "accessio";
104
+ ### Interceptors
166
105
 
167
- const api = accessio.create({
168
- baseURL: "https://api.example.com",
169
- timeout: 10000,
170
- headers: {
171
- Authorization: "Bearer my-token",
172
- },
173
- });
174
-
175
- // All requests will use the instance config
176
- const users = await api.get("/users");
177
- const posts = await api.get("/posts", { params: { limit: 5 } });
178
- ```
179
-
180
- ---
181
-
182
- ## 🔄 Interceptors
106
+ Interceptors allow you to transform requests or responses before they are handled by `then` or `catch`.
183
107
 
184
108
  ```typescript
185
- // Request interceptor — runs before every request
186
- accessio.interceptors.request.use(
187
- (config) => {
188
- config.headers["Authorization"] = `Bearer ${getToken()}`;
189
- return config;
190
- },
191
- (error) => Promise.reject(error),
192
- );
109
+ // Add a request interceptor
110
+ accessio.interceptors.request.use((config) => {
111
+ config.headers['Authorization'] = `Bearer ${storage.getToken()}`;
112
+ return config;
113
+ });
193
114
 
194
- // Response interceptor — runs after every response
115
+ // Add a response interceptor
195
116
  accessio.interceptors.response.use(
196
117
  (response) => response,
197
118
  (error) => {
198
- // Handle 401 globally
199
- if (error.response?.status === 401) {
200
- redirectToLogin();
201
- }
119
+ if (error.response?.status === 401) logout();
202
120
  return Promise.reject(error);
203
121
  },
204
122
  );
205
-
206
- // Conditional interceptor — runs only when the predicate returns true
207
- accessio.interceptors.request.use(
208
- (config) => {
209
- /* ... */ return config;
210
- },
211
- null,
212
- { runWhen: (config) => config.method === "post" },
213
- );
214
-
215
- // Remove a single interceptor
216
- const id = accessio.interceptors.request.use(/* ... */);
217
- accessio.interceptors.request.eject(id);
218
-
219
- // Remove all interceptors
220
- accessio.interceptors.request.clear();
221
123
  ```
222
124
 
223
- **Execution order:**
125
+ ### Error Handling
224
126
 
225
- - Request interceptors run in **reverse** order (last added → first executed)
226
- - Response interceptors run in **normal** order (first added → first executed)
127
+ `accessio` provides a structured error object with specific codes to help you handle failures gracefully.
227
128
 
228
- ---
129
+ | Code | Description |
130
+ | :----------------- | :---------------------------- |
131
+ | `ERR_BAD_REQUEST` | 4xx status code |
132
+ | `ERR_BAD_RESPONSE` | 5xx status code |
133
+ | `ERR_NETWORK` | Network connectivity issues |
134
+ | `ETIMEDOUT` | Request exceeded timeout |
135
+ | `ERR_CANCELED` | Request was manually aborted |
136
+ | `ERR_INVALID_URL` | The provided URL is malformed |
137
+ | `ERR_BAD_OPTION` | Invalid configuration option |
229
138
 
230
- ## ðŸ›Ąïļ Error Handling
139
+ ### Automatic Retries
231
140
 
232
- ```typescript
233
- try {
234
- await accessio.get("/might-fail");
235
- } catch (error) {
236
- if (accessio.isAccessioError(error)) {
237
- if (error.response) {
238
- console.log(error.response.status); // 404, 500, etc.
239
- console.log(error.response.data); // Response body
240
- } else if (error.code === "ERR_NETWORK") {
241
- console.log("Network error — no response received");
242
- } else if (error.code === "ETIMEDOUT") {
243
- console.log("Request timed out");
244
- } else if (accessio.isCancel(error)) {
245
- console.log("Request was cancelled");
246
- }
247
-
248
- console.log(error.config); // Request config
249
- console.log(error.message); // Error message
250
- console.log(error.toJSON()); // Serializable summary
251
- }
252
- }
253
- ```
254
-
255
- ### Error Codes
256
-
257
- | Code | Description |
258
- | --------------------------- | --------------------------------------- |
259
- | `ERR_BAD_REQUEST` | 4xx status code |
260
- | `ERR_BAD_RESPONSE` | 5xx status code |
261
- | `ERR_NETWORK` | Network error (no response received) |
262
- | `ETIMEDOUT` | Request timed out |
263
- | `ECONNABORTED` | Request was aborted |
264
- | `ERR_CANCELED` | Request was cancelled via `AbortSignal` |
265
- | `ERR_BAD_OPTION` | Invalid or missing configuration option |
266
- | `ERR_BAD_OPTION_VALUE` | Invalid configuration option value |
267
- | `ERR_INVALID_URL` | The provided URL is invalid |
268
- | `ERR_FR_TOO_MANY_REDIRECTS` | Too many redirects |
269
- | `ERR_NOT_SUPPORT` | Feature not supported in environment |
270
-
271
- ---
272
-
273
- ## 🔀 Concurrent Requests
141
+ `accessio` includes a powerful retry mechanism that handles network errors and 5xx responses automatically.
274
142
 
275
- ```javascript
276
- const [users, posts] = await accessio.all([
277
- accessio.get("/users"),
278
- accessio.get("/posts"),
279
- ]);
280
-
281
- // Or with the spread helper (deprecated — use modern spread syntax instead)
282
- accessio.all([accessio.get("/users"), accessio.get("/posts")]).then(
283
- accessio.spread((users, posts) => {
284
- console.log(users.data, posts.data);
285
- }),
286
- );
287
- ```
288
-
289
- ---
290
-
291
- ## ❌ Cancellation
292
-
293
- ```javascript
294
- const controller = new AbortController();
295
-
296
- accessio
297
- .get("/slow-endpoint", {
298
- signal: controller.signal,
299
- })
300
- .catch((error) => {
301
- if (accessio.isCancel(error)) {
302
- console.log("Request cancelled:", error.message);
303
- }
304
- });
305
-
306
- // Cancel the request
307
- controller.abort();
308
- ```
309
-
310
- ---
311
-
312
- ## 🔧 Transform Request / Response
313
-
314
- ```javascript
315
- const api = accessio.create({
316
- transformRequest: [
317
- (data, headers) => {
318
- // Add a timestamp to every outgoing body
319
- if (data && typeof data === "object") {
320
- data.timestamp = Date.now();
321
- }
322
- return JSON.stringify(data);
323
- },
324
- ],
325
- transformResponse: [
326
- (data) => {
327
- if (typeof data === "string") {
328
- try {
329
- return JSON.parse(data);
330
- } catch {}
331
- }
332
- return data;
333
- },
334
- (data) => {
335
- // Unwrap a { data: ... } envelope
336
- return data?.data ?? data;
337
- },
338
- ],
339
- });
340
- ```
341
-
342
- ---
343
-
344
- ## â™ŧïļ Advanced Features
345
-
346
- ### Automatic Retry
347
-
348
- accessio supports configurable retries with exponential backoff and Âą25% jitter to prevent thundering herd.
349
-
350
- ```javascript
351
- const response = await accessio.get("/flaky-api", {
352
- retry: 3, // Retry up to 3 times
353
- retryDelay: 1000, // Base delay — doubles on each attempt: 1s, 2s, 4s
354
-
355
- // Optional: custom condition (default: network errors + 5xx)
356
- retryCondition: (error) => error.response?.status === 503,
357
-
358
- // Optional: callback before each retry
359
- onRetry: (attempt, error, config) => {
360
- console.log(`Retry attempt #${attempt} after: ${error.message}`);
361
- },
143
+ ```typescript
144
+ const response = await accessio.get('/flaky-endpoint', {
145
+ retry: 5,
146
+ retryDelay: 1000, // Delays: 1s, 2s, 4s, 8s, 16s (+/- random jitter)
147
+ onRetry: (attempt, error) => console.log(`Retry #${attempt}...`),
362
148
  });
363
149
  ```
364
150
 
365
- The default `retryCondition` retries on:
366
-
367
- - `ERR_NETWORK` — no response received
368
- - `ETIMEDOUT` — request timed out
369
- - 5xx server errors
370
- - Does **not** retry on `ERR_CANCELED` or 4xx client errors
371
-
372
151
  ### Rate Limiting
373
152
 
374
- Control the maximum number of concurrent in-flight requests.
375
-
376
- #### Option 1: Built-in config integration (recommended)
377
-
378
- ```javascript
379
- import accessio, { createRateLimiter } from "accessio";
380
-
381
- const limiter = createRateLimiter(5); // max 5 concurrent requests
153
+ Limit concurrent requests globally or per-instance to prevent overloading APIs.
382
154
 
383
- // Pass the limiter directly in the request config
384
- const response = await accessio.get("/api/data", { rateLimiter: limiter });
155
+ ```typescript
156
+ import { createRateLimiter } from 'accessio';
385
157
 
386
- // Or set it as an instance default
158
+ const limiter = createRateLimiter(5); // Max 5 concurrent requests
387
159
  const api = accessio.create({ rateLimiter: limiter });
388
- const users = await api.get("/users");
389
- ```
390
-
391
- #### Option 2: Manual acquire / release
392
160
 
393
- ```javascript
394
- import { createRateLimiter } from "accessio";
395
-
396
- const limiter = createRateLimiter(5);
397
-
398
- await limiter.acquire();
399
- try {
400
- const res = await accessio.get("/api/data");
401
- } finally {
402
- limiter.release();
403
- }
404
-
405
- // Inspect state
406
- console.log(limiter.active); // Currently running requests
407
- console.log(limiter.pending); // Requests waiting in queue
408
-
409
- // Cleanup on navigation / component unmount — rejects all queued promises
410
- limiter.destroy();
411
- console.log(limiter.destroyed); // true
161
+ // Requests will wait in queue if limit is reached
162
+ const results = await Promise.all([
163
+ api.get('/req-1'),
164
+ api.get('/req-2'),
165
+ // ...
166
+ ]);
412
167
  ```
413
168
 
414
- ### Debug Logging
169
+ ### Debug Mode
415
170
 
416
- Enable `debug: true` on an instance or a single request to get structured console output.
417
-
418
- ```javascript
419
- const api = accessio.create({ debug: true });
171
+ Get beautiful, structured logs in your console by enabling `debug: true`.
420
172
 
173
+ ```typescript
421
174
  // ðŸĶ‍⮛ [accessio] → GET https://api.example.com/users
422
175
  // Params: {"page":1}
423
176
  // Timeout: 5000ms
424
177
  // ðŸĶ‍⮛ [accessio] ← ✅ 200 OK (142ms)
425
178
  // Size: ~3.2 KB
426
-
427
- // Or enable per-request only
428
- await accessio.get("/debug-this", { debug: true });
429
179
  ```
430
180
 
431
181
  ---
432
182
 
433
- ## ðŸ“Ĩ Imports
183
+ ## 🛠ïļ Developer Guide
434
184
 
435
- ### ESM (recommended)
185
+ ### Local Setup
436
186
 
437
- ```typescript
438
- import accessio from "accessio";
439
- import {
440
- createRateLimiter,
441
- AccessioError,
442
- mergeConfig,
443
- buildURL,
444
- logRequest,
445
- logResponse,
446
- logError,
447
- } from "accessio";
187
+ ```bash
188
+ git clone https://github.com/salvatorecorvaglia/accessio.git
189
+ cd accessio
190
+ npm install
448
191
  ```
449
192
 
450
- ### CommonJS
193
+ ### Available Scripts
451
194
 
452
- ```typescript
453
- const accessio = require("accessio");
454
- const { createRateLimiter } = require("accessio");
455
- ```
456
-
457
- ### Sub-path imports
458
-
459
- ```javascript
460
- // Import only what you need
461
- import { createRateLimiter } from "accessio/helpers/rateLimiter";
462
- import { logRequest, logResponse, logError } from "accessio/helpers/debug";
463
- import { buildURL } from "accessio/core/buildURL";
464
- import { mergeConfig } from "accessio/core/mergeConfig";
465
- import { dispatchRequest } from "accessio/core/request";
466
- import { retryRequest } from "accessio/core/retry";
467
- import { parseHeaders } from "accessio/helpers/parseHeaders";
468
- import { settle } from "accessio/helpers/settle";
469
- import { transformData } from "accessio/helpers/transformData";
470
- ```
195
+ - `npm run build`: Generate ESM and CommonJS bundles
196
+ - `npm run test`: Run the full test suite with Vitest
197
+ - `npm run lint`: Check for code style issues
198
+ - `npm run format`: Automatically format the codebase with Prettier
199
+ - `npm run typecheck`: Validate TypeScript types
471
200
 
472
201
  ---
473
202
 
474
- ## 📚 Documentation
203
+ ## ðŸĪ Contributing
204
+
205
+ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
475
206
 
476
- | Document | Description |
477
- | --------------------------------- | ------------------------------------------- |
478
- | [CHANGELOG](./CHANGELOG.md) | Version history and release notes |
479
- | [CONTRIBUTING](./CONTRIBUTING.md) | Guide for contributors |
480
- | [SECURITY](./SECURITY.md) | Security policy and vulnerability reporting |
481
- | [CONTRIBUTORS](./CONTRIBUTORS.md) | Project contributors |
207
+ ## 🔐 Security
482
208
 
483
- ## 📝 Author
209
+ If you discover a security vulnerability, please see our [Security Policy](SECURITY.md).
484
210
 
485
- **Salvatore Corvaglia**
211
+ ## 📝 License
212
+
213
+ Distributed under the MIT License. See [LICENSE](LICENSE) for more information.
214
+
215
+ ---
486
216
 
487
- - GitHub: [@salvatorecorvaglia](https://github.com/salvatorecorvaglia)
217
+ **Author**: [Salvatore Corvaglia](https://github.com/salvatorecorvaglia)