@enegalan/request-manager 1.1.0 → 1.1.2

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.
@@ -1,597 +1,720 @@
1
1
  var RequestManager = (function () {
2
- 'use strict';
2
+ 'use strict';
3
+
4
+ function _arrayLikeToArray(r, a) {
5
+ (null == a || a > r.length) && (a = r.length);
6
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
7
+ return n;
8
+ }
9
+ function _arrayWithHoles(r) {
10
+ if (Array.isArray(r)) return r;
11
+ }
12
+ function _assertClassBrand(e, t, n) {
13
+ if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
14
+ throw new TypeError("Private element is not present on this object");
15
+ }
16
+ function _checkPrivateRedeclaration(e, t) {
17
+ if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object");
18
+ }
19
+ function _classPrivateMethodInitSpec(e, a) {
20
+ _checkPrivateRedeclaration(e, a), a.add(e);
21
+ }
22
+ function _defineProperty(e, r, t) {
23
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
24
+ value: t,
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true
28
+ }) : e[r] = t, e;
29
+ }
30
+ function _iterableToArrayLimit(r, l) {
31
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
32
+ if (null != t) {
33
+ var e,
34
+ n,
35
+ i,
36
+ u,
37
+ a = [],
38
+ f = true,
39
+ o = false;
40
+ try {
41
+ if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
42
+ } catch (r) {
43
+ o = true, n = r;
44
+ } finally {
45
+ try {
46
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
47
+ } finally {
48
+ if (o) throw n;
49
+ }
50
+ }
51
+ return a;
52
+ }
53
+ }
54
+ function _nonIterableRest() {
55
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
56
+ }
57
+ function ownKeys(e, r) {
58
+ var t = Object.keys(e);
59
+ if (Object.getOwnPropertySymbols) {
60
+ var o = Object.getOwnPropertySymbols(e);
61
+ r && (o = o.filter(function (r) {
62
+ return Object.getOwnPropertyDescriptor(e, r).enumerable;
63
+ })), t.push.apply(t, o);
64
+ }
65
+ return t;
66
+ }
67
+ function _objectSpread2(e) {
68
+ for (var r = 1; r < arguments.length; r++) {
69
+ var t = null != arguments[r] ? arguments[r] : {};
70
+ r % 2 ? ownKeys(Object(t), true).forEach(function (r) {
71
+ _defineProperty(e, r, t[r]);
72
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
73
+ Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
74
+ });
75
+ }
76
+ return e;
77
+ }
78
+ function _slicedToArray(r, e) {
79
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
80
+ }
81
+ function _toPrimitive(t, r) {
82
+ if ("object" != typeof t || !t) return t;
83
+ var e = t[Symbol.toPrimitive];
84
+ if (void 0 !== e) {
85
+ var i = e.call(t, r);
86
+ if ("object" != typeof i) return i;
87
+ throw new TypeError("@@toPrimitive must return a primitive value.");
88
+ }
89
+ return ("string" === r ? String : Number)(t);
90
+ }
91
+ function _toPropertyKey(t) {
92
+ var i = _toPrimitive(t, "string");
93
+ return "symbol" == typeof i ? i : i + "";
94
+ }
95
+ function _unsupportedIterableToArray(r, a) {
96
+ if (r) {
97
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
98
+ var t = {}.toString.call(r).slice(8, -1);
99
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
100
+ }
101
+ }
102
+
103
+ var _RequestManager_brand = /*#__PURE__*/new WeakSet();
104
+ /**
105
+ * RequestManager - A library for managing and regulating HTTP requests efficiently.
106
+ * @license MIT
107
+ * @author Eneko Galan <enekogalanelorza@gmail.com>
108
+ * This library allows you to manage HTTP requests from any library (ajax, Ext.Ajax, axios, fetch, etc.)
109
+ * by accepting Promises as parameters. When a request is repeated with the same identifier,
110
+ * the previous request is automatically cancelled and the new one is executed, giving priority to the most recent requests.
111
+ */
112
+ class RequestManager {
113
+ constructor() {
114
+ var _options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
115
+ /**
116
+ * Resolves the AbortController for a request: explicit option, pending handoff, or new.
117
+ * Clears the pending handoff so concurrent requests do not share it.
118
+ * @param {AbortController|undefined} provided - Optional AbortController from options
119
+ * @returns {AbortController}
120
+ * @private
121
+ */
122
+ _classPrivateMethodInitSpec(this, _RequestManager_brand);
123
+ /**
124
+ * @type {Map<string, import('./index.d.ts').ActiveRequest>}
125
+ */
126
+ this.activeRequests = new Map();
127
+ /**
128
+ * @type {import('./index.d.ts').Options}
129
+ */
130
+ this.options = _options;
131
+ /**
132
+ * One-shot AbortController for getSignal()/getAbortController() handoff.
133
+ * Consumed by the next request that does not pass options.abortController.
134
+ * @type {AbortController|null}
135
+ */
136
+ this.abortController = null;
137
+ }
3
138
 
4
139
  /**
5
- * RequestManager - A library for managing and regulating HTTP requests efficiently.
6
- * @license MIT
7
- * @author Eneko Galan <enekogalanelorza@gmail.com>
8
- * This library allows you to manage HTTP requests from any library (ajax, Ext.Ajax, axios, fetch, etc.)
9
- * by accepting Promises as parameters. When a request is repeated with the same identifier,
10
- * the previous request is automatically cancelled and the new one is executed, giving priority to the most recent requests.
140
+ * Gets the manager options
141
+ * @returns {import('./index.d.ts').Options} Manager options
11
142
  */
12
- class RequestManager {
13
- constructor(options = {}) {
14
- /**
15
- * @type {Map<string, import('./index.d.ts').ActiveRequest>}
16
- */
17
- this.activeRequests = new Map();
18
- /**
19
- * @type {import('./index.d.ts').Options}
20
- */
21
- this.options = options;
22
- /**
23
- * One-shot AbortController for getSignal()/getAbortController() handoff.
24
- * Consumed by the next request that does not pass options.abortController.
25
- * @type {AbortController|null}
26
- */
27
- this.abortController = null;
28
- }
29
-
30
- /**
31
- * Gets the manager options
32
- * @returns {import('./index.d.ts').Options} Manager options
33
- */
34
- getOptions() {
35
- return this.options;
36
- }
37
-
38
- /**
39
- * Sets the manager options
40
- * @param {import('./index.d.ts').Options} options - The options to set
41
- */
42
- setOptions(options) {
43
- this.options = options;
44
- }
45
-
46
- /**
47
- * Creates a new AbortController and returns its signal for the next request()
48
- * (one getSignal → one request). Do not use for parallel requests; use fetch(),
49
- * axios(), or request(url, ({ options }) => ...) instead — they create their own signal.
50
- * @returns {AbortSignal} The signal from a new AbortController
51
- * @example
52
- * const signal = requestManager.getSignal();
53
- * requestManager.request('/api/users', fetch('/api/users', { signal }));
54
- */
55
- getSignal() {
56
- return this.getAbortController().signal;
57
- }
58
-
59
- /**
60
- * Creates a new AbortController for the next request handoff.
61
- * Always returns a fresh controller (never reuses one from another in-flight request).
62
- * @returns {AbortController} A new AbortController instance
63
- */
64
- getAbortController() {
65
- this.abortController = new AbortController();
66
- return this.abortController;
67
- }
68
-
69
- /**
70
- * Checks if a request with the given identifier is currently active.
71
- * @param {string} requestId - The unique identifier to check
72
- * @returns {boolean} True if the request is active, false otherwise
73
- */
74
- isActive(requestId) {
75
- return this.activeRequests.has(requestId);
76
- }
77
-
78
- /**
79
- * Gets the number of active requests.
80
- * @returns {number} The number of currently active requests
81
- */
82
- getActiveCount() {
83
- return this.activeRequests.size;
84
- }
85
-
86
- /**
87
- * Clears all active requests without cancelling them.
88
- * Use with caution - this will not cancel the underlying HTTP requests.
89
- */
90
- clear() {
91
- this.activeRequests.clear();
92
- }
143
+ getOptions() {
144
+ return this.options;
145
+ }
93
146
 
94
- /**
95
- * Executes an HTTP request, cancelling any previous request with the same identifier.
96
- * @param {string} url - The URL to request
97
- * @param {Promise|import('./index.d.ts').RequestFunction|string} requestPromise - The request promise or function that returns a promise
98
- * @param {import('./index.d.ts').RequestOptions} options - Optional configuration
99
- * @returns {Promise} A Promise that resolves/rejects based on the most recent request
100
- * @example
101
- * // Request with Promise
102
- * requestManager.request('/api/users', axios.get('/api/users'));
103
- * @example
104
- * // Request with Function
105
- * requestManager.request('/api/users', ({ options }) => fetch('/api/users', { signal: options.signal, ...options }));
106
- * @example
107
- * // Request with Promise and custom cancellation grouping with requestKey
108
- * const options = {
109
- * requestKey: 'get-users'
110
- * }
111
- * requestManager.request('/api/users', axios.get('/api/users', options), options);
112
- */
113
- request(url, requestPromise, options = {}) {
114
- return this.#_request(this.getRequestId(url, options), requestPromise, options);
115
- }
147
+ /**
148
+ * Sets the manager options
149
+ * @param {import('./index.d.ts').Options} options - The options to set
150
+ */
151
+ setOptions(options) {
152
+ this.options = options;
153
+ }
116
154
 
117
- /**
118
- * Executes an HTTP request using fetch, cancelling any previous request with the same identifier.
119
- * @param {string} url - The URL to fetch
120
- * @param {import('./index.d.ts').FetchOptions} options - Optional configuration
121
- * @returns {Promise} A Promise that resolves/rejects based on the most recent request
122
- * @example
123
- * // Simple GET request
124
- * requestManager.fetch('/api/users');
125
- * @example
126
- * // POST request with options
127
- * requestManager.fetch('/api/users', {
128
- * method: 'POST',
129
- * headers: { 'Content-Type': 'application/json' },
130
- * body: JSON.stringify({ name: 'John' })
131
- * });
132
- * @example
133
- * // Request with requestKey for custom cancellation grouping with requestKey
134
- * requestManager.fetch('/api/users', {
135
- * requestKey: 'get-users'
136
- * });
137
- */
138
- fetch(url, options = {}) {
139
- return this.#_request(this.getRequestId(url, options), url, options);
140
- }
155
+ /**
156
+ * Creates a new AbortController and returns its signal for the next request()
157
+ * (one getSignal one request). Do not use for parallel requests; use fetch(),
158
+ * axios(), or request(url, ({ options }) => ...) instead — they create their own signal.
159
+ * @returns {AbortSignal} The signal from a new AbortController
160
+ * @example
161
+ * const signal = requestManager.getSignal();
162
+ * requestManager.request('/api/users', fetch('/api/users', { signal }));
163
+ */
164
+ getSignal() {
165
+ return this.getAbortController().signal;
166
+ }
141
167
 
142
- /**
143
- * Executes an HTTP request using axios, cancelling any previous request with the same identifier.
144
- * @param {string} url - The URL to request
145
- * @param {import('./index.d.ts').AxiosRequestOptions} options - Optional configuration
146
- * @param {import('./index.d.ts').AxiosInstance|import('./index.d.ts').AxiosStatic|null} axiosInstance - Optional axios instance to use. If not provided, uses global axios.
147
- * @returns {Promise} A Promise that resolves/rejects based on the most recent request
148
- * @example
149
- * // Simple GET request (uses global axios)
150
- * requestManager.axios('/api/users');
151
- * @example
152
- * // With custom axios instance
153
- * const myAxios = axios.create({ baseURL: 'https://api.example.com' });
154
- * requestManager.axios('/users', {}, myAxios);
155
- * @example
156
- * // POST request with options
157
- * requestManager.axios('/api/users', {
158
- * method: 'POST',
159
- * headers: { 'Content-Type': 'application/json' },
160
- * body: JSON.stringify({ name: 'John' })
161
- * });
162
- * @example
163
- * // Request with requestKey for custom cancellation grouping with requestKey
164
- * requestManager.axios('/api/users', {
165
- * requestKey: 'get-users'
166
- * });
167
- */
168
- axios(url, options = {}, axiosInstance = null) {
169
- const axiosLib = axiosInstance || axios;
170
- return this.#_request(
171
- this.getRequestId(url, options),
172
- ({ options: requestOptions }) => axiosLib({ url, ...requestOptions }),
173
- options
174
- );
175
- }
168
+ /**
169
+ * Creates a new AbortController for the next request handoff.
170
+ * Always returns a fresh controller (never reuses one from another in-flight request).
171
+ * @returns {AbortController} A new AbortController instance
172
+ */
173
+ getAbortController() {
174
+ this.abortController = new AbortController();
175
+ return this.abortController;
176
+ }
176
177
 
177
- /**
178
- * Executes an HTTP request using jQuery.ajax, cancelling any previous request with the same identifier.
179
- * @param {import('./index.d.ts').AjaxFunction} ajaxFunction - A function that receives { url, ...options } and returns a Promise
180
- * @param {string} url - The URL to request
181
- * @param {import('./index.d.ts').BaseRequestOptions & Record<string, any>} options - Optional configuration
182
- * @returns {Promise} A Promise that resolves/rejects based on the most recent request
183
- * @example
184
- * // Simple GET request
185
- * requestManager.ajax(ajaxFunction, '/api/users');
186
- * @example
187
- * // POST request with options
188
- * requestManager.ajax(ajaxFunction, '/api/users', {
189
- * method: 'POST',
190
- * headers: { 'Content-Type': 'application/json' },
191
- * body: JSON.stringify({ name: 'John' })
192
- * });
193
- * @example
194
- * // Request with requestKey for custom cancellation grouping with requestKey
195
- * requestManager.ajax(ajaxFunction, '/api/users', {
196
- * requestKey: 'get-users'
197
- * });
198
- */
199
- ajax(ajaxFunction, url, options = {}) {
200
- if (typeof ajaxFunction !== 'function') throw new Error('ajaxFunction parameter must be a function');
201
- return this.#_request(
202
- this.getRequestId(url, options),
203
- ({ options: requestOptions }) => ajaxFunction({ url, ...requestOptions }),
204
- options
205
- );
206
- }
178
+ /**
179
+ * Checks if a request with the given identifier is currently active.
180
+ * @param {string} requestId - The unique identifier to check
181
+ * @returns {boolean} True if the request is active, false otherwise
182
+ */
183
+ isActive(requestId) {
184
+ return this.activeRequests.has(requestId);
185
+ }
207
186
 
