@enegalan/request-manager 1.0.10 → 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,589 +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
- * Map to store active requests by their unique identifier.
17
- * @type {Map<string, import('./index.d.ts').ActiveRequest>}
18
- */
19
- this.activeRequests = new Map();
20
- /**
21
- * @type {import('./index.d.ts').Options}
22
- */
23
- this.options = options;
24
- /**
25
- * One-shot AbortController for getSignal()/getAbortController() handoff.
26
- * Consumed by the next request that does not pass options.abortController.
27
- * @type {AbortController|null}
28
- */
29
- this.abortController = null;
30
- }
31
-
32
- /**
33
- * Gets the manager options
34
- * @returns {import('./index.d.ts').Options} Manager options
35
- */
36
- getOptions() {
37
- return this.options;
38
- }
39
-
40
- /**
41
- * Sets the manager options
42
- * @param {import('./index.d.ts').Options} options - The options to set
43
- */
44
- setOptions(options) {
45
- this.options = options;
46
- }
47
-
48
- /**
49
- * Creates a new AbortController and returns its signal for the next request()
50
- * (one getSignal → one request). Do not use for parallel requests; use fetch(),
51
- * axios(), or request(url, ({ options }) => ...) instead — they create their own signal.
52
- * @returns {AbortSignal} The signal from a new AbortController
53
- * @example
54
- * const signal = requestManager.getSignal();
55
- * requestManager.request('/api/users', fetch('/api/users', { signal }));
56
- */
57
- getSignal() {
58
- return this.getAbortController().signal;
59
- }
60
-
61
- /**
62
- * Creates a new AbortController for the next request handoff.
63
- * Always returns a fresh controller (never reuses one from another in-flight request).
64
- * @returns {AbortController} A new AbortController instance
65
- */
66
- getAbortController() {
67
- this.abortController = new AbortController();
68
- return this.abortController;
69
- }
70
-
71
- /**
72
- * Checks if a request with the given identifier is currently active.
73
- * @param {string} requestId - The unique identifier to check
74
- * @returns {boolean} True if the request is active, false otherwise
75
- */
76
- isActive(requestId) {
77
- return this.activeRequests.has(requestId);
78
- }
79
-
80
- /**
81
- * Gets the number of active requests.
82
- * @returns {number} The number of currently active requests
83
- */
84
- getActiveCount() {
85
- return this.activeRequests.size;
86
- }
87
-
88
- /**
89
- * Clears all active requests without cancelling them.
90
- * Use with caution - this will not cancel the underlying HTTP requests.
91
- */
92
- clear() {
93
- this.activeRequests.clear();
94
- }
95
-
114
+ constructor() {
115
+ var _options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
96
116
  /**
97
- * Executes an HTTP request, cancelling any previous request with the same identifier.
98
- * @param {string} url - The URL to request
99
- * @param {Promise|import('./index.d.ts').RequestFunction|string} requestPromise - The request promise or function that returns a promise
100
- * @param {import('./index.d.ts').RequestOptions} options - Optional configuration
101
- * @returns {Promise} A Promise that resolves/rejects based on the most recent request
102
- * @example
103
- * // Request with Promise
104
- * requestManager.request('/api/users', axios.get('/api/users', { cancelToken: axios.CancelToken.source().token }));
105
- * @example
106
- * // Request with Function
107
- * requestManager.request('/api/users', ({ options }) => fetch('/api/users', { signal: options.signal, ...options }));
108
- * @example
109
- * // Request with Promise and custom cancellation grouping with requestKey
110
- * const options = {
111
- * requestKey: 'get-users',
112
- * cancelToken: axios.CancelToken.source().cancel
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
- }
119
-
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
- * });
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
140
122
  */
141
- fetch(url, options = {}) {
142
- return this.#_request(this.getRequestId(url, options), url, options);
143
- }
144
-
123
+ _classPrivateMethodInitSpec(this, _RequestManager_brand);
145
124
  /**
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
- * });
125
+ * @type {Map<string, import('./index.d.ts').ActiveRequest>}
170
126
  */
171
- axios(url, options = {}, axiosInstance = null) {
172
- const axiosLib = axiosInstance || axios;
173
- const cancelToken = axiosLib.CancelToken.source();
174
- const requestId = this.getRequestId(url, options);
175
- return this.#_request(requestId, axiosLib({ url, cancelToken: cancelToken.token, ...options }), {
176
- cancelToken: cancelToken,
177
- ...options,
178
- });
179
- }
180
-
127
+ this.activeRequests = new Map();
181
128
  /**
182
- * Executes an HTTP request using jQuery.ajax, cancelling any previous request with the same identifier.
183
- * @param {import('./index.d.ts').AjaxFunction} ajaxFunction - A function that receives { url, ...options } and returns a Promise
184
- * @param {string} url - The URL to request
185
- * @param {import('./index.d.ts').BaseRequestOptions & Record<string, any>} options - Optional configuration
186
- * @returns {Promise} A Promise that resolves/rejects based on the most recent request
187
- * @example
188
- * // Simple GET request
189
- * requestManager.ajax(ajaxFunction, '/api/users');
190
- * @example
191
- * // POST request with options
192
- * requestManager.ajax(ajaxFunction, '/api/users', {
193
- * method: 'POST',
194
- * headers: { 'Content-Type': 'application/json' },
195
- * body: JSON.stringify({ name: 'John' })
196
- * });
197
- * @example
198
- * // Request with requestKey for custom cancellation grouping with requestKey
199
- * requestManager.ajax(ajaxFunction, '/api/users', {
200
- * requestKey: 'get-users'
201
- * });
129
+ * @type {import('./index.d.ts').Options}
202
130
  */
