UNPKG

@hyper-fetch/core

Version:

Cache, Queue and Persist your requests no matter if you are online or offline!

1,396 lines 141 kB
//#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