@hyper-fetch/core
Version:
Cache, Queue and Persist your requests no matter if you are online or offline!
1,396 lines • 141 kB
JavaScript
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
//#region src/plugin/plugin.ts
/**
* Base class for plugins that hook into the request, cache, dispatcher, and adapter lifecycle.
* Extend this to build devtools, logging, analytics, or custom side-effect plugins.
*/
var Plugin = class {
name;
data;
client = void 0;
pluginMethods = {};
constructor(config) {
this.config = config;
this.name = config.name;
if (config.data) this.data = config.data;
}
/** Bind the plugin to a client instance. Called automatically when the plugin is added via `client.addPlugin()`. */
initialize = (client) => {
this.client = client;
return this;
};
/** Invoke a registered plugin method by name. Used internally by the client to dispatch lifecycle events. */
trigger = (method, data) => {
const callback = this.pluginMethods[method];
if (callback) callback(data);
};
/**
* Callback that will be executed when plugin is mounted
*/
onMount = (callback) => {
this.pluginMethods.onMount = callback;
return this;
};
/**
* Callback that will be executed when plugin is unmounted
*/
onUnmount = (callback) => {
this.pluginMethods.onUnmount = callback;
return this;
};
/**
* Callback that will be executed when request is created
*/
onRequestCreate = (callback) => {
this.pluginMethods.onRequestCreate = callback;
return this;
};
/**
* Callback that will be executed when request gets triggered
*/
onRequestTrigger = (callback) => {
this.pluginMethods.onRequestTrigger = callback;
return this;
};
/**
* Callback that will be executed when request starts
*/
onRequestStart = (callback) => {
this.pluginMethods.onRequestStart = callback;
return this;
};
/**
* Callback that will be executed when response is successful
*/
onRequestSuccess = (callback) => {
this.pluginMethods.onRequestSuccess = callback;
return this;
};
/**
* Callback that will be executed when response is failed
*/
onRequestError = (callback) => {
this.pluginMethods.onRequestError = callback;
return this;
};
/**
* Callback that will be executed when response is finished
*/
onRequestFinished = (callback) => {
this.pluginMethods.onRequestFinished = callback;
return this;
};
/** Called when the dispatcher storage is fully cleared */
onDispatcherCleared = (callback) => {
this.pluginMethods.onDispatcherCleared = callback;
return this;
};
/** Called when a dispatcher queue has no more pending requests */
onDispatcherQueueDrained = (callback) => {
this.pluginMethods.onDispatcherQueueDrained = callback;
return this;
};
/** Called when a dispatcher queue transitions to running, paused, or stopped */
onDispatcherQueueRunning = (callback) => {
this.pluginMethods.onDispatcherQueueRunning = callback;
return this;
};
/** Called when a new item is added to a dispatcher queue */
onDispatcherItemAdded = (callback) => {
this.pluginMethods.onDispatcherItemAdded = callback;
return this;
};
/** Called when an item is removed from a dispatcher queue */
onDispatcherItemDeleted = (callback) => {
this.pluginMethods.onDispatcherItemDeleted = callback;
return this;
};
/** Called when a new dispatcher queue is created */
onDispatcherQueueCreated = (callback) => {
this.pluginMethods.onDispatcherQueueCreated = callback;
return this;
};
/** Called when an entire dispatcher queue is cleared */
onDispatcherQueueCleared = (callback) => {
this.pluginMethods.onDispatcherQueueCleared = callback;
return this;
};
/** Called when a cache entry is created or updated */
onCacheItemChange = (callback) => {
this.pluginMethods.onCacheItemChange = callback;
return this;
};
/** Called when a cache entry is deleted */
onCacheItemDelete = (callback) => {
this.pluginMethods.onCacheItemDelete = callback;
return this;
};
/** Called when the adapter fetcher is about to execute a request */
onAdapterFetch = (callback) => {
this.pluginMethods.onAdapterFetch = callback;
return this;
};
};
//#endregion
//#region src/utils/uuid.utils.ts
var getUniqueRequestId = (request) => {
return `${request.queryKey}_${Date.now().toString(36)}_${Math.random().toString(36).substring(2)}`;
};
//#endregion
//#region src/utils/emitter.utils.ts
var import_events = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
var R = typeof Reflect === "object" ? Reflect : null;
var ReflectApply = R && typeof R.apply === "function" ? R.apply : function ReflectApply(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args);
};
var ReflectOwnKeys;
if (R && typeof R.ownKeys === "function") ReflectOwnKeys = R.ownKeys;
else if (Object.getOwnPropertySymbols) ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target));
};
else ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target);
};
function ProcessEmitWarning(warning) {
if (console && console.warn) console.warn(warning);
}
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
return value !== value;
};
function EventEmitter() {
EventEmitter.init.call(this);
}
module.exports = EventEmitter;
module.exports.once = once;
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = void 0;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype._maxListeners = void 0;
var defaultMaxListeners = 10;
function checkListener(listener) {
if (typeof listener !== "function") throw new TypeError("The \"listener\" argument must be of type Function. Received type " + typeof listener);
}
Object.defineProperty(EventEmitter, "defaultMaxListeners", {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
if (typeof arg !== "number" || arg < 0 || NumberIsNaN(arg)) throw new RangeError("The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received " + arg + ".");
defaultMaxListeners = arg;
}
});
EventEmitter.init = function() {
if (this._events === void 0 || this._events === Object.getPrototypeOf(this)._events) {
this._events = Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || void 0;
};
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== "number" || n < 0 || NumberIsNaN(n)) throw new RangeError("The value of \"n\" is out of range. It must be a non-negative number. Received " + n + ".");
this._maxListeners = n;
return this;
};
function _getMaxListeners(that) {
if (that._maxListeners === void 0) return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return _getMaxListeners(this);
};
EventEmitter.prototype.emit = function emit(type) {
var args = [];
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = type === "error";
var events = this._events;
if (events !== void 0) doError = doError && events.error === void 0;
else if (!doError) return false;
if (doError) {
var er;
if (args.length > 0) er = args[0];
if (er instanceof Error) throw er;
var err = /* @__PURE__ */ new Error("Unhandled error." + (er ? " (" + er.message + ")" : ""));
err.context = er;
throw err;
}
var handler = events[type];
if (handler === void 0) return false;
if (typeof handler === "function") ReflectApply(handler, this, args);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i) ReflectApply(listeners[i], this, args);
}
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
checkListener(listener);
events = target._events;
if (events === void 0) {
events = target._events = Object.create(null);
target._eventsCount = 0;
} else {
if (events.newListener !== void 0) {
target.emit("newListener", type, listener.listener ? listener.listener : listener);
events = target._events;
}
existing = events[type];
}
if (existing === void 0) {
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === "function") existing = events[type] = prepend ? [listener, existing] : [existing, listener];
else if (prepend) existing.unshift(listener);
else existing.push(listener);
m = _getMaxListeners(target);
if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true;
var w = /* @__PURE__ */ new Error("Possible EventEmitter memory leak detected. " + existing.length + " " + String(type) + " listeners added. Use emitter.setMaxListeners() to increase limit");
w.name = "MaxListenersExceededWarning";
w.emitter = target;
w.type = type;
w.count = existing.length;
ProcessEmitWarning(w);
}
}
return target;
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener = function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function onceWrapper() {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
if (arguments.length === 0) return this.listener.call(this.target);
return this.listener.apply(this.target, arguments);
}
}
function _onceWrap(target, type, listener) {
var state = {
fired: false,
wrapFn: void 0,
target,
type,
listener
};
var wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
EventEmitter.prototype.once = function once(type, listener) {
checkListener(listener);
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) {
checkListener(listener);
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.removeListener = function removeListener(type, listener) {
var list, events, position, i, originalListener;
checkListener(listener);
events = this._events;
if (events === void 0) return this;
list = events[type];
if (list === void 0) return this;
if (list === listener || list.listener === listener) if (--this._eventsCount === 0) this._events = Object.create(null);
else {
delete events[type];
if (events.removeListener) this.emit("removeListener", type, list.listener || listener);
}
else if (typeof list !== "function") {
position = -1;
for (i = list.length - 1; i >= 0; i--) if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
if (position < 0) return this;
if (position === 0) list.shift();
else spliceOne(list, position);
if (list.length === 1) events[type] = list[0];
if (events.removeListener !== void 0) this.emit("removeListener", type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
var listeners, events = this._events, i;
if (events === void 0) return this;
if (events.removeListener === void 0) {
if (arguments.length === 0) {
this._events = Object.create(null);
this._eventsCount = 0;
} else if (events[type] !== void 0) if (--this._eventsCount === 0) this._events = Object.create(null);
else delete events[type];
return this;
}
if (arguments.length === 0) {
var keys = Object.keys(events);
var key;
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === "removeListener") continue;
this.removeAllListeners(key);
}
this.removeAllListeners("removeListener");
this._events = Object.create(null);
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === "function") this.removeListener(type, listeners);
else if (listeners !== void 0) for (i = listeners.length - 1; i >= 0; i--) this.removeListener(type, listeners[i]);
return this;
};
function _listeners(target, type, unwrap) {
var events = target._events;
if (events === void 0) return [];
var evlistener = events[type];
if (evlistener === void 0) return [];
if (typeof evlistener === "function") return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === "function") return emitter.listenerCount(type);
else return listenerCount.call(emitter, type);
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events = this._events;
if (events !== void 0) {
var evlistener = events[type];
if (typeof evlistener === "function") return 1;
else if (evlistener !== void 0) return evlistener.length;
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
};
function arrayClone(arr, n) {
var copy = new Array(n);
for (var i = 0; i < n; ++i) copy[i] = arr[i];
return copy;
}
function spliceOne(list, index) {
for (; index + 1 < list.length; index++) list[index] = list[index + 1];
list.pop();
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) ret[i] = arr[i].listener || arr[i];
return ret;
}
function once(emitter, name) {
return new Promise(function(resolve, reject) {
function errorListener(err) {
emitter.removeListener(name, resolver);
reject(err);
}
function resolver() {
if (typeof emitter.removeListener === "function") emitter.removeListener("error", errorListener);
resolve([].slice.call(arguments));
}
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
if (name !== "error") addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
});
}
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
if (typeof emitter.on === "function") eventTargetAgnosticAddListener(emitter, "error", handler, flags);
}
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
if (typeof emitter.on === "function") if (flags.once) emitter.once(name, listener);
else emitter.on(name, listener);
else if (typeof emitter.addEventListener === "function") emitter.addEventListener(name, function wrapListener(arg) {
if (flags.once) emitter.removeEventListener(name, wrapListener);
listener(arg);
});
else throw new TypeError("The \"emitter\" argument must be of type EventEmitter. Received type " + typeof emitter);
}
})))());
var getListenName = (event) => `listen_${String(event)}`;
var EventEmitter = class extends import_events.default {
emitCallbacks = [];
constructor(options) {
super(options);
}
emit(type, data, isTriggeredExternally) {
const params = [type, data];
if (isTriggeredExternally) params.push(isTriggeredExternally);
this.emitCallbacks?.forEach((callback) => callback(...params));
return super.emit(...params);
}
onEmit = (callback) => {
this.emitCallbacks?.push(callback);
return () => {
this.emitCallbacks = this.emitCallbacks?.filter((cb) => cb !== callback);
};
};
onListener = (event, listener) => {
super.on(getListenName(event), listener);
return () => {
super.off(getListenName(event), listener);
};
};
on = (event, listener) => {
super.on(event, listener);
super.emit(getListenName(event), super.listeners(event).length);
return this;
};
off = (event, listener) => {
super.off(event, listener);
super.emit(getListenName(event), super.listeners(event).length);
return this;
};
addListener = this.on;
removeListener = this.off;
};
//#endregion
//#region src/managers/app/app.manager.ts
/**
* App manager handles main application states - focus and online. Those two values can answer questions:
* - Is the tab or current view instance focused and visible for user?
* - Is our application online or offline?
* With the app manager it is not a problem to get the valid answer for this question.
*
* @caution
* Make sure to apply valid focus/online handlers for different environments like for example for native mobile applications.
*/
var AppManager = class {
emitter = new EventEmitter();
events = getAppManagerEvents(this.emitter);
isBrowser;
isOnline;
isFocused;
constructor(options) {
this.options = options;
this.emitter?.setMaxListeners(1e3);
const { initiallyFocused = appManagerInitialOptions.initiallyFocused, initiallyOnline = appManagerInitialOptions.initiallyOnline } = this.options || appManagerInitialOptions;
this.setInitialFocus(initiallyFocused);
this.setInitialOnline(initiallyOnline);
this.isBrowser = hasDocument();
}
initialize = () => {
const { focusEvent = appManagerInitialOptions.focusEvent, onlineEvent = appManagerInitialOptions.onlineEvent } = this.options || appManagerInitialOptions;
focusEvent(this.setFocused);
onlineEvent(this.setOnline);
};
setInitialFocus = async (initValue) => {
if (typeof initValue === "function") {
this.isFocused = false;
this.isFocused = await initValue();
} else this.isFocused = initValue;
};
setInitialOnline = async (initValue) => {
if (typeof initValue === "function") {
this.isOnline = false;
this.isOnline = await initValue();
} else this.isOnline = initValue;
};
setFocused = (isFocused) => {
this.isFocused = isFocused;
if (isFocused) this.events.emitFocus();
else this.events.emitBlur();
};
setOnline = (isOnline) => {
this.isOnline = isOnline;
if (isOnline) this.events.emitOnline();
else this.events.emitOffline();
};
};
//#endregion
//#region src/managers/app/app.manager.utils.ts
var hasWindow = () => {
try {
return Boolean(window && window.addEventListener);
} catch (err) {
/* istanbul ignore next */
return false;
}
};
var hasDocument = () => {
try {
return Boolean(hasWindow() && window.document && window.document.addEventListener);
} catch (err) {
/* istanbul ignore next */
return false;
}
};
var onWindowEvent = (key, listener, options) => {
/* istanbul ignore next */
if (hasWindow()) {
window.addEventListener(key, listener, options);
return () => window.removeEventListener(key, listener, options);
}
/* istanbul ignore next */
return () => null;
};
var onDocumentEvent = (key, listener, options) => {
/* istanbul ignore next */
if (hasDocument()) {
window.document.addEventListener(key, listener, options);
return () => window.document.removeEventListener(key, listener, options);
}
/* istanbul ignore next */
return () => null;
};
//#endregion
//#region src/managers/app/app.manager.events.ts
/** Create event emitters and listeners for application-level focus, blur, online, and offline state changes. */
var getAppManagerEvents = (emitter) => ({
emitFocus: () => {
emitter.emit(AppEvents.FOCUS);
},
emitBlur: () => {
emitter.emit(AppEvents.BLUR);
},
emitOnline: () => {
emitter.emit(AppEvents.ONLINE);
},
emitOffline: () => {
emitter.emit(AppEvents.OFFLINE);
},
onFocus: (callback) => {
emitter.on(AppEvents.FOCUS, callback);
return () => emitter.removeListener(AppEvents.FOCUS, callback);
},
onBlur: (callback) => {
emitter.on(AppEvents.BLUR, callback);
return () => emitter.removeListener(AppEvents.BLUR, callback);
},
onOnline: (callback) => {
emitter.on(AppEvents.ONLINE, callback);
return () => emitter.removeListener(AppEvents.ONLINE, callback);
},
onOffline: (callback) => {
emitter.on(AppEvents.OFFLINE, callback);
return () => emitter.removeListener(AppEvents.OFFLINE, callback);
}
});
//#endregion
//#region src/managers/app/app.manager.constants.ts
var AppEvents = /* @__PURE__ */ function(AppEvents) {
AppEvents["FOCUS"] = "focus";
AppEvents["BLUR"] = "blur";
AppEvents["ONLINE"] = "online";
AppEvents["OFFLINE"] = "offline";
return AppEvents;
}({});
var appManagerInitialOptions = {
initiallyFocused: true,
initiallyOnline: true,
focusEvent: (setFocused) => {
onDocumentEvent("visibilitychange", () => setFocused(true));
onWindowEvent("focus", () => setFocused(true));
onWindowEvent("blur", () => setFocused(false));
},
onlineEvent: (setOnline) => {
onWindowEvent("online", () => setOnline(true));
onWindowEvent("offline", () => setOnline(false));
}
};
//#endregion
//#region src/managers/request/request.manager.ts
/**
* **Request Manager** is used to emit `request lifecycle events` like - request start, request end, upload and download progress.
* It is also the place of `request aborting` system, here we store all the keys and controllers that are isolated for each client instance.
*/
var RequestManager = class {
emitter = new EventEmitter();
events = getRequestManagerEvents(this.emitter);
constructor() {
this.emitter?.setMaxListeners(1e3);
}
abortControllers = /* @__PURE__ */ new Map();
addAbortController = (abortKey, requestId) => {
let abortGroup = this.abortControllers.get(abortKey);
if (!abortGroup) {
const newAbortGroup = /* @__PURE__ */ new Map();
abortGroup = newAbortGroup;
this.abortControllers.set(abortKey, newAbortGroup);
}
const abortController = abortGroup.get(requestId);
if (!abortController || abortController.signal.aborted) abortGroup.set(requestId, new AbortController());
};
getAbortController = (abortKey, requestId) => {
return this.abortControllers.get(abortKey)?.get(requestId);
};
removeAbortController = (abortKey, requestId) => {
this.abortControllers.get(abortKey)?.delete(requestId);
};
useAbortController = (abortKey, requestId) => {
(this.abortControllers.get(abortKey)?.get(requestId))?.abort();
};
abortByKey = (abortKey) => {
const controllers = this.abortControllers.get(abortKey);
if (controllers) Array.from(controllers.entries()).forEach(([key]) => {
this.useAbortController(abortKey, key);
});
};
abortByRequestId = (abortKey, requestId) => {
this.useAbortController(abortKey, requestId);
};
abortAll = () => {
Array.from(this.abortControllers.entries()).forEach(([abortKey, value]) => {
Array.from(value.entries()).forEach(([requestId]) => {
this.useAbortController(abortKey, requestId);
});
});
};
};
//#endregion
//#region src/managers/request/request.manager.utils.ts
var getLoadingKey = () => `loading-event-any`;
var getLoadingByQueryKey = (queryKey) => `${queryKey}-loading-event`;
var getLoadingByCacheKey = (cacheKey) => `${cacheKey}-loading-cache-event`;
var getLoadingByIdKey = (id) => `${id}-loading-event-by-id`;
var getRemoveKey = () => `remove-event-any`;
var getRemoveByQueryKey = (queryKey) => `${queryKey}-remove-event`;
var getRemoveByIdKey = (id) => `${id}-remove-event-by-id`;
var getAbortKey = () => `request-abort-any`;
var getAbortByAbortKey = (abortKey) => `${abortKey}-request-abort`;
var getAbortByIdKey = (id) => `${id}-request-abort-by-id`;
var getResponseKey = () => `response-any`;
var getResponseByCacheKey = (cacheKey) => `${cacheKey}-response`;
var getResponseByIdKey = (id) => `${id}-response-by-id`;
var getRequestStartKey = () => `request-start-any`;
var getRequestStarByQueryKey = (queryKey) => `${queryKey}-request-start`;
var getRequestStartByIdKey = (id) => `${id}-request-start-by-id`;
var getResponseStartKey = () => `response-start-any`;
var getResponseStartByQueryKey = (queryKey) => `${queryKey}-response-start`;
var getResponseStartByIdKey = (id) => `${id}-response-start-by-id`;
var getUploadProgressKey = () => `request-progress-any`;
var getUploadProgressByQueryKey = (queryKey) => `${queryKey}-request-progress`;
var getUploadProgressByIdKey = (id) => `${id}-request-progress-by-id`;
var getDownloadProgressKey = () => `response-progress-any`;
var getDownloadProgressByQueryKey = (queryKey) => `${queryKey}-response-progress`;
var getDownloadProgressByIdKey = (id) => `${id}-response-progress-by-id`;
var getRequestDeduplicatedKey = () => `request-deduplicated-any`;
var getRequestDeduplicatedByQueryKey = (queryKey) => `${queryKey}-query-request-deduplicated`;
var getRequestDeduplicatedByCacheKey = (cacheKey) => `${cacheKey}-cache-request-deduplicated`;
var getRequestDeduplicatedByIdKey = (id) => `${id}-request-deduplicated-by-id`;
//#endregion
//#region src/managers/request/request.manager.events.ts
/** Create event emitters and listeners for the request lifecycle: loading, progress, response, abort, deduplication, and removal. */
var getRequestManagerEvents = (emitter) => ({
emitDeduplicated: (data, isTriggeredExternally = false) => {
emitter.emit(getRequestDeduplicatedKey(), data, isTriggeredExternally);
emitter.emit(getRequestDeduplicatedByIdKey(data.requestId), data, isTriggeredExternally);
emitter.emit(getRequestDeduplicatedByCacheKey(data.request.cacheKey), data, isTriggeredExternally);
emitter.emit(getRequestDeduplicatedByQueryKey(data.request.queryKey), data, isTriggeredExternally);
},
emitLoading: (data, isTriggeredExternally = false) => {
emitter.emit(getLoadingKey(), data, isTriggeredExternally);
emitter.emit(getLoadingByIdKey(data.requestId), data, isTriggeredExternally);
emitter.emit(getLoadingByCacheKey(data.request.cacheKey), data, isTriggeredExternally);
emitter.emit(getLoadingByQueryKey(data.request.queryKey), data, isTriggeredExternally);
},
emitRequestStart: (data, isTriggeredExternally = false) => {
emitter.emit(getRequestStartKey(), data, isTriggeredExternally);
emitter.emit(getRequestStartByIdKey(data.requestId), data, isTriggeredExternally);
emitter.emit(getRequestStarByQueryKey(data.request.queryKey), data, isTriggeredExternally);
},
emitResponseStart: (data, isTriggeredExternally = false) => {
emitter.emit(getResponseStartKey(), data, isTriggeredExternally);
emitter.emit(getResponseStartByIdKey(data.requestId), data, isTriggeredExternally);
emitter.emit(getResponseStartByQueryKey(data.request.queryKey), data, isTriggeredExternally);
},
emitUploadProgress: (data, isTriggeredExternally = false) => {
emitter.emit(getUploadProgressKey(), data, isTriggeredExternally);
emitter.emit(getUploadProgressByIdKey(data.requestId), data, isTriggeredExternally);
emitter.emit(getUploadProgressByQueryKey(data.request.queryKey), data, isTriggeredExternally);
},
emitDownloadProgress: (data, isTriggeredExternally = false) => {
emitter.emit(getDownloadProgressKey(), data, isTriggeredExternally);
emitter.emit(getDownloadProgressByIdKey(data.requestId), data, isTriggeredExternally);
emitter.emit(getDownloadProgressByQueryKey(data.request.queryKey), data, isTriggeredExternally);
},
emitResponse: (data, isTriggeredExternally = false) => {
emitter.emit(getResponseKey(), data, isTriggeredExternally);
emitter.emit(getResponseByIdKey(data.requestId), data, isTriggeredExternally);
emitter.emit(getResponseByCacheKey(data.request.cacheKey), data, isTriggeredExternally);
},
emitAbort: (data, isTriggeredExternally = false) => {
emitter.emit(getAbortKey(), data, isTriggeredExternally);
emitter.emit(getAbortByIdKey(data.requestId), data, isTriggeredExternally);
emitter.emit(getAbortByAbortKey(data.request.abortKey), data, isTriggeredExternally);
},
emitRemove: (data, isTriggeredExternally = false) => {
emitter.emit(getRemoveKey(), data, isTriggeredExternally);
emitter.emit(getRemoveByIdKey(data.requestId), data, isTriggeredExternally);
emitter.emit(getRemoveByQueryKey(data.request.queryKey), data, isTriggeredExternally);
},
onDeduplicated: (callback) => {
emitter.on(getRequestDeduplicatedKey(), callback);
return () => emitter.removeListener(getRequestDeduplicatedKey(), callback);
},
onDeduplicatedByQueue: (queryKey, callback) => {
emitter.on(getRequestDeduplicatedByQueryKey(queryKey), callback);
return () => emitter.removeListener(getRequestDeduplicatedByQueryKey(queryKey), callback);
},
onDeduplicatedByCache: (cacheKey, callback) => {
emitter.on(getRequestDeduplicatedByCacheKey(cacheKey), callback);
return () => emitter.removeListener(getRequestDeduplicatedByCacheKey(cacheKey), callback);
},
onDeduplicatedById: (requestId, callback) => {
emitter.on(getRequestDeduplicatedByIdKey(requestId), callback);
return () => emitter.removeListener(getRequestDeduplicatedByIdKey(requestId), callback);
},
onLoading: (callback) => {
emitter.on(getLoadingKey(), callback);
return () => emitter.removeListener(getLoadingKey(), callback);
},
onLoadingByQueue: (queryKey, callback) => {
emitter.on(getLoadingByQueryKey(queryKey), callback);
return () => emitter.removeListener(getLoadingByQueryKey(queryKey), callback);
},
onLoadingByCache: (cacheKey, callback) => {
emitter.on(getLoadingByCacheKey(cacheKey), callback);
return () => emitter.removeListener(getLoadingByCacheKey(cacheKey), callback);
},
onLoadingById: (requestId, callback) => {
emitter.on(getLoadingByIdKey(requestId), callback);
return () => emitter.removeListener(getLoadingByIdKey(requestId), callback);
},
onRequestStart: (callback) => {
emitter.on(getRequestStartKey(), callback);
return () => emitter.removeListener(getRequestStartKey(), callback);
},
onRequestStartByQueue: (queryKey, callback) => {
emitter.on(getRequestStarByQueryKey(queryKey), callback);
return () => emitter.removeListener(getRequestStarByQueryKey(queryKey), callback);
},
onRequestStartById: (requestId, callback) => {
emitter.on(getRequestStartByIdKey(requestId), callback);
return () => emitter.removeListener(getRequestStartByIdKey(requestId), callback);
},
onResponseStart: (callback) => {
emitter.on(getResponseStartKey(), callback);
return () => emitter.removeListener(getResponseStartKey(), callback);
},
onResponseStartByQueue: (queryKey, callback) => {
emitter.on(getResponseStartByQueryKey(queryKey), callback);
return () => emitter.removeListener(getResponseStartByQueryKey(queryKey), callback);
},
onResponseStartById: (requestId, callback) => {
emitter.on(getResponseStartByIdKey(requestId), callback);
return () => emitter.removeListener(getResponseStartByIdKey(requestId), callback);
},
onUploadProgress: (callback) => {
emitter.on(getUploadProgressKey(), callback);
return () => emitter.removeListener(getUploadProgressKey(), callback);
},
onUploadProgressByQueue: (queryKey, callback) => {
emitter.on(getUploadProgressByQueryKey(queryKey), callback);
return () => emitter.removeListener(getUploadProgressByQueryKey(queryKey), callback);
},
onUploadProgressById: (requestId, callback) => {
emitter.on(getUploadProgressByIdKey(requestId), callback);
return () => emitter.removeListener(getUploadProgressByIdKey(requestId), callback);
},
onDownloadProgress: (callback) => {
emitter.on(getDownloadProgressKey(), callback);
return () => emitter.removeListener(getDownloadProgressKey(), callback);
},
onDownloadProgressByQueue: (queryKey, callback) => {
emitter.on(getDownloadProgressByQueryKey(queryKey), callback);
return () => emitter.removeListener(getDownloadProgressByQueryKey(queryKey), callback);
},
onDownloadProgressById: (requestId, callback) => {
emitter.on(getDownloadProgressByIdKey(requestId), callback);
return () => emitter.removeListener(getDownloadProgressByIdKey(requestId), callback);
},
onResponse: (callback) => {
emitter.on(getResponseKey(), callback);
return () => emitter.removeListener(getResponseKey(), callback);
},
onResponseByCache: (cacheKey, callback) => {
emitter.on(getResponseByCacheKey(cacheKey), callback);
return () => emitter.removeListener(getResponseByCacheKey(cacheKey), callback);
},
onResponseById: (requestId, callback) => {
emitter.on(getResponseByIdKey(requestId), callback);
return () => emitter.removeListener(getResponseByIdKey(requestId), callback);
},
onAbort: (callback) => {
emitter.on(getAbortKey(), callback);
return () => emitter.removeListener(getAbortKey(), callback);
},
onAbortByKey: (abortKey, callback) => {
emitter.on(getAbortByAbortKey(abortKey), callback);
return () => emitter.removeListener(getAbortByAbortKey(abortKey), callback);
},
onAbortById: (requestId, callback) => {
emitter.on(getAbortByIdKey(requestId), callback);
return () => emitter.removeListener(getAbortByIdKey(requestId), callback);
},
onRemove: (callback) => {
emitter.on(getRemoveKey(), callback);
return () => emitter.removeListener(getRemoveKey(), callback);
},
onRemoveByQueue: (queryKey, callback) => {
emitter.on(getRemoveByQueryKey(queryKey), callback);
return () => emitter.removeListener(getRemoveByQueryKey(queryKey), callback);
},
onRemoveById: (requestId, callback) => {
emitter.on(getRemoveByIdKey(requestId), callback);
return () => emitter.removeListener(getRemoveByIdKey(requestId), callback);
}
});
//#endregion
//#region src/managers/logger/logger.manager.ts
/**
* This class is used across the Hyper Fetch library to provide unified logging system with necessary setup per each client.
* We can set up the logging level based on available values. This manager enable to initialize the logging instance per individual module
* like Client, Request etc. Which can give you better feedback on the logging itself.
*/
var LoggerManager = class {
logger;
level;
modules;
emitter = new import_events.default();
client;
constructor(options) {
this.options = options;
this.emitter?.setMaxListeners(1e3);
this.logger = this.options?.logger || logger;
this.level = this.options?.level || "warning";
this.modules = this.options?.modules;
}
setSeverity = (level) => {
this.level = level;
};
setModules = (modules) => {
this.modules = modules;
};
initialize = (client, module) => {
this.client = client;
return {
error: (data) => this.log({
...data,
level: "error",
module
}),
warning: (data) => this.log({
...data,
level: "warning",
module
}),
info: (data) => this.log({
...data,
level: "info",
module
}),
debug: (data) => this.log({
...data,
level: "debug",
module
})
};
};
log = (data) => {
if (!this.client.debug) return;
if (logLevelOrder.indexOf(this.level) <= logLevelOrder.indexOf(data.level)) return;
if (this.modules && !this.modules.includes(data.module)) return;
this.logger(data);
};
};
//#endregion
//#region src/managers/logger/logger.manager.utils.ts
var getTime = () => {
return `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}`;
};
var getModuleName = (data) => {
if (data.type === "request" || data.type === "response") return `${data.extra.request.endpoint}`;
return `[${data.module}]`;
};
var logger = (log) => {
const styles = loggerStyles[log.level];
const color = loggerColors[log.level];
const message = `%c[${getTime()}]%c${log.title}%c${getModuleName(log)}`;
const style1 = `${styles}${color}font-weight:600;border-radius: 4px 0 0 4px;`;
const style2 = `${styles}${color}font-weight:600;`;
const style3 = `${styles}font-weight:normal;border-radius: 0 4px 4px 0;`;
if (log.extra ? Object.keys(log.extra)?.length : false) {
console.groupCollapsed(message, style1, style2, style3);
console.log(log.extra);
console.groupEnd();
} else console.log(message, styles);
};
//#endregion
//#region src/managers/logger/logger.manager.constants.ts
var logLevelOrder = [
"error",
"warning",
"info",
"debug"
];
var defaultStyles = "padding:2px 5px;";
var loggerStyles = {
error: `${defaultStyles};background:#db252520;`,
warning: `${defaultStyles};background:#e1941e20;`,
info: `${defaultStyles};background:#1e74e120;`,
debug: `${defaultStyles};background:#00000020;`
};
var loggerColors = {
error: "color:#ff3737;",
warning: "color:#ffc107;",
info: "color:#34a8ff;",
debug: "color:#cccccc;"
};
//#endregion
//#region src/adapter/adapter.utils.ts
var TimeoutError = class extends Error {
constructor() {
super("Request timeout");
this.name = "TimeoutError";
}
};
var AbortError = class extends Error {
constructor() {
super("Request aborted");
this.name = "AbortError";
}
};
var DeletedError = class extends Error {
constructor() {
super("Request deleted");
this.name = "DeletedError";
}
};
var RequestProcessingError = class extends Error {
description;
constructor(description) {
super("Request processing error");
this.description = description;
this.name = "RequestProcessingError";
}
};
var UnexpectedError = class extends Error {
description;
constructor(description) {
super("Unexpected error");
this.description = description;
this.name = "UnexpectedError";
}
};
var getErrorMessage = (errorCase) => {
if (errorCase === "timeout") return new TimeoutError();
if (errorCase === "abort") return new AbortError();
if (errorCase === "deleted") return new DeletedError();
if (errorCase === "processing") return new RequestProcessingError();
return new UnexpectedError();
};
var stringifyValue = (response) => {
try {
return JSON.stringify(response);
} catch (err) {
return "";
}
};
var getAdapterHeaders = (request) => {
const isFormData = hasWindow() && request.payload instanceof FormData;
const isObject = typeof request.payload === "object" && request.payload !== null;
const headers = {};
if (!isFormData && isObject) headers["Content-Type"] = "application/json";
Object.assign(headers, request.headers);
return headers;
};
var getAdapterPayload = (data) => {
if (hasWindow() && data instanceof FormData) return data;
return stringifyValue(data);
};
//#endregion
//#region src/request/request.hooks.ts
var HOOK_NAMES = [
"onBeforeSent",
"onRequestStart",
"onResponseStart",
"onUploadProgress",
"onDownloadProgress",
"onResponse",
"onRemove"
];
function createRequestHooks(initial) {
const listeners = {
onBeforeSent: [],
onRequestStart: [],
onResponseStart: [],
onUploadProgress: [],
onDownloadProgress: [],
onResponse: [],
onRemove: []
};
if (initial) HOOK_NAMES.forEach((name) => {
listeners[name] = [...initial[name]];
});
const subscribe = (name) => {
return (cb) => {
listeners[name].push(cb);
return () => {
const idx = listeners[name].indexOf(cb);
if (idx !== -1) listeners[name].splice(idx, 1);
};
};
};
return {
onBeforeSent: subscribe("onBeforeSent"),
onRequestStart: subscribe("onRequestStart"),
onResponseStart: subscribe("onResponseStart"),
onUploadProgress: subscribe("onUploadProgress"),
onDownloadProgress: subscribe("onDownloadProgress"),
onResponse: subscribe("onResponse"),
onRemove: subscribe("onRemove"),
__emit(name, data) {
listeners[name].forEach((cb) => {
cb(data);
});
},
__snapshot() {
const snap = {};
HOOK_NAMES.forEach((name) => {
snap[name] = [...listeners[name]];
});
return snap;
}
};
}
//#endregion
//#region src/constants/http.constants.ts
var HttpMethods = /* @__PURE__ */ function(HttpMethods) {
HttpMethods["GET"] = "GET";
HttpMethods["POST"] = "POST";
HttpMethods["PUT"] = "PUT";
HttpMethods["PATCH"] = "PATCH";
HttpMethods["DELETE"] = "DELETE";
return HttpMethods;
}({});
//#endregion
//#region src/request/request.utils.ts
var scopeKey = (key, scope) => {
return scope ? `${scope}__${key}` : key;
};
var stringifyKey = (value) => {
try {
if (typeof value === "string") return value;
if (value === void 0 || value === null) return "";
const data = JSON.stringify(value);
if (typeof data !== "string") throw new Error();
return data;
} catch (_) {
return "";
}
};
var getProgressValue = ({ loaded, total }) => {
if (!loaded || !total) return 0;
return Number((loaded * 100 / total).toFixed(0));
};
var getRequestEta = (startDate, progressDate, { total, loaded }) => {
const uploadSpeed = loaded / (+progressDate - +startDate || 1);
const totalValue = Math.max(total, loaded);
const sizeLeft = totalValue - loaded;
const estimatedTimeValue = uploadSpeed ? sizeLeft / uploadSpeed : null;
return {
timeLeft: totalValue === loaded ? 0 : estimatedTimeValue,
sizeLeft
};
};
var getProgressData = (requestStartTime, progressDate, progressEvent) => {
const { total, loaded } = progressEvent;
if (Number.isNaN(total) || Number.isNaN(loaded)) return {
progress: 0,
timeLeft: 0,
sizeLeft: 0,
total: 0,
loaded: 0,
startTimestamp: +requestStartTime
};
const { timeLeft, sizeLeft } = getRequestEta(requestStartTime, progressDate, progressEvent);
return {
progress: getProgressValue(progressEvent),
timeLeft,
sizeLeft,
total,
loaded,
startTimestamp: +requestStartTime
};
};
var getSimpleKey = (request) => {
return `${request.method}_${request.requestOptions.endpoint}_${request.cancelable}`;
};
/**
* Cache instance for individual request that collects individual requests responses from
* the same endpoint (they may differ base on the custom key, endpoint params etc)
* @param request
* @param useInitialValues
* @returns
*/
var getRequestKey = (request, useInitialValues) => {
return `${stringifyKey(request.method)}_${useInitialValues ? request.requestOptions.endpoint : stringifyKey(request.endpoint)}_${useInitialValues ? "" : stringifyKey(request.queryParams)}`;
};
var getRequestDispatcher = (request, dispatcherType = "auto") => {
const { fetchDispatcher, submitDispatcher } = request.client;
const isGet = request.method === HttpMethods.GET;
const isFetchDispatcher = dispatcherType === "auto" && isGet || dispatcherType === "fetch";
return [isFetchDispatcher ? fetchDispatcher : submitDispatcher, isFetchDispatcher];
};
var mapResponseForSend = async (request, response) => {
const mapping = request.unstable_responseMapper?.(response);
if (mapping instanceof Promise) return await mapping;
return mapping || response;
};
var sendRequest = async (request, options) => {
const { client } = request;
const { requestManager } = client;
const [dispatcher] = getRequestDispatcher(request, options?.dispatcherType);
let mutationContext;
if (request.optimistic) try {
mutationContext = await request.optimistic({
request,
client: request.client,
payload: request.payload
});
} catch (err) {
return {
data: null,
error: /* @__PURE__ */ new Error(`Optimistic callback failed: ${err instanceof Error ? err.message : err}`),
status: null,
success: false,
extra: client.adapter.defaultExtra,
requestTimestamp: +/* @__PURE__ */ new Date(),
responseTimestamp: +/* @__PURE__ */ new Date()
};
}
return new Promise((resolve) => {
let isResolved = false;
const requestId = dispatcher.add(request);
const { $hooks } = request;
const beforeSentData = {
requestId,
request,
mutationContext
};
options?.onBeforeSent?.(beforeSentData);
$hooks.__emit("onBeforeSent", beforeSentData);
const unmountRequestStart = requestManager.events.onRequestStartById(requestId, (data) => {
const enriched = {
...data,
mutationContext
};
options?.onRequestStart?.(enriched);
$hooks.__emit("onRequestStart", enriched);
});
const unmountResponseStart = requestManager.events.onResponseStartById(requestId, (data) => {
const enriched = {
...data,
mutationContext
};
options?.onResponseStart?.(enriched);
$hooks.__emit("onResponseStart", enriched);
});
const unmountUpload = requestManager.events.onUploadProgressById(requestId, (data) => {
const enriched = {
...data,
mutationContext
};
options?.onUploadProgress?.(enriched);
$hooks.__emit("onUploadProgress", enriched);
});
const unmountDownload = requestManager.events.onDownloadProgressById(requestId, (data) => {
const enriched = {
...data,
mutationContext
};
options?.onDownloadProgress?.(enriched);
$hooks.__emit("onDownloadProgress", enriched);
});
const unmountResponse = requestManager.events.onResponseById(requestId, (values) => {
const { details, response } = values;
isResolved = true;
const enrichedValues = {
...values,
mutationContext
};
const mapping = request.unstable_responseMapper?.(response);
const isOfflineStatus = request.offline && details.isOffline;
const { willRetry } = details;
const handleResponse = (success, data) => {
if (!success && isOfflineStatus) return;
if (!success && willRetry) return;
options?.onResponse?.(enrichedValues);
$hooks.__emit("onResponse", enrichedValues);
resolve(data);
umountAll();
};
if (mapping instanceof Promise) (async () => {
const responseData = await mapping;
const { success } = responseData;
handleResponse(success, responseData);
})();
else {
const data = mapping || response;
const { success } = data;
handleResponse(success, data);
}
});
const unmountRemoveQueueElement = requestManager.events.onRemoveById(requestId, (data) => {
if (!isResolved) {
const enriched = {
...data,
mutationContext
};
options?.onRemove?.(enriched);
$hooks.__emit("onRemove", enriched);
resolve({
data: null,
status: null,
success: false,
error: getErrorMessage("deleted"),
extra: request.client.adapter.defaultExtra,
requestTimestamp: +/* @__PURE__ */ new Date(),
responseTimestamp: +/* @__PURE__ */ new Date()
});
umountAll();
}
});
function umountAll() {
unmountRequestStart();
unmountResponseStart();
unmountUpload();
unmountDownload();
unmountResponse();
unmountRemoveQueueElement();
}
});
};
//#endregion
//#region src/constants/time.constants.ts
var Time = /* @__PURE__ */ function(Time) {
Time[Time["SEC"] = 1e3] = "SEC";
Time[Time["MIN"] = 6e4] = "MIN";
Time[Time["HOUR"] = 36e5] = "HOUR";
Time[Time["DAY"] = 864e5] = "DAY";
Time[Time["WEEK"] = 6048e5] = "WEEK";
Time[Time["MONTH_30"] = 2592e6] = "MONTH_30";
Time[Time["MONTH_31"] = 26784e5] = "MONTH_31";
Time[Time["YEAR"] = 31536e6] = "YEAR";
Time[Time["YEAR_LEAP"] = 316224e5] = "YEAR_LEAP";
return Time;
}({});
//#endregion
//#region src/request/request.ts
/**
* Request is a class that represents a request sent to the server. It contains all the necessary information to make a request, like endpoint, method, headers, data, and much more.
* It is executed at any time via methods like `send` or `exec`.
*
* We can set it up with options like endpoint, method, headers and more.
* We can choose some of advanced settings like cache, invalidation patterns, concurrency, retries and much, much more.
*
* @info We should not use this class directly in the standard development flow.
* We can initialize it using the `createRequest` method on the **Client** class.
*
* @attention The most important thing about the request is that it keeps data in the format that can be dumped.
* This is necessary for the persistence and different dispatcher storage types.
* This class doesn't have any callback methods by design and communicate with dispatcher and cache by events.
*
* It should be serializable to JSON and deserializable back to the class.
* Serialization should not affect the result of the request, so it's methods and functional part should be only syntax sugar for given runtime.
*/
var Request = class Request {
endpoint;
headers;
auth;
method;
params;
payload;
queryParams;
options;
cancelable;
retry;
retryTime;
cacheTime;
cache;
staleTime;
queued;
offline;
abortKey;
cacheKey;
queryKey;
used;
deduplicate;
deduplicateTime;
scope;
/**
* Instance-level lifecycle hooks. These callbacks fire for every `send()` / `exec()` call
* made on this request instance (and its clones), without needing to pass them to `send()` each time.
* Useful for cross-cutting concerns like logging, analytics, or toast notifications.
*
* Each method registers a callback and returns an unsubscribe function.
* Multiple listeners per hook are supported.
*/
$hooks = createRequestHooks();
isMockerEnabled = false;
unstable_mock;
/** @internal */
unstable_payloadMapper;
/** @internal */
unstable_requestMapper;
/** @internal */
unstable_responseMapper;
/** @internal */
retryOnError;
/** @internal */
optimistic;
unstable_hasParams = false;
unstable_hasPayload = false;
unstable_hasQuery = false;
unstable_hasMutationContext = void 0;
updatedAbortKey;
updatedCacheKey;
updatedQueryKey;
constructor(client, requestOptions, initialRequestConfiguration) {
this.client = client;
this.requestOptions = requestOptions;
this.initialRequestConfiguration = initialRequestConfiguration;
const { endpoint, headers, auth = true, method = client.adapter.defaultMethod, options, cancelable = false, retry = 0, retryTime = 500, cacheTime = Time.MIN * 5