@enegalan/request-manager 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Eneko Galan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,608 @@
1
+ # @enegalan/request-manager
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@enegalan/request-manager.svg)](https://www.npmjs.com/package/@enegalan/request-manager)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ RequestManager is a JavaScript library designed to manage and regulate HTTP requests efficiently. It allows you to use HTTP calls from any library (ajax, Ext.Ajax, axios, fetch, etc.) by accepting Promises as parameters.
7
+
8
+ ## Key Features
9
+
10
+ - **Universal Compatibility**: Works with any HTTP library that returns a Promise (fetch, axios, Ext.Ajax, etc.)
11
+ - **Automatic Cancellation**: When a request is repeated with the same identifier, the previous request is automatically cancelled
12
+ - **Prioritizes Recent Requests**: The library ensures that only the most recent request is processed, cancelling older ones
13
+ - **Simple API**: Easy to use and integrate into existing projects
14
+ - **Adapt to your requirements**: The library supports `options` for custom request management
15
+ - **TypeScript Support**: Full TypeScript type definitions included
16
+ - **Multiple Module Formats**: ESM, CommonJS, and UMD builds available
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @enegalan/request-manager
22
+ ```
23
+
24
+ ### Usage in Different Environments
25
+
26
+ **ES Modules (recommended):**
27
+ ```javascript
28
+ import RequestManager from '@enegalan/request-manager';
29
+ ```
30
+
31
+ **CommonJS:**
32
+ ```javascript
33
+ const { RequestManager } = require('@enegalan/request-manager');
34
+ ```
35
+
36
+ **Browser (CDN):**
37
+ ```html
38
+ <!-- Using unpkg -->
39
+ <script src="https://unpkg.com/@enegalan/request-manager/dist/request-manager.min.js"></script>
40
+
41
+ <!-- Or using jsDelivr -->
42
+ <script src="https://cdn.jsdelivr.net/npm/@enegalan/request-manager/dist/request-manager.min.js"></script>
43
+
44
+ <script>
45
+ const requestManager = new RequestManager();
46
+ </script>
47
+ ```
48
+
49
+ ### TypeScript
50
+
51
+ Full TypeScript support is included. Types are automatically resolved:
52
+
53
+ ```typescript
54
+ import RequestManager, { RequestOptions, XhrResponse } from '@enegalan/request-manager';
55
+
56
+ const requestManager = new RequestManager({ verbose: true });
57
+
58
+ // Types are automatically inferred
59
+ const response: Response = await requestManager.fetch('/api/users');
60
+ const xhrResult: XhrResponse<{ name: string }> = await requestManager.xhr('/api/user/1');
61
+ ```
62
+
63
+ ## Usage
64
+
65
+ ### Basic Example with fetch()
66
+
67
+ ```javascript
68
+ import RequestManager from '@enegalan/request-manager';
69
+
70
+ const requestManager = new RequestManager();
71
+
72
+ // Simple GET request - automatically uses cleaned URL as requestKey
73
+ requestManager.fetch('/api/users')
74
+ .then(response => response.json())
75
+ .then(data => console.log(data))
76
+ .catch(error => {
77
+ if (error.message === 'Request was cancelled') {
78
+ console.log('Request was cancelled');
79
+ } else {
80
+ console.error('Request failed:', error);
81
+ }
82
+ });
83
+ ```
84
+
85
+ ### POST Request with Options
86
+
87
+ ```javascript
88
+ import RequestManager from '@enegalan/request-manager';
89
+
90
+ const requestManager = new RequestManager();
91
+
92
+ // POST request with options
93
+ requestManager.fetch('/api/users', {
94
+ method: 'POST',
95
+ headers: { 'Content-Type': 'application/json' },
96
+ body: JSON.stringify({ name: 'John' })
97
+ })
98
+ .then(response => response.json())
99
+ .then(data => console.log(data));
100
+ ```
101
+
102
+ ### Using request() with Promise
103
+
104
+ ```javascript
105
+ import RequestManager from '@enegalan/request-manager';
106
+
107
+ const requestManager = new RequestManager();
108
+
109
+ // Using request() with a Promise
110
+ // The URL is used to generate the request ID (cleaned URL)
111
+ requestManager.request('/api/users', fetch('/api/users'))
112
+ .then(response => response.json())
113
+ .then(data => console.log(data));
114
+ ```
115
+
116
+ ### Using request() with Function
117
+
118
+ ```javascript
119
+ import RequestManager from '@enegalan/request-manager';
120
+
121
+ const requestManager = new RequestManager();
122
+
123
+ // Using request() with a Function - allows custom logic
124
+ // The function receives { options } where options contains the signal
125
+ requestManager.request('/api/users', ({ options }) => {
126
+ return fetch('/api/users', options);
127
+ })
128
+ .then(response => response.json())
129
+ .then(data => console.log(data));
130
+ ```
131
+
132
+ ### Automatic Cancellation with Same URL
133
+
134
+ ```javascript
135
+ import RequestManager from '@enegalan/request-manager';
136
+
137
+ const requestManager = new RequestManager();
138
+
139
+ // By default, requests with the same URL (cleaned) will cancel previous ones
140
+ // The URL is automatically cleaned (protocol and query params removed) to generate the request ID
141
+ requestManager.fetch('/api/search?q=test')
142
+ .catch(error => {
143
+ console.log('First request cancelled:', error.message);
144
+ });
145
+
146
+ // This second request will automatically cancel the first one
147
+ // because they share the same cleaned URL
148
+ setTimeout(() => {
149
+ requestManager.fetch('/api/search?q=updated')
150
+ .then(response => response.json())
151
+ .then(data => console.log('Second request completed:', data));
152
+ }, 100);
153
+ ```
154
+
155
+ ### Using requestKey to Override URL-based ID
156
+
157
+ ```javascript
158
+ import RequestManager from '@enegalan/request-manager';
159
+
160
+ const requestManager = new RequestManager();
161
+
162
+ // You can use requestKey to override the default URL-based ID generation
163
+ requestManager.fetch('/api/search?q=test', {
164
+ requestKey: 'search-users' // Custom key instead of cleaned URL
165
+ })
166
+ .catch(error => {
167
+ console.log('First request cancelled:', error.message);
168
+ });
169
+
170
+ // This second request will cancel the first one because they share the same requestKey
171
+ setTimeout(() => {
172
+ requestManager.fetch('/api/search?q=updated', {
173
+ requestKey: 'search-users' // Same key = same request ID = cancellation
174
+ })
175
+ .then(response => response.json())
176
+ .then(data => console.log('Second request completed:', data));
177
+ }, 100);
178
+ ```
179
+
180
+ ### Using requestKey with Function
181
+
182
+ ```javascript
183
+ import RequestManager from '@enegalan/request-manager';
184
+
185
+ const requestManager = new RequestManager();
186
+
187
+ // You can use a function to generate the requestKey dynamically
188
+ function searchUsers(query) {
189
+ return requestManager.fetch(`/api/search?q=${query}`, {
190
+ requestKey: () => `search-${query}` // Function that returns the key
191
+ });
192
+ }
193
+
194
+ // Both calls will share the same requestKey and cancel each other
195
+ searchUsers('test');
196
+ searchUsers('test'); // This will cancel the previous one
197
+ ```
198
+
199
+ ### Using noCancel to Allow Concurrent Requests
200
+
201
+ ```javascript
202
+ import RequestManager from '@enegalan/request-manager';
203
+
204
+ const requestManager = new RequestManager();
205
+
206
+ // Use noCancel: true to allow multiple requests to execute concurrently
207
+ // This is useful for lazy loading scenarios where you want all requests to complete
208
+ requestManager.fetch('/api/lazy?load=1', { noCancel: true })
209
+ .then(response => response.json())
210
+ .then(data => console.log('Load 1:', data));
211
+
212
+ requestManager.fetch('/api/lazy?load=2', { noCancel: true })
213
+ .then(response => response.json())
214
+ .then(data => console.log('Load 2:', data));
215
+
216
+ requestManager.fetch('/api/lazy?load=3', { noCancel: true })
217
+ .then(response => response.json())
218
+ .then(data => console.log('Load 3:', data));
219
+
220
+ // All three requests will execute concurrently without canceling each other
221
+ // Even though they share the same cleaned URL (without query params)
222
+ ```
223
+
224
+ ### Using with Axios
225
+
226
+ ```javascript
227
+ import axios from 'axios';
228
+ import RequestManager from '@enegalan/request-manager';
229
+
230
+ const requestManager = new RequestManager();
231
+
232
+ // Using axios with request() method
233
+ // The URL is used to generate the request ID
234
+ const CancelToken = axios.CancelToken;
235
+ const source = CancelToken.source();
236
+
237
+ requestManager.request('/api/users', axios.get('/api/users', {
238
+ cancelToken: source.token
239
+ }), {
240
+ cancelToken: () => source.cancel()
241
+ })
242
+ .then(response => console.log(response.data))
243
+ .catch(error => {
244
+ if (axios.isCancel(error)) {
245
+ console.log('Request was cancelled');
246
+ } else {
247
+ console.error('Request failed:', error);
248
+ }
249
+ });
250
+ ```
251
+
252
+ ### Using with Axios and Function
253
+
254
+ ```javascript
255
+ import axios from 'axios';
256
+ import RequestManager from '@enegalan/request-manager';
257
+
258
+ const requestManager = new RequestManager();
259
+
260
+ // Using axios with Function - The function receives { options } where options contains the signal
261
+ requestManager.request('/api/users', ({ options }) => {
262
+ const CancelToken = axios.CancelToken;
263
+ const source = CancelToken.source();
264
+ return axios.get('/api/users', { cancelToken: source.token });
265
+ })
266
+ .then(response => console.log(response.data))
267
+ .catch(error => {
268
+ if (axios.isCancel(error)) {
269
+ console.log('Request was cancelled');
270
+ } else {
271
+ console.error('Request failed:', error);
272
+ }
273
+ });
274
+ ```
275
+
276
+ ### Using with Other Libraries
277
+
278
+ ```javascript
279
+ import RequestManager from '@enegalan/request-manager';
280
+
281
+ const requestManager = new RequestManager();
282
+
283
+ // Example with a custom HTTP library using Function
284
+ // The function receives { options } where options contains the signal
285
+ requestManager.request('/api/data', ({ options }) => {
286
+ return new Promise((resolve, reject) => {
287
+ const xhr = new XMLHttpRequest();
288
+ xhr.open('GET', '/api/data');
289
+ xhr.onload = () => resolve(xhr.responseText);
290
+ xhr.onerror = () => reject(new Error('Request failed'));
291
+ xhr.send();
292
+
293
+ // Use signal to cancel if needed
294
+ options.signal.addEventListener('abort', () => {
295
+ xhr.abort();
296
+ reject(new Error('Request was cancelled'));
297
+ });
298
+ });
299
+ })
300
+ .then(data => console.log(data))
301
+ .catch(error => console.error(error));
302
+ ```
303
+
304
+ ### Using with Pre-created Promises
305
+
306
+ ```javascript
307
+ import RequestManager from '@enegalan/request-manager';
308
+
309
+ const requestManager = new RequestManager();
310
+
311
+ // If you already have a Promise, you can pass it directly
312
+ const existingPromise = fetch('/api/data');
313
+
314
+ requestManager.request('/api/data', existingPromise, {
315
+ // You can still provide cancelToken if your library supports it
316
+ cancelToken: () => {
317
+ // Custom cancellation logic
318
+ }
319
+ })
320
+ .then(response => response.json())
321
+ .then(data => console.log(data));
322
+ ```
323
+
324
+ ## API Reference
325
+
326
+ ### `new RequestManager(options)`
327
+
328
+ Creates a new RequestManager instance.
329
+
330
+ **Parameters:**
331
+ - `options` (Object, optional): Configuration options
332
+ - `verbose` (boolean, optional): If true, cancellation errors will include messages globally for all requests.
333
+
334
+ **Example:**
335
+ ```javascript
336
+ // Create with verbose mode enabled
337
+ const requestManager = new RequestManager({ verbose: true });
338
+ ```
339
+
340
+ ### `request(url, requestPromise, options)`
341
+
342
+ Executes an HTTP request, cancelling any previous request with the same identifier.
343
+
344
+ **Parameters:**
345
+ - `url` (string): The URL of the request (used to generate request ID from cleaned URL)
346
+ - `requestPromise` (Promise|Function|string): The Promise returned by any HTTP library (fetch, axios, etc.), a Function that receives `{ options }` and returns a Promise, or a URL string (which will be used with fetch internally)
347
+ - `options` (Object, optional): Configuration options
348
+ - `abortController` (AbortController): AbortController instance (created automatically if not provided)
349
+ - `cancelToken` (Function|Object): Cancel token or cancel function for other libraries
350
+ - `requestKey` (string|number|Function, optional): Key to identify duplicate requests. If provided, requests with the same key will share the same ID and cancel previous ones. If not provided, the cleaned URL is used as the key. Can be a string, number, or function that returns a key.
351
+ - `noCancel` (boolean): If true, this request will not cancel previous requests with the same ID, allowing concurrent requests. Useful for lazy loading scenarios where multiple requests should execute in parallel.
352
+
353
+ **Returns:** Promise that resolves/rejects based on the most recent request
354
+
355
+ **Note:** The request ID is automatically generated from the cleaned URL (protocol and query params removed) unless `requestKey` is specified. When `noCancel` is true, a unique ID is generated for each request to prevent cancellation. When `requestPromise` is a Function, it receives `{ options }` where `options` contains the `signal` (AbortSignal) and any other fetch options.
356
+
357
+ ### `fetch(url, options)`
358
+
359
+ Executes an HTTP request using fetch, cancelling any previous request with the same identifier.
360
+
361
+ **Parameters:**
362
+ - `url` (string): The URL to fetch
363
+ - `options` (Object, optional): Configuration options (same as `request()` method)
364
+ - `requestKey` (string|number|Function, optional): Key to identify duplicate requests. If not provided, the cleaned URL is used as the key.
365
+ - `abortController` (AbortController): AbortController instance (created automatically if not provided)
366
+ - `cancelToken` (Function|Object): Cancel token or cancel function for other libraries
367
+ - `noCancel` (boolean): If true, this request will not cancel previous requests with the same ID, allowing concurrent requests
368
+ - Any other properties are passed as fetch options (method, headers, body, etc.)
369
+
370
+ **Returns:** Promise that resolves/rejects based on the most recent request
371
+
372
+ **Note:** This is a convenience method that internally calls `request()` with the URL as the requestPromise. The request ID is automatically generated from the cleaned URL unless `requestKey` is specified. When `noCancel` is true, a unique ID is generated for each request.
373
+
374
+ ### `axios(url, options, axiosInstance)`
375
+
376
+ Executes an HTTP request using axios, cancelling any previous request with the same identifier.
377
+
378
+ **Parameters:**
379
+ - `url` (string): The URL to request
380
+ - `options` (Object, optional): Configuration options
381
+ - `requestKey` (string|number|Function, optional): Key to identify duplicate requests. If provided, requests with the same key will cancel previous ones. Can be a string, number, or function that returns a key.
382
+ - `noCancel` (boolean): If true, this request will not cancel previous requests with the same ID, allowing concurrent requests
383
+ - Any other properties are passed as axios options (method, headers, params, data, etc.)
384
+ - `axiosInstance` (Object, optional): Custom axios instance to use. If not provided, uses the global `axios` object.
385
+
386
+ **Returns:** Promise that resolves/rejects based on the most recent request
387
+
388
+ **Note:** This method automatically creates a CancelToken for axios cancellation. The request ID is automatically generated from the cleaned URL unless `requestKey` is specified. When `noCancel` is true, a unique ID is generated for each request.
389
+
390
+ **Example:**
391
+ ```javascript
392
+ import axios from 'axios';
393
+ import RequestManager from '@enegalan/request-manager';
394
+
395
+ const requestManager = new RequestManager();
396
+
397
+ // Simple GET request (uses global axios)
398
+ requestManager.axios('/api/users')
399
+ .then(response => console.log(response.data))
400
+ .catch(error => console.error(error));
401
+
402
+ // With custom axios instance
403
+ const apiClient = axios.create({
404
+ baseURL: 'https://api.example.com',
405
+ timeout: 5000
406
+ });
407
+
408
+ requestManager.axios('/users', {}, apiClient)
409
+ .then(response => console.log(response.data));
410
+
411
+ // POST request with options
412
+ requestManager.axios('/api/users', {
413
+ method: 'POST',
414
+ data: { name: 'John' },
415
+ headers: { 'Content-Type': 'application/json' }
416
+ })
417
+ .then(response => console.log(response.data));
418
+ ```
419
+
420
+ ### `ajax(ajaxMethod, url, options)`
421
+
422
+ Executes an HTTP request using a custom ajax method function, cancelling any previous request with the same identifier.
423
+
424
+ **Parameters:**
425
+ - `ajaxMethod` (Function): A function that receives `{ url, ...options }` and returns a Promise. The function should accept an object with `url` and other options including the `signal` (AbortSignal).
426
+ - `url` (string): The URL to request
427
+ - `options` (Object, optional): Configuration options
428
+ - `requestKey` (string|number|Function, optional): Key to identify duplicate requests. If provided, requests with the same key will cancel previous ones. Can be a string, number, or function that returns a key.
429
+ - `abortController` (AbortController): AbortController instance (created automatically if not provided)
430
+ - `cancelToken` (Function|Object): Cancel token or cancel function for other libraries
431
+ - `verbose` (boolean): If true, cancellation errors will include messages
432
+ - `noCancel` (boolean): If true, this request will not cancel previous requests with the same ID, allowing concurrent requests
433
+ - Any other properties are passed to the ajax method function
434
+
435
+ **Returns:** Promise that resolves/rejects based on the most recent request
436
+
437
+ **Example:**
438
+ ```javascript
439
+ import RequestManager from '@enegalan/request-manager';
440
+
441
+ const requestManager = new RequestManager();
442
+
443
+ // Using with jQuery.ajax
444
+ requestManager.ajax(
445
+ ({ url, ...options }) => {
446
+ return new Promise((resolve, reject) => {
447
+ $.ajax({
448
+ url: url,
449
+ ...options,
450
+ success: resolve,
451
+ error: reject
452
+ });
453
+ });
454
+ },
455
+ '/api/users',
456
+ {
457
+ method: 'GET',
458
+ headers: { 'Content-Type': 'application/json' }
459
+ }
460
+ )
461
+ .then(data => console.log(data))
462
+ .catch(error => console.error(error));
463
+ ```
464
+
465
+ ### `xhr(url, options)`
466
+
467
+ Executes an HTTP request using XMLHttpRequest, cancelling any previous request with the same identifier.
468
+
469
+ **Parameters:**
470
+ - `url` (string): The URL to request
471
+ - `options` (Object, optional): Configuration options
472
+ - `method` (string): HTTP method (GET, POST, PUT, DELETE, etc.). Defaults to 'GET'.
473
+ - `headers` (Object): Headers object to set on the request
474
+ - `body` (string|FormData|Blob|ArrayBuffer): Request body
475
+ - `responseType` (string): Response type ('text', 'json', 'blob', 'arraybuffer', 'document'). Defaults to 'text'.
476
+ - `withCredentials` (boolean): Whether to send credentials with the request
477
+ - `timeout` (number): Request timeout in milliseconds
478
+ - `requestKey` (string|number|Function, optional): Key to identify duplicate requests. If provided, requests with the same key will cancel previous ones. Can be a string, number, or function that returns a key.
479
+ - `abortController` (AbortController): AbortController instance (created automatically if not provided)
480
+ - `verbose` (boolean): If true, cancellation errors will include messages
481
+ - `noCancel` (boolean): If true, this request will not cancel previous requests with the same ID, allowing concurrent requests
482
+
483
+ **Returns:** Promise that resolves/rejects based on the most recent request. The resolved value is an object with:
484
+ - `data`: The response data (automatically parsed as JSON if Content-Type is application/json)
485
+ - `status`: HTTP status code
486
+ - `statusText`: HTTP status text
487
+ - `headers`: Response headers string
488
+ - `xhr`: The XMLHttpRequest instance
489
+
490
+ **Example:**
491
+ ```javascript
492
+ import RequestManager from '@enegalan/request-manager';
493
+
494
+ const requestManager = new RequestManager();
495
+
496
+ // Simple GET request
497
+ requestManager.xhr('/api/users')
498
+ .then(response => console.log(response.data))
499
+ .catch(error => console.error(error));
500
+
501
+ // POST request with options
502
+ requestManager.xhr('/api/users', {
503
+ method: 'POST',
504
+ headers: { 'Content-Type': 'application/json' },
505
+ body: JSON.stringify({ name: 'John' }),
506
+ responseType: 'json'
507
+ })
508
+ .then(response => console.log(response.data));
509
+ ```
510
+
511
+ ### `cancel(requestId)`
512
+
513
+ Cancels a specific request by its identifier.
514
+
515
+ **Parameters:**
516
+ - `requestId` (string): The unique identifier of the request to cancel
517
+
518
+ **Returns:** `true` if the request was found and cancelled, `false` otherwise
519
+
520
+ ### `cancelAll()`
521
+
522
+ Cancels all active requests.
523
+
524
+ **Returns:** The number of requests that were cancelled
525
+
526
+ ### `isActive(requestId)`
527
+
528
+ Checks if a request with the given identifier is currently active.
529
+
530
+ **Parameters:**
531
+ - `requestId` (string): The unique identifier to check
532
+
533
+ **Returns:** `true` if the request is active, `false` otherwise
534
+
535
+ ### `getActiveCount()`
536
+
537
+ Gets the number of active requests.
538
+
539
+ **Returns:** The number of currently active requests
540
+
541
+ ### `clear()`
542
+
543
+ Clears all active requests without cancelling them. Use with caution - this will not cancel the underlying HTTP requests.
544
+
545
+ ### `getSignal()`
546
+
547
+ Gets the AbortSignal from the current AbortController. Creates a new AbortController if one doesn't exist or if the current one is aborted.
548
+
549
+ **Returns:** AbortSignal from the current AbortController
550
+
551
+ **Example:**
552
+ ```javascript
553
+ const signal = requestManager.getSignal();
554
+ requestManager.request('/api/users', fetch('/api/users', { signal }));
555
+ ```
556
+
557
+ ### `getAbortController()`
558
+
559
+ Gets the current AbortController instance. Creates a new AbortController if one doesn't exist or if the current one is aborted.
560
+
561
+ **Returns:** AbortController instance
562
+
563
+ **Example:**
564
+ ```javascript
565
+ const abortController = requestManager.getAbortController();
566
+ requestManager.request('/api/users', fetch('/api/users', { signal: abortController.signal }));
567
+ ```
568
+
569
+ ### `getOptions()`
570
+
571
+ Gets the manager options that were passed to the constructor or set via `setOptions`.
572
+
573
+ **Returns:** Object containing the manager options
574
+
575
+ ### `setOptions(options)`
576
+
577
+ Sets the manager options.
578
+
579
+ **Parameters:**
580
+ - `options` (Object): Configuration options
581
+ - `verbose` (boolean, optional): If true, cancellation errors will include messages
582
+
583
+ **Example:**
584
+ ```javascript
585
+ const requestManager = new RequestManager();
586
+
587
+ // Enable verbose mode at runtime
588
+ requestManager.setOptions({ verbose: true });
589
+
590
+ // Disable verbose mode
591
+ requestManager.setOptions({ verbose: false });
592
+ ```
593
+
594
+ ### `addAbortListener(abortMethod, signal)`
595
+
596
+ Links an abort signal with an HTTP client abort method. Useful for custom HTTP clients that only support the abort method to cancel requests.
597
+
598
+ **Parameters:**
599
+ - `abortMethod` (Function): The abort method to call when the signal is aborted
600
+ - `signal` (AbortSignal): The signal to listen to
601
+
602
+ **Example:**
603
+ ```javascript
604
+ const abortController = new AbortController();
605
+ const req = $.ajax({ url });
606
+ requestManager.addAbortListener(req.abort, abortController.signal);
607
+ requestManager.request(url, req, { abortController: abortController });
608
+ ```