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