@enegalan/request-manager 1.1.0 → 1.1.2

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