203
- ajax(ajaxFunction, url, options = {}) {
204
- if (typeof ajaxFunction !== 'function') throw new Error('ajaxFunction parameter must be a function');
205
- return this.#_request(
206
- this.getRequestId(url, options),
207
- ({ options: requestOptions }) => ajaxFunction({ url, ...requestOptions }),
208
- options
209
- );
210
- }
211
-
131
+ this.options = _options;
212
132
  /**
213
- * Executes an HTTP request using XMLHttpRequest, cancelling any previous request with the same identifier.
214
- * @param {string} url - The URL to request
215
- * @param {import('./index.d.ts').XhrOptions} options - Optional configuration
216
- * @returns {Promise<import('./index.d.ts').XhrResponse>} A Promise that resolves/rejects based on the most recent request
217
- * @example
218
- * // Simple GET request
219
- * requestManager.xhr('/api/users');
220
- * @example
221
- * // POST request with options
222
- * requestManager.xhr('/api/users', {
223
- * method: 'POST',
224
- * headers: { 'Content-Type': 'application/json' },
225
- * body: JSON.stringify({ name: 'John' })
226
- * });
227
- * @example
228
- * // Request with requestKey for custom cancellation grouping
229
- * requestManager.xhr('/api/users', {
230
- * requestKey: 'get-users'
231
- * });
133
+ * One-shot AbortController for getSignal()/getAbortController() handoff.
134
+ * Consumed by the next request that does not pass options.abortController.
135
+ * @type {AbortController|null}
232
136
  */
233
- xhr(url, options = {}) {
234
- const requestId = this.getRequestId(url, options);
235
- /** @type {import('./index.d.ts').RequestFunction<import('./index.d.ts').XhrResponse>} */
236
- const xhrFunction = ({ options: fetchOptions }) => {
237
- // Create XMLHttpRequest
238
- const xhr = new XMLHttpRequest();
239
- const method = (options.method || 'GET').toUpperCase();
240
- // Create a promise that wraps the XHR request
241
- const xhrPromise = new Promise((resolve, reject) => {
242
- xhr.onload = function () {
243
- if (xhr.status >= 200 && xhr.status < 300) {
244
- let response = xhr.response;
245
- if (
246
- options.responseType === 'json' ||
247
- (xhr.getResponseHeader('Content-Type') &&
248
- xhr.getResponseHeader('Content-Type').includes('application/json'))
249
- ) {
250
- try {
251
- response = JSON.parse(xhr.responseText);
252
- } catch {
253
- response = xhr.responseText;
254
- }
255
- }
256
- resolve({
257
- data: response,
258
- status: xhr.status,
259
- statusText: xhr.statusText,
260
- headers: xhr.getAllResponseHeaders(),
261
- xhr: xhr,
262
- });
263
- } else {
264
- reject({
265
- message: `Request failed with status ${xhr.status}`,
266
- status: xhr.status,
267
- statusText: xhr.statusText,
268
- xhr: xhr,
269
- });
270
- }
271
- };
272
- xhr.onerror = function () {
273
- reject({
274
- message: 'Network error',
275
- xhr: xhr,
276
- });
277
- };
278
- xhr.ontimeout = function () {
279
- reject({
280
- message: 'Request timeout',
281
- xhr: xhr,
282
- });
283
- };
284
-
285
- // Open the request
286
- xhr.open(method, url, true);
287
-
288
- // Set response type
289
- if (options.responseType) xhr.responseType = options.responseType;
290
- // Set withCredentials
291
- if (options.withCredentials !== undefined) xhr.withCredentials = options.withCredentials;
292
- // Set timeout
293
- if (options.timeout !== undefined) xhr.timeout = options.timeout;
294
- // Set headers
295
- if (options.headers)
296
- Object.keys(options.headers).forEach((key) => {
297
- xhr.setRequestHeader(key, options.headers[key]);
298
- });
299
-
300
- // Connect abort signal to xhr.abort()
301
- if (fetchOptions.signal) fetchOptions.signal.addEventListener('abort', () => xhr.abort());
302
-
303
- // Send the request
304
- 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
373
+ });
374
+ } else {
375
+ reject({
376
+ message: "Request failed with status ".concat(xhr.status),
377
+ status: xhr.status,
378
+ statusText: xhr.statusText,
379
+ xhr: xhr
305
380
  });
306
- return xhrPromise;
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
+ });
307
394
  };
