@enegalan/request-manager 1.1.0 → 1.1.1

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