@dvcol/common-utils
Version:
Typescript library for common utility functions and constants
176 lines (173 loc) • 6.17 kB
JavaScript
import {
getUUID
} from "./chunk-F2ODECNN.js";
// lib/http/utils/fetch.utils.ts
var CancellablePromise = class _CancellablePromise extends Promise {
_controller = new AbortController();
_debug = false;
id = `cancellable-promise-${getUUID()}`;
/**
* Gets the AbortSignal associated with the AbortController, allowing external code to listen for cancellation events.
*
* @type {AbortSignal}
* @memberof CancellableFetch
*/
get signal() {
return this._controller.signal;
}
/**
* Sets the AbortSignal associated with the AbortController, allowing external code to listen for cancellation events.
* @param signal - The AbortSignal to associate with the AbortController.
*
* @memberof CancellableFetch
*/
set signal(signal) {
this._controller.signal.onabort = signal.onabort;
}
/**
* Cancels the currently active fetch request by aborting the associated AbortController.
*
* @param {unknown} [reason] - An optional reason for aborting the fetch request.
* @memberof CancellableFetch
*/
cancel(reason) {
if (this.signal.aborted) return;
if (this._debug) console.error("Promise was cancelled.", { id: this.id });
this._controller.abort(reason);
}
then(...params) {
const promise = super.then(...params);
promise.cancel = this.cancel.bind(this);
promise.signal = this.signal;
return promise;
}
catch(onRejected) {
const promise = super.catch(onRejected);
promise.cancel = this.cancel.bind(this);
promise.signal = this.signal;
return promise;
}
finally(onFinally) {
const promise = super.finally(onFinally);
promise.cancel = this.cancel.bind(this);
promise.signal = this.signal;
return promise;
}
static isCancellable(promise) {
return promise instanceof _CancellablePromise || "cancel" in promise && "signal" in promise;
}
// eslint-disable-next-line no-dupe-class-members -- To allow method overloading
static resolve(value) {
return _CancellablePromise.from(Promise.resolve(value));
}
static from(promise) {
if (_CancellablePromise.isCancellable(promise)) return promise;
let _reject;
const cancellablePromise = new _CancellablePromise((resolve, reject) => {
_reject = reject;
promise.then(resolve).catch(reject);
});
const onAbort = () => _reject(new DOMException("The operation was aborted.", "AbortError"));
cancellablePromise.signal.addEventListener("abort", onAbort);
return cancellablePromise;
}
};
var CancellableFetch = class _CancellableFetch {
_controller = new AbortController();
_debug;
id = `cancellable-promise-${getUUID()}`;
/**
* Gets the AbortSignal associated with the AbortController, allowing external code to listen for cancellation events.
*
* @readonly
* @type {AbortSignal}
* @memberof CancellableFetch
*/
get signal() {
return this._controller.signal;
}
/**
* Creates an instance of CancellableFetch.
*
* @param {boolean} [debug=false] - Indicates whether debug information should be logged when a fetch request is cancelled.
* @memberof CancellableFetch
*/
constructor(debug = false) {
this._debug = debug;
}
/**
* Performs a fetch request with optional cancellation support.
*
* @param {RequestInfo | URL} input - The resource URL or a Request object.
* @param {Omit<RequestInit, 'signal'>} [init] - Optional RequestInit options, excluding the 'signal' property.
* @returns {Promise<Response>} A Promise that resolves to the Response to that request.
* @memberof CancellableFetch
* @throws {Error} If an error occurs during the fetch operation, other than an 'AbortError'.
*/
async fetch(input, init) {
try {
return await fetch(input, { ...init, signal: this._controller.signal });
} catch (error) {
if (error instanceof Error && error?.name === "AbortError") {
if (this._debug) console.debug("Fetch request was cancelled.", { input, init, error });
return Promise.reject(error);
}
throw error;
}
}
/**
* Performs a fetch request using a new instance of CancellableFetch.
*
* @static
* @template T - The type of the promise result.
* @param {RequestInfo | URL} input - The resource URL or a Request object.
* @param {Omit<RequestInit, 'signal'>} [init] - Optional RequestInit options, excluding the 'signal' property.
* @returns {CancellablePromise<T>} A cancellable promise that resolves to the Response to the fetch request.
* @memberof CancellableFetch
*/
static fetch(input, init) {
const cancellableFetch = new _CancellableFetch();
const promise = cancellableFetch.fetch(input, init);
const attributes = ["id", "signal"];
const bindAttributes = (_promise) => {
attributes.forEach((prop) => {
if (prop in _promise) return;
Object.defineProperty(_promise, prop, { get: () => cancellableFetch[prop] });
});
};
const methods = ["then", "catch", "finally"];
const bindMethods = (_promise) => {
methods.forEach((method) => {
const _method = _promise[method];
_promise[method] = (...params) => {
const methodPromise = _method.bind(_promise)(...params);
if (CancellablePromise.isCancellable(methodPromise)) return methodPromise;
bindAttributes(methodPromise);
bindMethods(methodPromise);
methodPromise.cancel = (...args) => cancellableFetch.cancel(...args);
return methodPromise;
};
});
};
bindAttributes(promise);
bindMethods(promise);
promise.cancel = (...args) => cancellableFetch.cancel(...args);
return promise;
}
/**
* Cancels the currently active fetch request by aborting the associated AbortController.
*
* @param {unknown} [reason] - An optional reason for aborting the fetch request.
*
* @memberof CancellableFetch
*/
cancel(reason) {
if (this.signal.aborted) return;
if (this._debug) console.debug("Fetch request was cancelled.", { id: this.id, reason });
this._controller.abort(reason);
}
};
export {
CancellablePromise,
CancellableFetch
};