308
- return this.#_request(requestId, xhrFunction, options);
309
- }
310
395
 
311
- /**
312
- * Returns the request identifier for a URL and options.
313
- * @param {string} url - The URL used when starting the request
314
- * @param {import('./index.d.ts').BaseRequestOptions} options - Same options used for the request
315
- * @returns {string} The request identifier
316
- * @example
317
- * requestManager.fetch('/api/users');
318
- * const id = requestManager.getRequestId('/api/users');
319
- * requestManager.cancel(id);
320
- */
321
- getRequestId(url, options = {}) {
322
- let requestKey = options.requestKey;
323
-
324
- const prefix = 'request_';
325
-
326
- // Generate a unique identifier to prevent cancellation for non cancelable requests
327
- if (options.noCancel) {
328
- return `${prefix}${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
329
- }
330
-
331
- // Handle function requestKey
332
- if (typeof requestKey === 'function') {
333
- try {
334
- requestKey = requestKey();
335
- } catch {
336
- requestKey = null;
337
- }
338
- }
339
- if (requestKey !== null && requestKey !== undefined) return `${prefix}${String(requestKey)}`;
340
-
341
- // Use cleaned URL as key as fallback
342
- let cleanedUrl = url || '';
343
- if (cleanedUrl.includes('://')) cleanedUrl = cleanedUrl.split('://')[1];
344
- if (cleanedUrl.includes('#')) cleanedUrl = cleanedUrl.split('#')[0];
345
- if (!options.includeQuery && cleanedUrl.includes('?')) cleanedUrl = cleanedUrl.split('?')[0];
346
- return `${prefix}${cleanedUrl}`;
347
- }
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
+ });
348
409
 
349
- /**
350
- * Cancels a specific request by its identifier.
351
- * @param {string} requestId - The unique identifier of the request to cancel
352
- * @returns {boolean} True if the request was found and cancelled, false otherwise
353
- */
354
- cancel(requestId) {
355
- /** @type {import('./index.d.ts').ActiveRequest|undefined} */
356
- const requestInfo = this.activeRequests.get(requestId);
357
- if (!requestInfo) return false;
358
-
359
- requestInfo.isCancelled = true; // Mark as cancelled
360
-
361
- // Try to abort using AbortController (for fetch)
362
- if (requestInfo.abortController && !requestInfo.abortController.signal.aborted) {
363
- try {
364
- requestInfo.abortController.abort('Request was cancelled');
365
- } catch (error) {}
366
- }
367
-
368
- // Try to cancel using cancel token/function (for axios and others)
369
- if (requestInfo.cancelToken) {
370
- try {
371
- if (typeof requestInfo.cancelToken === 'function') requestInfo.cancelToken();
372
- else if (requestInfo.cancelToken.cancel) requestInfo.cancelToken.cancel();
373
- } catch (error) {}
374
- }
375
-
376
- // Reject the wrapper promise
377
- this.#_deleteRequest(
378
- requestId,
379
- requestInfo.rejectWrapper,
380
- this.getOptions().verbose ? new Error(`Request ${requestId} was cancelled`) : null
381
- );
382
- 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));
383
439
  }
384
440
 
385
- /**
386
- * Link abort signal with HTTP client abort method.
387
- * Useful for custom HTTP clients that only support the abort method to cancel requests.
388
- * @param {Function} abortMethod - The abort method to call when the signal is aborted
389
- * @param {AbortSignal} signal - The signal to listen to
390
- */
391
- addAbortListener(abortMethod, signal) {
392
- if (!abortMethod || !signal) return;
393
- signal.addEventListener('abort', () => {
394
- if (typeof abortMethod === 'function') {
395
- try {
396
- abortMethod();
397
- } catch (error) {}
398
- }
399
- });
441
+ // Handle function requestKey
442
+ if (typeof requestKey === 'function') {
443
+ try {
444
+ requestKey = requestKey();
445
+ } catch (_unused2) {
446
+ requestKey = null;
447
+ }
400
448
  }
401
-
402
- /**
403
- * Cancels all active requests.
404
- * @returns {number} The number of requests that were cancelled
405
- */
406
- cancelAll() {
407
- const requestIds = Array.from(this.activeRequests.keys());
408
- let cancelledCount = 0;
409
- requestIds.forEach((requestId) => {
410
- if (this.cancel(requestId)) cancelledCount++;
411
- });
412
- 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) {}
413
476
  }
414
477
 
415
- /**
416
- * Resolves the AbortController for a request: explicit option, pending handoff, or new.
417
- * Clears the pending handoff so concurrent requests do not share it.
418
- * @param {AbortController|undefined} provided - Optional AbortController from options
419
- * @returns {AbortController}
420
- * @private
421
- */
422
- #_resolveAbortController(provided) {
423
- const abortController = provided || this.abortController || new AbortController();
424
- this.abortController = null;
425
- 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) {}
426
483
  }
427
484
 
428
- /**
429
- * Picks the best abort callback for a client request object.
430
- * @param {Object} req - The request object
431
- * @returns {Function|null}
432
- * @private
433
- */
434
- #_resolveAbortMethod(req) {
435
- if (!req) return null;
436
- if (typeof req.abort === 'function') return () => req.abort();
437
- const ExtAjax =
438
- typeof globalThis !== 'undefined' && globalThis.Ext && globalThis.Ext.Ajax ? globalThis.Ext.Ajax : null;
439
- if (req.xhr && ExtAjax && typeof ExtAjax.abort === 'function') {
440
- return () => ExtAjax.abort(req);
441
- }
442
- if (req.xhr && typeof req.xhr.abort === 'function') return () => req.xhr.abort();
443
- 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);
444
625
  }
445
-
446
- /**
447
- * Prepares request options by merging options and removing custom properties
448
- * @param {import('./index.d.ts').RequestOptions} options - Configuration options
449
- * @param {AbortSignal} signal - Abort signal to add to request options
450
- * @returns {Object} Prepared request options
451
- * @private
452
- */
453
- #_prepareRequestOptions(options, signal) {
454
- const requestOptions = {};
455
- const customOptions = ['abortController', 'cancelToken', 'requestKey', 'noCancel', 'includeQuery'];
456
- Object.keys(options).forEach((key) => {
457
- if (customOptions.includes(key)) return;
458
- requestOptions[key] = options[key];
459
- });
460
- requestOptions.signal = signal;
461
- 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);
462
632
  }
463
-
464
- /**
465
- * Deletes a request from the active requests map and rejects the wrapper promise
466
- * @param {string} requestId - The unique identifier of the request
467
- * @param {Function} rejectWrapper - The function to reject the wrapper promise
468
- * @param {*} error - The error to reject the wrapper promise with; the wrapper is not rejected if null/undefined
469
- * @private
470
- */
471
- #_deleteRequest(requestId, rejectWrapper, error) {
472
- this.activeRequests.delete(requestId);
473
- if (error !== null && error !== undefined) {
474
- rejectWrapper(error);
475
- }
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);
476
673
  }
477
-
478
- /**
479
- * Completes a request by deleting it from the active requests map and resolving the wrapper promise
480
- * @param {string} requestId - The unique identifier of the request
481
- * @param {Function} resolveWrapper - The function to resolve the wrapper promise
482
- * @param {Promise} requestPromise - The request promise
483
- * @param {boolean} isCancelled - Whether the request was cancelled
484
- * @private
485
- */
486
- #_completeRequest(requestId, resolveWrapper, requestPromise, isCancelled) {
487
- this.activeRequests.delete(requestId);
488
- if (!isCancelled) {
489
- resolveWrapper(requestPromise);
490
- }
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);
491
684
  }
492
-
493
- /**
494
- * Internal method that handles the core request logic.
495
- * @param {string} requestId - Unique identifier for the request
496
- * @param {Promise|import('./index.d.ts').RequestFunction|string} requestPromise - The request promise, function, or URL string
497
- * @param {import('./index.d.ts').BaseRequestOptions} options - Configuration options
498
- * @returns {Promise} A Promise that resolves/rejects based on the most recent request
499
- * @private
500
- */
501
- #_request(requestId, requestPromise, options = {}) {
502
- const abortController = this.#_resolveAbortController(options.abortController);
503
-
504
- // Handle different types of requestPromise inputs
505
- // Priority: Function (Custom logic) > String (URL) > Promise (axios, fetch, etc.)
506
- if (typeof requestPromise === 'function') {
507
- // Function: custom logic for any library (axios, ajax, etc.)
508
- try {
509
- requestPromise = requestPromise({
510
- options: this.#_prepareRequestOptions(options, abortController.signal),
511
- });
512
- } catch (error) {
513
- return Promise.reject(error);
514
- }
515
- } else if (typeof requestPromise === 'string') {
516
- // String (URL): make fetch internally
517
- try {
518
- requestPromise = fetch(requestPromise, this.#_prepareRequestOptions(options, abortController.signal));
519
- } catch (error) {
520
- return Promise.reject(error);
521
- }
522
- }
523
-
524
- // Link the request object's abort method (Ext.Ajax, XHR, etc.) to the cancellation signal
525
- this.addAbortListener(this.#_resolveAbortMethod(requestPromise), abortController.signal);
526
-
527
- // Cancel previous request with the same identifier if it exists
528
- if (!options.noCancel) this.cancel(requestId);
529
-
530
- // Create a wrapper promise that will be resolved/rejected based on the request
531
- let resolveWrapper, rejectWrapper;
532
- const wrapperPromise = new Promise((resolve, reject) => {
533
- resolveWrapper = resolve;
534
- rejectWrapper = reject;
535
- });
536
-
537
- /**
538
- * @type {import('./index.d.ts').ActiveRequest}
539
- */
540
- const requestInfo = {
541
- promise: requestPromise,
542
- abortController: abortController,
543
- cancelToken: options.cancelToken || null,
544
- resolveWrapper: resolveWrapper,
545
- rejectWrapper: rejectWrapper,
546
- isCancelled: false,
547
- };
548
-
549
- this.activeRequests.set(requestId, requestInfo);
550
-
551
- // Handle request promise completion
552
- if (requestPromise && typeof requestPromise.then === 'function') {
553
- try {
554
- let req = requestPromise.then((result) => {
555
- if (this.activeRequests.get(requestId) !== requestInfo) return;
556
- this.#_completeRequest(requestId, resolveWrapper, result, requestInfo.isCancelled);
557
- });
558
- if (req.catch)
559
- req.catch((error) => {
560
- onError(this, error);
561
- });
562
- } catch (error) {
563
- onError(this, error);
564
- }
565
- function onError(scope, error) {
566
- // Check if this requestInfo is still the active one, or if it was cancelled
567
- if (scope.activeRequests.get(requestId) !== requestInfo) return;
568
- if (requestInfo.isCancelled) {
569
- // Already cancelled: let cancel() handle cleanup and reject the wrapper promise
570
- scope.cancel(requestId);
571
- return;
572
- }
573
- // Only delete if this is still the active request
574
- scope.#_deleteRequest(requestId, rejectWrapper, error);
575
- }
576
- } else {
577
- // Non-promise (Ext.Ajax request object, raw XHR, etc.). Keep tracked until the
578
- // underlying XHR finishes so a later duplicate can still cancel it.
579
- const xhr =
580
- requestPromise &&
581
- (requestPromise.xhr ||
582
- (typeof XMLHttpRequest !== 'undefined' && requestPromise instanceof XMLHttpRequest
583
- ? requestPromise
584
- : null));
585
- const finish = () => {
586
- if (this.activeRequests.get(requestId) !== requestInfo) return;
587
- this.#_completeRequest(requestId, resolveWrapper, requestPromise, requestInfo.isCancelled);
588
- };
589
- if (xhr && typeof xhr.addEventListener === 'function') {
590
- xhr.addEventListener('loadend', finish);
591
- } else {
592
- setTimeout(finish, 0);
593
- }
594
- }
595
- 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);
596
697
  }
698
+ }
699
+ return wrapperPromise;
597
700
  }
598
701
 
599
702
  exports.RequestManager = RequestManager;