208
- /**
209
- * Executes an HTTP request using XMLHttpRequest, cancelling any previous request with the same identifier.
210
- * @param {string} url - The URL to request
211
- * @param {import('./index.d.ts').XhrOptions} options - Optional configuration
212
- * @returns {Promise<import('./index.d.ts').XhrResponse>} A Promise that resolves/rejects based on the most recent request
213
- * @example
214
- * // Simple GET request
215
- * requestManager.xhr('/api/users');
216
- * @example
217
- * // POST request with options
218
- * requestManager.xhr('/api/users', {
219
- * method: 'POST',
220
- * headers: { 'Content-Type': 'application/json' },
221
- * body: JSON.stringify({ name: 'John' })
222
- * });
223
- * @example
224
- * // Request with requestKey for custom cancellation grouping
225
- * requestManager.xhr('/api/users', {
226
- * requestKey: 'get-users'
227
- * });
228
- */
229
- xhr(url, options = {}) {
230
- const requestId = this.getRequestId(url, options);
231
- /** @type {import('./index.d.ts').RequestFunction<import('./index.d.ts').XhrResponse>} */
232
- const xhrFunction = ({ options: fetchOptions }) => {
233
- // Create XMLHttpRequest
234
- const xhr = new XMLHttpRequest();
235
- const method = (options.method || 'GET').toUpperCase();
236
- // Create a promise that wraps the XHR request
237
- const xhrPromise = new Promise((resolve, reject) => {
238
- xhr.onload = function () {
239
- if (xhr.status >= 200 && xhr.status < 300) {
240
- let response = xhr.response;
241
- if (
242
- options.responseType === 'json' ||
243
- ((!options.responseType || options.responseType === 'text') &&
244
- xhr.getResponseHeader('Content-Type')?.includes('application/json') &&
245
- typeof response === 'string')
246
- ) {
247
- try {
248
- response = JSON.parse(response);
249
- } catch {}
250
- }
251
- resolve({
252
- data: response,
253
- status: xhr.status,
254
- statusText: xhr.statusText,
255
- headers: xhr.getAllResponseHeaders(),
256
- xhr: xhr,
257
- });
258
- } else {
259
- reject({
260
- message: `Request failed with status ${xhr.status}`,
261
- status: xhr.status,
262
- statusText: xhr.statusText,
263
- xhr: xhr,
264
- });
265
- }
266
- };
267
- xhr.onerror = function () {
268
- reject({
269
- message: 'Network error',
270
- xhr: xhr,
271
- });
272
- };
273
- xhr.ontimeout = function () {
274
- reject({
275
- message: 'Request timeout',
276
- xhr: xhr,
277
- });
278
- };
279
-
280
- // Open the request
281
- xhr.open(method, url, true);
282
-
283
- // Set response type
284
- if (options.responseType) xhr.responseType = options.responseType;
285
- // Set withCredentials
286
- if (options.withCredentials !== undefined) xhr.withCredentials = options.withCredentials;
287
- // Set timeout
288
- if (options.timeout !== undefined) xhr.timeout = options.timeout;
289
- // Set headers
290
- if (options.headers)
291
- Object.keys(options.headers).forEach((key) => {
292
- xhr.setRequestHeader(key, options.headers[key]);
293
- });
294
-
295
- // Connect abort signal to xhr.abort()
296
- if (fetchOptions.signal) fetchOptions.signal.addEventListener('abort', () => xhr.abort());
297
-
298
- // Send the request
299
- xhr.send(options.body || null);
300
- });
301
- return xhrPromise;
302
- };
303
- return this.#_request(requestId, xhrFunction, options);
304
- }
187
+ /**
188
+ * Gets the number of active requests.
189
+ * @returns {number} The number of currently active requests
190
+ */
191
+ getActiveCount() {
192
+ return this.activeRequests.size;
193
+ }
305
194
 
