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