306
- /**
307
- * Returns the request identifier for a URL and options.
308
- * @param {string} url - The URL used when starting the request
309
- * @param {import('./index.d.ts').BaseRequestOptions} options - Same options used for the request
310
- * @returns {string} The request identifier
311
- * @example
312
- * requestManager.fetch('/api/users');
313
- * const id = requestManager.getRequestId('/api/users');
314
- * requestManager.cancel(id);
315
- */
316
- getRequestId(url, options = {}) {
317
- let requestKey = options.requestKey;
318
-
319
- const prefix = 'request_';
320
-
321
- // Generate a unique identifier to prevent cancellation for non cancelable requests
322
- if (options.noCancel) {
323
- return `${prefix}${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
324
- }
195
+ /**
196
+ * Clears all active requests without cancelling them.
197
+ * Use with caution - this will not cancel the underlying HTTP requests.
198
+ */
199
+ clear() {
200
+ this.activeRequests.clear();
201
+ }
325
202
 
326
- // Handle function requestKey
327
- if (typeof requestKey === 'function') {
328
- try {
329
- requestKey = requestKey();
330
- } catch {
331
- requestKey = null;
332
- }
333
- }
334
- if (requestKey !== null && requestKey !== undefined) return `${prefix}${String(requestKey)}`;
335
-
336
- // Use cleaned URL as key as fallback
337
- let cleanedUrl = url || '';
338
- if (cleanedUrl.includes('://')) cleanedUrl = cleanedUrl.split('://')[1];
339
- if (cleanedUrl.includes('#')) cleanedUrl = cleanedUrl.split('#')[0];
340
- if (!options.includeQuery && cleanedUrl.includes('?')) cleanedUrl = cleanedUrl.split('?')[0];
341
- return `${prefix}${cleanedUrl}`;
342
- }
203
+ /**
204
+ * Executes an HTTP request, cancelling any previous request with the same identifier.
205
+ * @param {string} url - The URL to request
206
+ * @param {Promise|import('./index.d.ts').RequestFunction|string} requestPromise - The request promise or function that returns a promise
207
+ * @param {import('./index.d.ts').RequestOptions} options - Optional configuration
208
+ * @returns {Promise} A Promise that resolves/rejects based on the most recent request
209
+ * @example
210
+ * // Request with Promise
211
+ * requestManager.request('/api/users', axios.get('/api/users'));
212
+ * @example
213
+ * // Request with Function
214
+ * requestManager.request('/api/users', ({ options }) => fetch('/api/users', { signal: options.signal, ...options }));
215
+ * @example
216
+ * // Request with Promise and custom cancellation grouping with requestKey
217
+ * const options = {
218
+ * requestKey: 'get-users'
219
+ * }
220
+ * requestManager.request('/api/users', axios.get('/api/users', options), options);
221
+ */
222
+ request(url, requestPromise) {
223
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
224
+ return _assertClassBrand(_RequestManager_brand, this, _request).call(this, this.getRequestId(url, options), requestPromise, options);
225
+ }
343
226
 
344
- /**
345
- * Cancels a specific request by its identifier.
346
- * @param {string} requestId - The unique identifier of the request to cancel
347
- * @returns {boolean} True if the request was found and cancelled, false otherwise
348
- */
349
- cancel(requestId) {
350
- /** @type {import('./index.d.ts').ActiveRequest|undefined} */
351
- const requestInfo = this.activeRequests.get(requestId);
352
- if (!requestInfo) return false;
227
+ /**
228
+ * Executes an HTTP request using fetch, cancelling any previous request with the same identifier.
229
+ * @param {string} url - The URL to fetch
230
+ * @param {import('./index.d.ts').FetchOptions} options - Optional configuration
231
+ * @returns {Promise} A Promise that resolves/rejects based on the most recent request
232
+ * @example
233
+ * // Simple GET request
234
+ * requestManager.fetch('/api/users');
235
+ * @example
236
+ * // POST request with options
237
+ * requestManager.fetch('/api/users', {
238
+ * method: 'POST',
239
+ * headers: { 'Content-Type': 'application/json' },
240
+ * body: JSON.stringify({ name: 'John' })
241
+ * });
242
+ * @example
243
+ * // Request with requestKey for custom cancellation grouping with requestKey
244
+ * requestManager.fetch('/api/users', {
245
+ * requestKey: 'get-users'
246
+ * });
247
+ */
248
+ fetch(url) {
249
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
250
+ return _assertClassBrand(_RequestManager_brand, this, _request).call(this, this.getRequestId(url, options), url, options);
251
+ }
353
252
 
354
- requestInfo.isCancelled = true; // Mark as cancelled
253
+ /**
254
+ * Executes an HTTP request using axios, cancelling any previous request with the same identifier.
255
+ * @param {string} url - The URL to request
256
+ * @param {import('./index.d.ts').AxiosRequestOptions} options - Optional configuration
257
+ * @param {import('./index.d.ts').AxiosInstance|import('./index.d.ts').AxiosStatic|null} axiosInstance - Optional axios instance to use. If not provided, uses global axios.
258
+ * @returns {Promise} A Promise that resolves/rejects based on the most recent request
259
+ * @example
260
+ * // Simple GET request (uses global axios)
261
+ * requestManager.axios('/api/users');
262
+ * @example
263
+ * // With custom axios instance
264
+ * const myAxios = axios.create({ baseURL: 'https://api.example.com' });
265
+ * requestManager.axios('/users', {}, myAxios);
266
+ * @example
267
+ * // POST request with options
268
+ * requestManager.axios('/api/users', {
269
+ * method: 'POST',
270
+ * headers: { 'Content-Type': 'application/json' },
271
+ * body: JSON.stringify({ name: 'John' })
272
+ * });
273
+ * @example
274
+ * // Request with requestKey for custom cancellation grouping with requestKey
275
+ * requestManager.axios('/api/users', {
276
+ * requestKey: 'get-users'
277
+ * });
278
+ */
279
+ axios(url) {
280
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
281
+ var axiosInstance = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
282
+ var axiosLib = axiosInstance || (typeof axios !== 'undefined' ? axios : null);
283
+ if (!axiosLib) {
284
+ throw new Error('axios was not found: pass an axios instance as the third argument of requestManager.axios() or make sure axios is available globally');
285
+ }
286
+ _assertClassBrand(_RequestManager_brand, this, _checkAxiosVersion).call(this, axiosLib);
287
+ return _assertClassBrand(_RequestManager_brand, this, _request).call(this, this.getRequestId(url, options), _ref => {
288
+ var requestOptions = _ref.options;
289
+ return axiosLib(_objectSpread2({
290
+ url
291
+ }, requestOptions));
292
+ }, options);
293
+ }
355
294
 
356
- // Try to abort using AbortController (for fetch)
357
- if (requestInfo.abortController && !requestInfo.abortController.signal.aborted) {
358
- try {
359
- requestInfo.abortController.abort('Request was cancelled');
360
- } catch (error) {}
361
- }
295
+ /**
296
+ * Executes an HTTP request using jQuery.ajax, cancelling any previous request with the same identifier.
297
+ * @param {import('./index.d.ts').AjaxFunction} ajaxFunction - A function that receives { url, ...options } and returns a Promise
298
+ * @param {string} url - The URL to request
299
+ * @param {import('./index.d.ts').BaseRequestOptions & Record<string, any>} options - Optional configuration
300
+ * @returns {Promise} A Promise that resolves/rejects based on the most recent request
301
+ * @example
302
+ * // Simple GET request
303
+ * requestManager.ajax(ajaxFunction, '/api/users');
304
+ * @example
305
+ * // POST request with options
306
+ * requestManager.ajax(ajaxFunction, '/api/users', {
307
+ * method: 'POST',
308
+ * headers: { 'Content-Type': 'application/json' },
309
+ * body: JSON.stringify({ name: 'John' })
310
+ * });
311
+ * @example
312
+ * // Request with requestKey for custom cancellation grouping with requestKey
313
+ * requestManager.ajax(ajaxFunction, '/api/users', {
314
+ * requestKey: 'get-users'
315
+ * });
316
+ */
317
+ ajax(ajaxFunction, url) {
318
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
319
+ if (typeof ajaxFunction !== 'function') throw new Error('ajaxFunction parameter must be a function');
320
+ return _assertClassBrand(_RequestManager_brand, this, _request).call(this, this.getRequestId(url, options), _ref2 => {
321
+ var requestOptions = _ref2.options;
322
+ return ajaxFunction(_objectSpread2({
323
+ url
324
+ }, requestOptions));
325
+ }, options);
326
+ }
362
327
 
363
- // Try to cancel using cancel token/function (for axios and others)
364
- if (requestInfo.cancelToken) {
328
+ /**
329
+ * Executes an HTTP request using XMLHttpRequest, cancelling any previous request with the same identifier.
330
+ * @param {string} url - The URL to request
331
+ * @param {import('./index.d.ts').XhrOptions} options - Optional configuration
332
+ * @returns {Promise<import('./index.d.ts').XhrResponse>} A Promise that resolves/rejects based on the most recent request
333
+ * @example
334
+ * // Simple GET request
335
+ * requestManager.xhr('/api/users');
336
+ * @example
337
+ * // POST request with options
338
+ * requestManager.xhr('/api/users', {
339
+ * method: 'POST',
340
+ * headers: { 'Content-Type': 'application/json' },
341
+ * body: JSON.stringify({ name: 'John' })
342
+ * });
343
+ * @example
344
+ * // Request with requestKey for custom cancellation grouping
345
+ * requestManager.xhr('/api/users', {
346
+ * requestKey: 'get-users'
347
+ * });
348
+ */
349
+ xhr(url) {
350
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
351
+ /** @type {import('./index.d.ts').RequestFunction<import('./index.d.ts').XhrResponse>} */
352
+ var xhrFunction = _ref3 => {
353
+ var fetchOptions = _ref3.options;
354
+ // Create XMLHttpRequest
355
+ var xhr = new XMLHttpRequest();
356
+ var method = (options.method || 'GET').toUpperCase();
357
+ // Create a promise that wraps the XHR request
358
+ var xhrPromise = new Promise((resolve, reject) => {
359
+ xhr.onload = function () {
360
+ detachAbortListener();
361
+ if (xhr.status >= 200 && xhr.status < 300) {
362
+ var _xhr$getResponseHeade;
363
+ var response = xhr.response;
364
+ if (options.responseType === 'json' || (!options.responseType || options.responseType === 'text') && (_xhr$getResponseHeade = xhr.getResponseHeader('Content-Type')) !== null && _xhr$getResponseHeade !== void 0 && _xhr$getResponseHeade.includes('application/json') && typeof response === 'string') {
365
365
  try {
366
- if (typeof requestInfo.cancelToken === 'function') requestInfo.cancelToken();
367
- else if (requestInfo.cancelToken.cancel) requestInfo.cancelToken.cancel();
368
- } catch (error) {}
366
+ response = JSON.parse(response);
367
+ } catch (_unused) {}
368
+ }
369
+ resolve({
370
+ data: response,
371
+ status: xhr.status,
372
+ statusText: xhr.statusText,
373
+ headers: xhr.getAllResponseHeaders(),
374
+ xhr: xhr
375
+ });
376
+ } else {
377
+ reject({
378
+ message: "Request failed with status ".concat(xhr.status),
379
+ status: xhr.status,
380
+ statusText: xhr.statusText,
381
+ xhr: xhr
382
+ });
369
383
  }
370
-
371
- // Reject the wrapper promise
372
- this.#_deleteRequest(
373
- requestId,
374
- requestInfo.rejectWrapper,
375
- this.getOptions().verbose ? new Error(`Request ${requestId} was cancelled`) : null
376
- );
377
- return true;
378
- }
379
-
380
- /**
381
- * Link abort signal with HTTP client abort method.
382
- * Useful for custom HTTP clients that only support the abort method to cancel requests.
383
- * @param {Function} abortMethod - The abort method to call when the signal is aborted
384
- * @param {AbortSignal} signal - The signal to listen to
385
- */
386
- addAbortListener(abortMethod, signal) {
387
- if (!abortMethod || !signal) return;
388
- signal.addEventListener('abort', () => {
389
- if (typeof abortMethod === 'function') {
390
- try {
391
- abortMethod();
392
- } catch (error) {}
393
- }
384
+ };
385
+ xhr.onerror = function () {
386
+ detachAbortListener();
387
+ reject({
388
+ message: 'Network error',
389
+ xhr: xhr
394
390
  });
395
- }
396
-
397
- /**
398
- * Cancels all active requests.
399
- * @returns {number} The number of requests that were cancelled
400
- */
401
- cancelAll() {
402
- const requestIds = Array.from(this.activeRequests.keys());
403
- let cancelledCount = 0;
404
- requestIds.forEach((requestId) => {
405
- if (this.cancel(requestId)) cancelledCount++;
391
+ };
392
+ xhr.ontimeout = function () {
393
+ detachAbortListener();
394
+ reject({
395
+ message: 'Request timeout',
396
+ xhr: xhr
406
397
  });
407
- return cancelledCount;
408
- }
409
-
410
- /**
411
- * Resolves the AbortController for a request: explicit option, pending handoff, or new.
412
- * Clears the pending handoff so concurrent requests do not share it.
413
- * @param {AbortController|undefined} provided - Optional AbortController from options
414
- * @returns {AbortController}
415
- * @private
416
- */
417
- #_resolveAbortController(provided) {
418
- const abortController = provided || this.abortController || new AbortController();
419
- this.abortController = null;
420
- return abortController;
421
- }
422
-
423
- /**
424
- * Picks the best abort callback for a client request object.
425
- * @param {Object} req - The request object
426
- * @returns {Function|null}
427
- * @private
428
- */
429
- #_resolveAbortMethod(req) {
430
- if (!req) return null;
431
- if (typeof req.abort === 'function') return () => req.abort();
432
- const ExtAjax =
433
- typeof globalThis !== 'undefined' && globalThis.Ext && globalThis.Ext.Ajax ? globalThis.Ext.Ajax : null;
434
- if (req.xhr && ExtAjax && typeof ExtAjax.abort === 'function') {
435
- return () => ExtAjax.abort(req);
436
- }
437
- if (req.xhr && typeof req.xhr.abort === 'function') return () => req.xhr.abort();
438
- return null;
439
- }
440
-
441
- /**
442
- * Prepares request options by merging options and removing custom properties
443
- * @param {import('./index.d.ts').RequestOptions} options - Configuration options
444
- * @param {AbortSignal} signal - Abort signal to add to request options
445
- * @returns {Object} Prepared request options
446
- * @private
447
- */
448
- #_prepareRequestOptions(options, signal) {
449
- const requestOptions = {};
450
- const customOptions = ['abortController', 'cancelToken', 'requestKey', 'noCancel', 'includeQuery'];
451
- Object.keys(options).forEach((key) => {
452
- if (customOptions.includes(key)) return;
453
- requestOptions[key] = options[key];
398
+ };
399
+
400
+ // Open the request
401
+ xhr.open(method, url, true);
402
+
403
+ // Set response type
404
+ if (options.responseType) xhr.responseType = options.responseType;
405
+ // Set withCredentials
406
+ if (options.withCredentials !== undefined) xhr.withCredentials = options.withCredentials;
407
+ // Set timeout
408
+ if (options.timeout !== undefined) xhr.timeout = options.timeout;
409
+ // Set headers
410
+ if (options.headers) Object.keys(options.headers).forEach(key => {
411
+ xhr.setRequestHeader(key, options.headers[key]);
412
+ });
413
+
414
+ // Connect abort signal to xhr.abort()
415
+ var abortListener = () => xhr.abort();
416
+ if (fetchOptions.signal) fetchOptions.signal.addEventListener('abort', abortListener);
417
+ // Detach the listener once the request settles so completed requests do not keep it alive
418
+ var detachAbortListener = () => {
419
+ if (fetchOptions.signal) fetchOptions.signal.removeEventListener('abort', abortListener);
420
+ };
421
+ xhr.onabort = function () {
422
+ reject({
423
+ message: 'Request was cancelled',
424
+ xhr: xhr
454
425
  });
455
- requestOptions.signal = signal;
456
- return requestOptions;
457
- }
426
+ };
427
+
428
+ // Send the request
429
+ xhr.send(options.body || null);
430
+ });
431
+ return xhrPromise;
432
+ };
433
+ return _assertClassBrand(_RequestManager_brand, this, _request).call(this, this.getRequestId(url, options), xhrFunction, options);
434
+ }
458
435
 
459
- /**
460
- * Deletes a request from the active requests map and rejects the wrapper promise
461
- * @param {string} requestId - The unique identifier of the request
462
- * @param {Function} rejectWrapper - The function to reject the wrapper promise
463
- * @param {*} error - The error to reject the wrapper promise with; the wrapper is not rejected if null/undefined
464
- * @private
465
- */
466
- #_deleteRequest(requestId, rejectWrapper, error) {
467
- this.activeRequests.delete(requestId);
468
- if (error !== null && error !== undefined) {
469
- rejectWrapper(error);
470
- }
436
+ /**
437
+ * Returns the request identifier for a URL and options.
438
+ * @param {string} url - The URL used when starting the request
439
+ * @param {import('./index.d.ts').BaseRequestOptions} options - Same options used for the request
440
+ * @returns {string} The request identifier
441
+ * @example
442
+ * requestManager.fetch('/api/users');
443
+ * const id = requestManager.getRequestId('/api/users');
444
+ * requestManager.cancel(id);
445
+ */
446
+ getRequestId(url) {
447
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
448
+ var requestKey = options.requestKey;
449
+ var prefix = 'request_';
450
+
451
+ // Generate a unique identifier to prevent cancellation for non cancelable requests
452
+ if (options.noCancel) {
453
+ return "".concat(prefix).concat(Date.now(), "_").concat(Math.random().toString(36).slice(2, 11));
454
+ }
455
+
456
+ // Handle function requestKey
457
+ if (typeof requestKey === 'function') {
458
+ try {
459
+ requestKey = requestKey();
460
+ } catch (_unused2) {
461
+ requestKey = null;
471
462
  }
463
+ }
464
+ if (requestKey !== null && requestKey !== undefined) return "".concat(prefix).concat(String(requestKey));
465
+
466
+ // Use cleaned URL as key as fallback
467
+ var cleanedUrl = url || '';
468
+ if (cleanedUrl.includes('://')) cleanedUrl = cleanedUrl.split('://')[1];
469
+ if (cleanedUrl.includes('#')) cleanedUrl = cleanedUrl.split('#')[0];
470
+ if (!options.includeQuery && cleanedUrl.includes('?')) cleanedUrl = cleanedUrl.split('?')[0];
471
+ var methodPrefix = options.includeMethod === false ? '' : "".concat((options.method || options.type || 'GET').toUpperCase(), "_");
472
+ return "".concat(prefix).concat(methodPrefix).concat(cleanedUrl);
473
+ }
472
474
 
473
- /**
474
- * Completes a request by deleting it from the active requests map and resolving the wrapper promise
475
- * @param {string} requestId - The unique identifier of the request
476
- * @param {Function} resolveWrapper - The function to resolve the wrapper promise
477
- * @param {Promise} requestPromise - The request promise
478
- * @param {boolean} isCancelled - Whether the request was cancelled
479
- * @private
480
- */
481
- #_completeRequest(requestId, resolveWrapper, requestPromise, isCancelled) {
482
- this.activeRequests.delete(requestId);
483
- if (!isCancelled) {
484
- resolveWrapper(requestPromise);
485
- }
475
+ /**
476
+ * Cancels a specific request by its identifier.
477
+ * @param {string} requestId - The unique identifier of the request to cancel
478
+ * @returns {boolean} True if the request was found and cancelled, false otherwise
479
+ */
480
+ cancel(requestId) {
481
+ /** @type {import('./index.d.ts').ActiveRequest|undefined} */
482
+ var requestInfo = this.activeRequests.get(requestId);
483
+ if (!requestInfo) return false;
484
+ requestInfo.isCancelled = true; // Mark as cancelled
485
+
486
+ // Try to abort using AbortController (for fetch)
487
+ if (requestInfo.abortController && !requestInfo.abortController.signal.aborted) {
488
+ try {
489
+ requestInfo.abortController.abort('Request was cancelled');
490
+ } catch (error) {}
491
+ }
492
+
493
+ // Try to cancel using cancel token/function (for axios and others)
494
+ if (requestInfo.cancelToken) {
495
+ try {
496
+ if (typeof requestInfo.cancelToken === 'function') requestInfo.cancelToken();else if (requestInfo.cancelToken.cancel) requestInfo.cancelToken.cancel();
497
+ } catch (error) {}
498
+ }
499
+
500
+ // Reject the wrapper promise
501
+ _assertClassBrand(_RequestManager_brand, this, _deleteRequest).call(this, requestId, requestInfo.rejectWrapper, this.getOptions().verbose ? new Error("Request ".concat(requestId, " was cancelled")) : null);
502
+ return true;
503
+ }
504
+
505
+ /**
506
+ * Link abort signal with HTTP client abort method.
507
+ * Useful for custom HTTP clients that only support the abort method to cancel requests.
508
+ * @param {Function} abortMethod - The abort method to call when the signal is aborted
509
+ * @param {AbortSignal} signal - The signal to listen to
510
+ */
511
+ addAbortListener(abortMethod, signal) {
512
+ if (!abortMethod || !signal) return;
513
+ signal.addEventListener('abort', () => {
514
+ if (typeof abortMethod === 'function') {
515
+ try {
516
+ abortMethod();
517
+ } catch (error) {}
486
518
  }
519
+ });
520
+ }
487
521
 
488
- /**
489
- * Internal method that handles the core request logic.
490
- * @param {string} requestId - Unique identifier for the request
491
- * @param {Promise|import('./index.d.ts').RequestFunction|string} requestPromise - The request promise, function, or URL string
492
- * @param {import('./index.d.ts').BaseRequestOptions} options - Configuration options
493
- * @returns {Promise} A Promise that resolves/rejects based on the most recent request
494
- * @private
495
- */
496
- #_request(requestId, requestPromise, options = {}) {
497
- const abortController = this.#_resolveAbortController(options.abortController);
498
-
499
- // Handle different types of requestPromise inputs
500
- // Priority: Function (Custom logic) > String (URL) > Promise (axios, fetch, etc.)
501
- if (typeof requestPromise === 'function') {
502
- // Function: custom logic for any library (axios, ajax, etc.)
503
- try {
504
- requestPromise = requestPromise({
505
- options: this.#_prepareRequestOptions(options, abortController.signal),
506
- });
507
- } catch (error) {
508
- return Promise.reject(error);
509
- }
510
- } else if (typeof requestPromise === 'string') {
511
- // String (URL): make fetch internally
512
- try {
513
- requestPromise = fetch(requestPromise, this.#_prepareRequestOptions(options, abortController.signal));
514
- } catch (error) {
515
- return Promise.reject(error);
516
- }
517
- }
522
+ /**
523
+ * Cancels all active requests.
524
+ * @returns {number} The number of requests that were cancelled
525
+ */
526
+ cancelAll() {
527
+ var requestIds = Array.from(this.activeRequests.keys());
528
+ var cancelledCount = 0;
529
+ requestIds.forEach(requestId => {
530
+ if (this.cancel(requestId)) cancelledCount++;
531
+ });
532
+ return cancelledCount;
533
+ }
534
+ }
535
+ function _resolveAbortController(provided) {
536
+ var abortController = provided || this.abortController || new AbortController();
537
+ this.abortController = null;
538
+ return abortController;
539
+ }
540
+ /**
541
+ * Picks the best abort callback for a client request object.
542
+ * @param {Object} req - The request object
543
+ * @returns {Function|null}
544
+ * @private
545
+ */
546
+ function _resolveAbortMethod(req) {
547
+ if (!req) return null;
548
+ if (typeof req.abort === 'function') return () => req.abort();
549
+ var ExtAjax = typeof globalThis !== 'undefined' && globalThis.Ext && globalThis.Ext.Ajax ? globalThis.Ext.Ajax : null;
550
+ if (req.xhr && ExtAjax && typeof ExtAjax.abort === 'function') {
551
+ return () => ExtAjax.abort(req);
552
+ }
553
+ if (req.xhr && typeof req.xhr.abort === 'function') return () => req.xhr.abort();
554
+ return null;
555
+ }
556
+ /**
557
+ * Prepares request options by merging options and removing custom properties
558
+ * @param {import('./index.d.ts').RequestOptions} options - Configuration options
559
+ * @param {AbortSignal} signal - Abort signal to add to request options
560
+ * @returns {Object} Prepared request options
561
+ * @private
562
+ */
563
+ function _prepareRequestOptions(options, signal) {
564
+ var requestOptions = {};
565
+ var customOptions = ['abortController', 'cancelToken', 'requestKey', 'noCancel', 'includeQuery', 'includeMethod', 'verbose'];
566
+ Object.keys(options).forEach(key => {
567
+ if (customOptions.includes(key)) return;
568
+ requestOptions[key] = options[key];
569
+ });
570
+ requestOptions.signal = signal;
571
+ return requestOptions;
572
+ }
573
+ /**
574
+ * Warns when the provided axios instance predates 0.22.0, the first version
575
+ * supporting AbortSignal cancellation. Older instances silently ignore
576
+ * options.signal, so duplicate requests would not be cancelled.
577
+ * @param {object} axiosLib - The axios instance about to be used
578
+ * @private
579
+ */
580
+ function _checkAxiosVersion(axiosLib) {
581
+ var version = typeof (axiosLib === null || axiosLib === void 0 ? void 0 : axiosLib.VERSION) === 'string' ? axiosLib.VERSION : null;
582
+ if (!version) return;
583
+ var _version$split$map = version.split('.').map(Number),
584
+ _version$split$map2 = _slicedToArray(_version$split$map, 2),
585
+ major = _version$split$map2[0],
586
+ minor = _version$split$map2[1];
587
+ if (major === 0 && minor < 22) {
588
+ console.warn("[request-manager] axios >= 0.22.0 is required: axios ".concat(version, " ignores the AbortSignal used for automatic cancellation. Please upgrade axios."));
589
+ }
590
+ }
591
+ /**
592
+ * Deletes a request from the active requests map and rejects the wrapper promise
593
+ * @param {string} requestId - The unique identifier of the request
594
+ * @param {Function} rejectWrapper - The function to reject the wrapper promise
595
+ * @param {*} error - The error to reject the wrapper promise with; the wrapper is not rejected if null/undefined
596
+ * @private
597
+ */
598
+ function _deleteRequest(requestId, rejectWrapper, error) {
599
+ this.activeRequests.delete(requestId);
600
+ if (error !== null && error !== undefined) {
601
+ rejectWrapper(error);
602
+ }
603
+ }
604
+ /**
605
+ * Completes a request by deleting it from the active requests map and resolving the wrapper promise
606
+ * @param {string} requestId - The unique identifier of the request
607
+ * @param {Function} resolveWrapper - The function to resolve the wrapper promise
608
+ * @param {Promise} requestPromise - The request promise
609
+ * @param {boolean} isCancelled - Whether the request was cancelled
610
+ * @private
611
+ */
612
+ function _completeRequest(requestId, resolveWrapper, requestPromise, isCancelled) {
613
+ this.activeRequests.delete(requestId);
614
+ if (!isCancelled) {
615
+ resolveWrapper(requestPromise);
616
+ }
617
+ }
618
+ /**
619
+ * Internal method that handles the core request logic.
620
+ * @param {string} requestId - Unique identifier for the request
621
+ * @param {Promise|import('./index.d.ts').RequestFunction|string} requestPromise - The request promise, function, or URL string
622
+ * @param {import('./index.d.ts').BaseRequestOptions} options - Configuration options
623
+ * @returns {Promise} A Promise that resolves/rejects based on the most recent request
624
+ * @private
625
+ */
626
+ function _request(requestId, requestPromise) {
627
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
628
+ var abortController = _assertClassBrand(_RequestManager_brand, this, _resolveAbortController).call(this, options.abortController);
629
+
630
+ // Handle different types of requestPromise inputs
631
+ // Priority: Function (Custom logic) > String (URL) > Promise (axios, fetch, etc.)
632
+ if (typeof requestPromise === 'function') {
633
+ // Function: custom logic for any library (axios, ajax, etc.)
634
+ try {
635
+ requestPromise = requestPromise({
636
+ options: _assertClassBrand(_RequestManager_brand, this, _prepareRequestOptions).call(this, options, abortController.signal)
637
+ });
638
+ } catch (error) {
639
+ return Promise.reject(error);
640
+ }
641
+ } else if (typeof requestPromise === 'string') {
642
+ // String (URL): make fetch internally
643
+ try {
644
+ requestPromise = fetch(requestPromise, _assertClassBrand(_RequestManager_brand, this, _prepareRequestOptions).call(this, options, abortController.signal));
645
+ } catch (error) {
646
+ return Promise.reject(error);
647
+ }
648
+ }
518
649
 
519
- // Link the request object's abort method (Ext.Ajax, XHR, etc.) to the cancellation signal
520
- this.addAbortListener(this.#_resolveAbortMethod(requestPromise), abortController.signal);
650
+ // Link the request object's abort method (Ext.Ajax, XHR, etc.) to the cancellation signal
651
+ this.addAbortListener(_assertClassBrand(_RequestManager_brand, this, _resolveAbortMethod).call(this, requestPromise), abortController.signal);
521
652
 
522
- // Cancel previous request with the same identifier if it exists
523
- if (!options.noCancel) this.cancel(requestId);
653
+ // Cancel previous request with the same identifier if it exists
654
+ if (!options.noCancel) this.cancel(requestId);
524
655
 
525
- // Create a wrapper promise that will be resolved/rejected based on the request
526
- let resolveWrapper, rejectWrapper;
527
- const wrapperPromise = new Promise((resolve, reject) => {
528
- resolveWrapper = resolve;
529
- rejectWrapper = reject;
530
- });
656
+ // Create a wrapper promise that will be resolved/rejected based on the request
657
+ var resolveWrapper, rejectWrapper;
658
+ var wrapperPromise = new Promise((resolve, reject) => {
659
+ resolveWrapper = resolve;
660
+ rejectWrapper = reject;
661
+ });
531
662
 
532
- /**
533
- * @type {import('./index.d.ts').ActiveRequest}
534
- */
535
- const requestInfo = {
536
- promise: requestPromise,
537
- abortController: abortController,
538
- cancelToken: options.cancelToken || null,
539
- resolveWrapper: resolveWrapper,
540
- rejectWrapper: rejectWrapper,
541
- isCancelled: false,
542
- };
543
-
544
- this.activeRequests.set(requestId, requestInfo);
545
-
546
- // Handle request promise completion
547
- if (requestPromise && typeof requestPromise.then === 'function') {
548
- try {
549
- let req = requestPromise.then((result) => {
550
- if (this.activeRequests.get(requestId) !== requestInfo) return;
551
- this.#_completeRequest(requestId, resolveWrapper, result, requestInfo.isCancelled);
552
- });
553
- if (req.catch)
554
- req.catch((error) => {
555
- onError(this, error);
556
- });
557
- } catch (error) {
558
- onError(this, error);
559
- }
560
- function onError(scope, error) {
561
- // Check if this requestInfo is still the active one, or if it was cancelled
562
- if (scope.activeRequests.get(requestId) !== requestInfo) return;
563
- if (requestInfo.isCancelled) {
564
- // Already cancelled: let cancel() handle cleanup and reject the wrapper promise
565
- scope.cancel(requestId);
566
- return;
567
- }
568
- // Only delete if this is still the active request
569
- scope.#_deleteRequest(requestId, rejectWrapper, error);
570
- }
571
- } else {
572
- // Non-promise (Ext.Ajax request object, raw XHR, etc.). Keep tracked until the
573
- // underlying XHR finishes so a later duplicate can still cancel it.
574
- const xhr =
575
- requestPromise &&
576
- (requestPromise.xhr ||
577
- (typeof XMLHttpRequest !== 'undefined' && requestPromise instanceof XMLHttpRequest
578
- ? requestPromise
579
- : null));
580
- const finish = () => {
581
- if (this.activeRequests.get(requestId) !== requestInfo) return;
582
- this.#_completeRequest(requestId, resolveWrapper, requestPromise, requestInfo.isCancelled);
583
- };
584
- if (xhr && typeof xhr.addEventListener === 'function') {
585
- xhr.addEventListener('loadend', finish);
586
- } else {
587
- setTimeout(finish, 0);
588
- }
589
- }
590
- return wrapperPromise;
663
+ /**
664
+ * @type {import('./index.d.ts').ActiveRequest}
665
+ */
666
+ var requestInfo = {
667
+ promise: requestPromise,
668
+ abortController: abortController,
669
+ cancelToken: options.cancelToken || null,
670
+ resolveWrapper: resolveWrapper,
671
+ rejectWrapper: rejectWrapper,
672
+ isCancelled: false
673
+ };
674
+ this.activeRequests.set(requestId, requestInfo);
675
+
676
+ // Handle request promise completion
677
+ if (requestPromise && typeof requestPromise.then === 'function') {
678
+ try {
679
+ var req = requestPromise.then(result => {
680
+ if (this.activeRequests.get(requestId) !== requestInfo) return;
681
+ _assertClassBrand(_RequestManager_brand, this, _completeRequest).call(this, requestId, resolveWrapper, result, requestInfo.isCancelled);
682
+ });
683
+ if (req.catch) req.catch(error => {
684
+ onError(this, error);
685
+ });
686
+ } catch (error) {
687
+ onError(this, error);
688
+ }
689
+ function onError(scope, error) {
690
+ // Check if this requestInfo is still the active one, or if it was cancelled
691
+ if (scope.activeRequests.get(requestId) !== requestInfo) return;
692
+ if (requestInfo.isCancelled) {
693
+ // Already cancelled: let cancel() handle cleanup and reject the wrapper promise
694
+ scope.cancel(requestId);
695
+ return;
591
696
  }
697
+ // Only delete if this is still the active request
698
+ _assertClassBrand(_RequestManager_brand, scope, _deleteRequest).call(scope, requestId, rejectWrapper, error);
699
+ }
700
+ } else {
701
+ // Non-promise (Ext.Ajax request object, raw XHR, etc.). Keep tracked until the
702
+ // underlying XHR finishes so a later duplicate can still cancel it.
703
+ var xhr = requestPromise && (requestPromise.xhr || (typeof XMLHttpRequest !== 'undefined' && requestPromise instanceof XMLHttpRequest ? requestPromise : null));
704
+ var finish = () => {
705
+ if (this.activeRequests.get(requestId) !== requestInfo) return;
706
+ _assertClassBrand(_RequestManager_brand, this, _completeRequest).call(this, requestId, resolveWrapper, requestPromise, requestInfo.isCancelled);
707
+ };
708
+ if (xhr && typeof xhr.addEventListener === 'function') {
709
+ xhr.addEventListener('loadend', finish);
710
+ } else {
711
+ setTimeout(finish, 0);
712
+ }
592
713
  }
714
+ return wrapperPromise;
715
+ }
593
716
 
594
- return RequestManager;
717
+ return RequestManager;
595
718
 
596
719
  })();
597
720
  //# sourceMappingURL=request-manager.js.map