UNPKG

@hyper-fetch/core

Version:

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

1,272 lines (1,262 loc) 136 kB
var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/plugin/plugin.ts var Plugin = class { constructor(config) { this.config = config; __publicField(this, "name"); __publicField(this, "data"); __publicField(this, "client"); __publicField(this, "pluginMethods", {}); __publicField(this, "initialize", (client) => { this.client = client; return this; }); __publicField(this, "trigger", (method, data) => { const callback = this.pluginMethods[method]; if (callback) { callback(data); } }); /* ------------------------------------------------------------------------------------------------- * Plugin lifecycle * -----------------------------------------------------------------------------------------------*/ /** * Callback that will be executed when plugin is mounted */ __publicField(this, "onMount", (callback) => { this.pluginMethods.onMount = callback; return this; }); /** * Callback that will be executed when plugin is unmounted */ __publicField(this, "onUnmount", (callback) => { this.pluginMethods.onUnmount = callback; return this; }); /* ------------------------------------------------------------------------------------------------- * Request lifecycle * -----------------------------------------------------------------------------------------------*/ /** * Callback that will be executed when request is created */ __publicField(this, "onRequestCreate", (callback) => { this.pluginMethods.onRequestCreate = callback; return this; }); /** * Callback that will be executed when request gets triggered */ __publicField(this, "onRequestTrigger", (callback) => { this.pluginMethods.onRequestTrigger = callback; return this; }); /** * Callback that will be executed when request starts */ __publicField(this, "onRequestStart", (callback) => { this.pluginMethods.onRequestStart = callback; return this; }); /** * Callback that will be executed when response is successful */ __publicField(this, "onRequestSuccess", (callback) => { this.pluginMethods.onRequestSuccess = callback; return this; }); /** * Callback that will be executed when response is failed */ __publicField(this, "onRequestError", (callback) => { this.pluginMethods.onRequestError = callback; return this; }); /** * Callback that will be executed when response is finished */ __publicField(this, "onRequestFinished", (callback) => { this.pluginMethods.onRequestFinished = callback; return this; }); /* ------------------------------------------------------------------------------------------------- * Dispatcher lifecycle * -----------------------------------------------------------------------------------------------*/ __publicField(this, "onDispatcherCleared", (callback) => { this.pluginMethods.onDispatcherCleared = callback; return this; }); __publicField(this, "onDispatcherQueueDrained", (callback) => { this.pluginMethods.onDispatcherQueueDrained = callback; return this; }); __publicField(this, "onDispatcherQueueRunning", (callback) => { this.pluginMethods.onDispatcherQueueRunning = callback; return this; }); __publicField(this, "onDispatcherItemAdded", (callback) => { this.pluginMethods.onDispatcherItemAdded = callback; return this; }); __publicField(this, "onDispatcherItemDeleted", (callback) => { this.pluginMethods.onDispatcherItemDeleted = callback; return this; }); __publicField(this, "onDispatcherQueueCreated", (callback) => { this.pluginMethods.onDispatcherQueueCreated = callback; return this; }); __publicField(this, "onDispatcherQueueCleared", (callback) => { this.pluginMethods.onDispatcherQueueCleared = callback; return this; }); /* ------------------------------------------------------------------------------------------------- * Cache lifecycle * -----------------------------------------------------------------------------------------------*/ __publicField(this, "onCacheItemChange", (callback) => { this.pluginMethods.onCacheItemChange = callback; return this; }); __publicField(this, "onCacheItemDelete", (callback) => { this.pluginMethods.onCacheItemDelete = callback; return this; }); /* ------------------------------------------------------------------------------------------------- * Adapter lifecycle * -----------------------------------------------------------------------------------------------*/ __publicField(this, "onAdapterFetch", (callback) => { this.pluginMethods.onAdapterFetch = callback; return this; }); this.name = config.name; if (config.data) { this.data = config.data; } } }; // src/constants/time.constants.ts var Time = /* @__PURE__ */ ((Time2) => { Time2[Time2["SEC"] = 1e3] = "SEC"; Time2[Time2["MIN"] = 6e4] = "MIN"; Time2[Time2["HOUR"] = 36e5] = "HOUR"; Time2[Time2["DAY"] = 864e5] = "DAY"; Time2[Time2["WEEK"] = 6048e5] = "WEEK"; Time2[Time2["MONTH_30"] = 2592e6] = "MONTH_30"; Time2[Time2["MONTH_31"] = 26784e5] = "MONTH_31"; Time2[Time2["YEAR"] = 31536e6] = "YEAR"; Time2[Time2["YEAR_LEAP"] = 316224e5] = "YEAR_LEAP"; return Time2; })(Time || {}); // src/request/request.ts var _Request = class _Request { constructor(client, requestOptions, initialRequestConfiguration) { this.client = client; this.requestOptions = requestOptions; this.initialRequestConfiguration = initialRequestConfiguration; __publicField(this, "endpoint"); __publicField(this, "headers"); __publicField(this, "auth"); __publicField(this, "method"); __publicField(this, "params"); __publicField(this, "payload"); __publicField(this, "queryParams"); __publicField(this, "options"); __publicField(this, "cancelable"); __publicField(this, "retry"); __publicField(this, "retryTime"); __publicField(this, "cacheTime"); __publicField(this, "cache"); __publicField(this, "staleTime"); __publicField(this, "queued"); __publicField(this, "offline"); __publicField(this, "abortKey"); __publicField(this, "cacheKey"); __publicField(this, "queryKey"); __publicField(this, "used"); __publicField(this, "deduplicate"); __publicField(this, "deduplicateTime"); __publicField(this, "isMockerEnabled", false); __publicField(this, "unstable_mock"); /** @internal */ __publicField(this, "unstable_payloadMapper"); /** @internal */ __publicField(this, "unstable_requestMapper"); /** @internal */ __publicField(this, "unstable_responseMapper"); __publicField(this, "unstable_hasParams", false); __publicField(this, "unstable_hasPayload", false); __publicField(this, "unstable_hasQuery", false); __publicField(this, "updatedAbortKey"); __publicField(this, "updatedCacheKey"); __publicField(this, "updatedQueryKey"); __publicField(this, "setHeaders", (headers) => { return this.clone({ headers }); }); __publicField(this, "setAuth", (auth) => { return this.clone({ auth }); }); __publicField(this, "setParams", (params) => { return this.clone({ params }); }); __publicField(this, "setPayload", (payload) => { return this.clone({ payload }); }); __publicField(this, "setQueryParams", (queryParams) => { return this.clone({ queryParams }); }); __publicField(this, "setOptions", (options) => { return this.clone({ options }); }); __publicField(this, "setCancelable", (cancelable) => { return this.clone({ cancelable }); }); __publicField(this, "setRetry", (retry) => { return this.clone({ retry }); }); __publicField(this, "setRetryTime", (retryTime) => { return this.clone({ retryTime }); }); __publicField(this, "setCacheTime", (cacheTime) => { return this.clone({ cacheTime }); }); __publicField(this, "setCache", (cache) => { return this.clone({ cache }); }); __publicField(this, "setStaleTime", (staleTime) => { return this.clone({ staleTime }); }); __publicField(this, "setQueued", (queued) => { return this.clone({ queued }); }); __publicField(this, "setAbortKey", (abortKey) => { this.updatedAbortKey = true; return this.clone({ abortKey }); }); __publicField(this, "setCacheKey", (cacheKey) => { this.updatedCacheKey = true; return this.clone({ cacheKey }); }); __publicField(this, "setQueryKey", (queryKey) => { this.updatedQueryKey = true; return this.clone({ queryKey }); }); __publicField(this, "setDeduplicate", (deduplicate) => { return this.clone({ deduplicate }); }); __publicField(this, "setDeduplicateTime", (deduplicateTime) => { return this.clone({ deduplicateTime }); }); __publicField(this, "setUsed", (used) => { return this.clone({ used }); }); __publicField(this, "setOffline", (offline) => { return this.clone({ offline }); }); __publicField(this, "setMock", (fn, config = {}) => { this.unstable_mock = { fn, config }; this.isMockerEnabled = true; return this; }); __publicField(this, "clearMock", () => { this.unstable_mock = void 0; this.isMockerEnabled = false; return this; }); __publicField(this, "setMockingEnabled", (isMockerEnabled) => { this.isMockerEnabled = isMockerEnabled; return this; }); /** * Map data before it gets send to the server * @param payloadMapper * @returns */ __publicField(this, "setPayloadMapper", (payloadMapper) => { const cloned = this.clone(void 0); cloned.unstable_payloadMapper = payloadMapper; return cloned; }); /** * Map request before it gets send to the server * @param requestMapper mapper of the request * @returns new request */ __publicField(this, "setRequestMapper", (requestMapper) => { const cloned = this.clone(void 0); cloned.unstable_requestMapper = requestMapper; return cloned; }); /** * Map the response to the new interface * @param responseMapper our mapping callback * @returns new response */ __publicField(this, "setResponseMapper", (responseMapper) => { const cloned = this.clone(); cloned.unstable_responseMapper = responseMapper; return cloned; }); __publicField(this, "paramsMapper", (params) => { const { endpoint } = this.requestOptions; let stringEndpoint = String(endpoint); if (params) { Object.entries(params).forEach(([key, value]) => { stringEndpoint = stringEndpoint.replace(new RegExp(`:${key}`, "g"), String(value)); }); } return stringEndpoint; }); __publicField(this, "abort", () => { const { requestManager } = this.client; requestManager.abortByKey(this.abortKey); return this.clone(); }); __publicField(this, "dehydrate", (config) => { const { response, override = true } = config || {}; if (response) { return { override, cacheTime: this.cacheTime, staleTime: this.staleTime, cacheKey: this.cacheKey, timestamp: +/* @__PURE__ */ new Date(), hydrated: true, cache: true, response }; } const cacheData = this.client.cache.get(this.cacheKey); if (!cacheData) { return void 0; } return { override, cacheTime: this.cacheTime, staleTime: this.staleTime, cacheKey: this.cacheKey, timestamp: +/* @__PURE__ */ new Date(), hydrated: true, cache: true, response: { data: cacheData.data, error: cacheData.error, status: cacheData.status, success: cacheData.success, extra: cacheData.extra, requestTimestamp: cacheData.requestTimestamp, responseTimestamp: cacheData.responseTimestamp } }; }); /** * Method to use the request WITHOUT adding it to cache and queues. This mean it will make simple request without queue side effects. * @param options * @disableReturns * @returns * ```tsx * Promise<[Data | null, Error | null, HttpStatus]> * ``` */ __publicField(this, "exec", (options) => __async(this, null, function* () { const { adapter, requestManager } = this.client; const request = this.clone(options); const requestId = this.client.unstable_requestIdMapper(this); requestManager.addAbortController(this.abortKey, requestId); const response = yield adapter.fetch(request, requestId); requestManager.removeAbortController(this.abortKey, requestId); if (request.unstable_responseMapper) { return request.unstable_responseMapper(response); } return response; })); /** * Method used to perform requests with usage of cache and queues * @param options * @param requestCallback * @disableReturns * @returns * ```tsx * Promise<[Data | null, Error | null, HttpStatus]> * ``` */ __publicField(this, "send", (options) => __async(this, null, function* () { const _a = options || {}, { dispatcherType } = _a, configuration = __objRest(_a, ["dispatcherType"]); const request = this.clone(configuration); return sendRequest(request, options); })); var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z; const configuration = __spreadValues(__spreadValues({}, (_b = (_a = this.client.adapter).unstable_getRequestDefaults) == null ? void 0 : _b.call(_a, requestOptions)), requestOptions); const { endpoint, headers, auth = true, method = client.adapter.defaultMethod, options, cancelable = false, retry = 0, retryTime = 500, cacheTime = 6e4 /* MIN */ * 5, cache = true, staleTime = 6e4 /* MIN */ * 5, queued = false, offline = true, abortKey, cacheKey, queryKey, deduplicate = false, deduplicateTime = null } = configuration; this.endpoint = (_c = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.endpoint) != null ? _c : endpoint; this.headers = (_d = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.headers) != null ? _d : headers; this.auth = (_e = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.auth) != null ? _e : auth; this.method = method; this.params = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.params; this.payload = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.payload; this.queryParams = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.queryParams; this.options = (_f = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.options) != null ? _f : options; this.cancelable = (_g = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.cancelable) != null ? _g : cancelable; this.retry = (_h = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.retry) != null ? _h : retry; this.retryTime = (_i = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.retryTime) != null ? _i : retryTime; this.cacheTime = (_j = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.cacheTime) != null ? _j : cacheTime; this.cache = (_k = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.cache) != null ? _k : cache; this.staleTime = (_l = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.staleTime) != null ? _l : staleTime; this.queued = (_m = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.queued) != null ? _m : queued; this.offline = (_n = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.offline) != null ? _n : offline; this.abortKey = (_p = (_o = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.abortKey) != null ? _o : abortKey) != null ? _p : this.client.unstable_abortKeyMapper(this); this.cacheKey = (_r = (_q = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.cacheKey) != null ? _q : cacheKey) != null ? _r : this.client.unstable_cacheKeyMapper(this); this.queryKey = (_t = (_s = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.queryKey) != null ? _s : queryKey) != null ? _t : this.client.unstable_queryKeyMapper(this); this.used = (_u = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.used) != null ? _u : false; this.deduplicate = (_v = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.deduplicate) != null ? _v : deduplicate; this.deduplicateTime = (_w = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.deduplicateTime) != null ? _w : deduplicateTime; this.updatedAbortKey = (_x = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.updatedAbortKey) != null ? _x : false; this.updatedCacheKey = (_y = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.updatedCacheKey) != null ? _y : false; this.updatedQueryKey = (_z = initialRequestConfiguration == null ? void 0 : initialRequestConfiguration.updatedQueryKey) != null ? _z : false; } toJSON() { return { requestOptions: this.requestOptions, endpoint: this.endpoint, headers: this.headers, auth: this.auth, // TODO: fix this type method: this.method, params: this.params, payload: this.payload, queryParams: this.queryParams, options: this.options, cancelable: this.cancelable, retry: this.retry, retryTime: this.retryTime, cacheTime: this.cacheTime, cache: this.cache, staleTime: this.staleTime, queued: this.queued, offline: this.offline, abortKey: this.abortKey, cacheKey: this.cacheKey, queryKey: this.queryKey, used: this.used, disableResponseInterceptors: this.requestOptions.disableResponseInterceptors, disableRequestInterceptors: this.requestOptions.disableRequestInterceptors, updatedAbortKey: this.updatedAbortKey, updatedCacheKey: this.updatedCacheKey, updatedQueryKey: this.updatedQueryKey, deduplicate: this.deduplicate, deduplicateTime: this.deduplicateTime, isMockerEnabled: this.isMockerEnabled, hasMock: !!this.unstable_mock }; } clone(configuration) { const json = this.toJSON(); const initialRequestConfiguration = __spreadProps(__spreadValues(__spreadValues({}, json), configuration), { options: (configuration == null ? void 0 : configuration.options) || this.options, abortKey: this.updatedAbortKey ? (configuration == null ? void 0 : configuration.abortKey) || this.abortKey : void 0, cacheKey: this.updatedCacheKey ? (configuration == null ? void 0 : configuration.cacheKey) || this.cacheKey : void 0, queryKey: this.updatedQueryKey ? (configuration == null ? void 0 : configuration.queryKey) || this.queryKey : void 0, endpoint: this.paramsMapper((configuration == null ? void 0 : configuration.params) || this.params), queryParams: (configuration == null ? void 0 : configuration.queryParams) || this.queryParams, payload: (configuration == null ? void 0 : configuration.payload) || this.payload, params: (configuration == null ? void 0 : configuration.params) || this.params }); const cloned = new _Request(this.client, this.requestOptions, initialRequestConfiguration); cloned.unstable_payloadMapper = this.unstable_payloadMapper; cloned.unstable_responseMapper = this.unstable_responseMapper; cloned.unstable_requestMapper = this.unstable_requestMapper; cloned.unstable_mock = this.unstable_mock; cloned.isMockerEnabled = this.isMockerEnabled; return cloned; } /** * Read the response from cache data * * If it returns error and data at the same time, it means that latest request was failed * and we show previous data from cache together with error received from actual request */ read() { const cacheData = this.client.cache.get(this.cacheKey); if (cacheData) { return { data: cacheData.data, error: cacheData.error, status: cacheData.status, success: cacheData.success, extra: cacheData.extra, requestTimestamp: cacheData.requestTimestamp, responseTimestamp: cacheData.responseTimestamp }; } return void 0; } }; __publicField(_Request, "fromJSON", (client, json) => { return new _Request(client, json.requestOptions, json); }); var Request = _Request; // src/constants/http.constants.ts var HttpMethods = /* @__PURE__ */ ((HttpMethods2) => { HttpMethods2["GET"] = "GET"; HttpMethods2["POST"] = "POST"; HttpMethods2["PUT"] = "PUT"; HttpMethods2["PATCH"] = "PATCH"; HttpMethods2["DELETE"] = "DELETE"; return HttpMethods2; })(HttpMethods || {}); // src/dispatcher/dispatcher.constants.ts var DispatcherMode = /* @__PURE__ */ ((DispatcherMode2) => { DispatcherMode2["ONE_BY_ONE"] = "one-by-one"; DispatcherMode2["ALL_AT_ONCE"] = "all-at-once"; DispatcherMode2["PREVIOUS_CANCELED"] = "previous-canceled"; DispatcherMode2["DEDUPLICATED"] = "deduplicated"; return DispatcherMode2; })(DispatcherMode || {}); // src/dispatcher/dispatcher.events.ts var getDispatcherEvents = (emitter) => ({ emitDrained: (values, isTriggeredExternally) => { emitter.emit(getDispatcherDrainedKey(), values, isTriggeredExternally); emitter.emit(getDispatcherDrainedByKey(values.queryKey), values, isTriggeredExternally); }, emitQueueStatusChanged: (values, isTriggeredExternally) => { emitter.emit(getDispatcherStatusKey(), values, isTriggeredExternally); emitter.emit(getDispatcherStatusByKey(values.queryKey), values, isTriggeredExternally); }, emitQueueChanged: (values, isTriggeredExternally) => { emitter.emit(getDispatcherChangeKey(), values, isTriggeredExternally); emitter.emit(getDispatcherChangeByKey(values.queryKey), values, isTriggeredExternally); }, /** * When queue becomes empty * @param callback * @returns */ onDrained: (callback) => { emitter.on(getDispatcherDrainedKey(), callback); return () => emitter.removeListener(getDispatcherDrainedKey(), callback); }, /** * When queue becomes empty * @param queryKey * @param callback * @returns */ onDrainedByKey: (queryKey, callback) => { emitter.on(getDispatcherDrainedByKey(queryKey), callback); return () => emitter.removeListener(getDispatcherDrainedByKey(queryKey), callback); }, /** * When queue status change from enabled to paused or vice versa * @param callback * @returns */ onQueueStatusChange: (callback) => { emitter.on(getDispatcherStatusKey(), callback); return () => emitter.removeListener(getDispatcherStatusKey(), callback); }, /** * When queue status change from enabled to paused or vice versa * @param queryKey * @param callback * @returns */ onQueueStatusChangeByKey: (queryKey, callback) => { emitter.on(getDispatcherStatusByKey(queryKey), callback); return () => emitter.removeListener(getDispatcherStatusByKey(queryKey), callback); }, /** * When new elements are added or removed from the queue * @param queryKey * @param callback * @returns */ onQueueChange: (callback) => { emitter.on(getDispatcherChangeKey(), callback); return () => emitter.removeListener(getDispatcherChangeKey(), callback); }, /** * When new elements are added or removed from the queue * @param queryKey * @param callback * @returns */ onQueueChangeByKey: (queryKey, callback) => { emitter.on(getDispatcherChangeByKey(queryKey), callback); return () => emitter.removeListener(getDispatcherChangeByKey(queryKey), callback); } }); // src/utils/uuid.utils.ts var getUniqueRequestId = (request) => { return `${request.queryKey}_${Date.now().toString(36)}_${Math.random().toString(36).substring(2)}`; }; // src/utils/emitter.utils.ts import Emitter from "events"; var getListenName = (event) => `listen_${String(event)}`; var EventEmitter = class extends Emitter { // eslint-disable-next-line @typescript-eslint/no-useless-constructor constructor(options) { super(options); __publicField(this, "emitCallbacks", []); __publicField(this, "onEmit", (callback) => { var _a; (_a = this.emitCallbacks) == null ? void 0 : _a.push(callback); return () => { var _a2; this.emitCallbacks = (_a2 = this.emitCallbacks) == null ? void 0 : _a2.filter((cb) => cb !== callback); }; }); __publicField(this, "onListener", (event, listener) => { super.on(getListenName(event), listener); return () => { super.off(getListenName(event), listener); }; }); __publicField(this, "on", (event, listener) => { super.on(event, listener); super.emit(getListenName(event), super.listeners(event).length); return this; }); __publicField(this, "off", (event, listener) => { super.off(event, listener); super.emit(getListenName(event), super.listeners(event).length); return this; }); __publicField(this, "addListener", this.on); __publicField(this, "removeListener", this.off); } emit(type, data, isTriggeredExternally) { var _a; const params = [type, data]; if (isTriggeredExternally) { params.push(isTriggeredExternally); } (_a = this.emitCallbacks) == null ? void 0 : _a.forEach((callback) => callback(...params)); return super.emit(...params); } }; // src/dispatcher/dispatcher.ts var Dispatcher = class { constructor(options) { this.options = options; __publicField(this, "emitter", new EventEmitter()); __publicField(this, "events", getDispatcherEvents(this.emitter)); __publicField(this, "storage", /* @__PURE__ */ new Map()); __publicField(this, "requestCount", /* @__PURE__ */ new Map()); __publicField(this, "runningRequests", /* @__PURE__ */ new Map()); __publicField(this, "logger"); __publicField(this, "client"); __publicField(this, "initialize", (client) => { this.client = client; this.logger = client.loggerManager.initialize(client, "Dispatcher"); this.client.appManager.events.onOnline(() => { this.flush(); }); return this; }); // ********************************************************************* // ********************************************************************* // Queue // ********************************************************************* // ********************************************************************* /** * Start request handling by queryKey */ __publicField(this, "start", (queryKey) => { const queue = this.getQueue(queryKey); queue.stopped = false; this.setQueue(queryKey, queue); this.flushQueue(queryKey); this.client.triggerPlugins("onDispatcherQueueRunning", { dispatcher: this, queue, status: "running" }); this.events.emitQueueStatusChanged(queue); }); /** * Pause request queue, but do not cancel already started requests */ __publicField(this, "pause", (queryKey) => { const queue = this.getQueue(queryKey); queue.stopped = true; this.setQueue(queryKey, queue); this.client.triggerPlugins("onDispatcherQueueRunning", { dispatcher: this, queue, status: "paused" }); this.events.emitQueueStatusChanged(queue); }); /** * Stop request queue and cancel all started requests - those will be treated like not started */ __publicField(this, "stop", (queryKey) => { const queue = this.getQueue(queryKey); queue.stopped = true; this.setQueue(queryKey, queue); this.cancelRunningRequests(queryKey); this.client.triggerPlugins("onDispatcherQueueRunning", { dispatcher: this, queue, status: "stopped" }); this.events.emitQueueStatusChanged(queue); }); /** * Return all */ __publicField(this, "getQueuesKeys", () => { return Array.from(this.storage.keys()); }); /** * Return queue state object */ __publicField(this, "getQueue", (queryKey) => { const initialQueueState = { queryKey, requests: [], stopped: false }; const storedEntity = this.storage.get(queryKey); return storedEntity || initialQueueState; }); /** * Return request from queue state */ __publicField(this, "getRequest", (queryKey, requestId) => { const initialQueueState = { queryKey, requests: [], stopped: false }; const storedEntity = this.storage.get(queryKey) || initialQueueState; return storedEntity.requests.find((req) => req.requestId === requestId); }); /** * Get value of the active queue status based on the stopped status */ __publicField(this, "getIsActiveQueue", (queryKey) => { const queue = this.getQueue(queryKey); const hasAvailableRequests = queue.requests.some((req) => !req.stopped); const isRunningQueue = !queue.stopped; return hasAvailableRequests && isRunningQueue; }); /** * Add new element to storage */ __publicField(this, "addQueueItem", (queryKey, element) => { const queue = this.getQueue(queryKey); queue.requests.push(element); this.client.triggerPlugins("onDispatcherItemAdded", { dispatcher: this, queue, queueItem: element }); this.setQueue(queryKey, queue); }); /** * Set new queue storage value */ __publicField(this, "setQueue", (queryKey, queue) => { this.storage.set(queryKey, queue); this.client.triggerPlugins("onDispatcherQueueCreated", { dispatcher: this, queue }); this.events.emitQueueChanged(queue); return queue; }); /** * Clear requests from queue cache */ __publicField(this, "clearQueue", (queryKey) => { const queue = this.getQueue(queryKey); const newQueue = { queryKey, requests: [], stopped: queue.stopped }; this.storage.set(queryKey, newQueue); this.client.triggerPlugins("onDispatcherQueueCleared", { dispatcher: this, queue: newQueue }); this.events.emitQueueChanged(newQueue); this.events.emitDrained(newQueue); return newQueue; }); /** * Method used to flush the queue requests */ __publicField(this, "flushQueue", (queryKey) => __async(this, null, function* () { const queue = this.getQueue(queryKey); const runningRequests = this.getRunningRequests(queryKey); const queueItem = queue.requests.find((request) => !request.stopped); const isStopped = queue && queue.stopped; const isOffline = !this.client.appManager.isOnline; const isConcurrent = !(queueItem == null ? void 0 : queueItem.request.queued); const isInactive = !runningRequests.length; const isEmpty = !queueItem; if (isStopped || isOffline || isEmpty) { this.logger.debug({ title: "Skipping queue trigger", type: "system", extra: { isStopped, isOffline, isEmpty } }); } else if (isConcurrent) { queue.requests.forEach((element) => { if (!this.hasRunningRequest(queryKey, element.requestId)) { this.performRequest(element); } }); } else if (isInactive) { yield this.performRequest(queueItem); this.flushQueue(queryKey); } })); /** * Flush all available requests from all queues */ __publicField(this, "flush", () => __async(this, null, function* () { const keys = this.getQueuesKeys(); for (const key of keys) { const storageItem = this.getQueue(key); if (storageItem) { this.flushQueue(key); } } })); /** * Clear all running requests and storage */ __publicField(this, "clear", () => { const keys = this.getQueuesKeys(); keys.forEach((queryKey) => this.cancelRunningRequests(queryKey)); this.runningRequests.clear(); this.storage.clear(); this.client.triggerPlugins("onDispatcherCleared", { dispatcher: this }); }); // ********************************************************************* // ********************************************************************* // Requests // ********************************************************************* // ********************************************************************* /** * Start particular request */ __publicField(this, "startRequest", (queryKey, requestId) => { const queue = this.getQueue(queryKey); const request = queue.requests.find((element) => element.requestId === requestId); if (request) { request.stopped = false; this.setQueue(queryKey, queue); this.flushQueue(queryKey); this.events.emitQueueStatusChanged(queue); } }); /** * Stop particular request */ __publicField(this, "stopRequest", (queryKey, requestId) => { const queue = this.getQueue(queryKey); const request = queue.requests.find((element) => element.requestId === requestId); if (request) { request.stopped = true; this.setQueue(queryKey, queue); this.cancelRunningRequest(queryKey, requestId); this.events.emitQueueStatusChanged(queue); } }); /** * Get currently running requests from all queryKeys */ __publicField(this, "getAllRunningRequests", () => { return Array.from(this.runningRequests.values()).flat(); }); /** * Get currently running requests */ __publicField(this, "getRunningRequests", (queryKey) => { return this.runningRequests.get(queryKey) || []; }); /** * Get running request by id */ __publicField(this, "getRunningRequest", (queryKey, requestId) => { const runningRequests = this.getRunningRequests(queryKey); return runningRequests.find((req) => req.requestId === requestId); }); /** * Add request to the running requests list */ __publicField(this, "addRunningRequest", (queryKey, requestId, request) => { const newRunningRequest = { requestId, request, timestamp: Date.now() }; const runningRequests = this.getRunningRequests(queryKey); runningRequests.push(newRunningRequest); this.runningRequests.set(queryKey, runningRequests); return newRunningRequest; }); /** * Get the value based on the currently running requests */ __publicField(this, "hasRunningRequests", (queryKey) => { return !!this.getRunningRequests(queryKey).length; }); /** * Check if request is currently processing */ __publicField(this, "hasRunningRequest", (queryKey, requestId) => { const runningRequests = this.getRunningRequests(queryKey); return !!runningRequests.find((req) => req.requestId === requestId); }); /** * Cancel all started requests, but do NOT remove it from main storage */ __publicField(this, "cancelRunningRequests", (queryKey) => { var _a; (_a = this.runningRequests.get(queryKey)) == null ? void 0 : _a.forEach((request) => { this.client.requestManager.abortByRequestId(request.request.abortKey, request.requestId); }); this.deleteRunningRequests(queryKey); }); /** * Cancel started request, but do NOT remove it from main storage */ __publicField(this, "cancelRunningRequest", (queryKey, requestId) => { const requests = this.getRunningRequests(queryKey).filter((request) => { if (request.requestId === requestId) { this.client.requestManager.abortByRequestId(request.request.abortKey, request.requestId); return false; } return true; }); this.runningRequests.set(queryKey, requests); }); /** * Delete all started requests, but do NOT clear it from queue and do NOT cancel them */ __publicField(this, "deleteRunningRequests", (queryKey) => { this.runningRequests.set(queryKey, []); }); /** * Delete request by id, but do NOT clear it from queue and do NOT cancel them */ __publicField(this, "deleteRunningRequest", (queryKey, requestId) => { const runningRequests = this.getRunningRequests(queryKey); this.runningRequests.set( queryKey, runningRequests.filter((req) => req.requestId !== requestId) ); }); /** * Get count of requests from the same queryKey */ __publicField(this, "getQueueRequestCount", (queryKey) => { return this.requestCount.get(queryKey) || 0; }); /** * Add request count to the queryKey */ __publicField(this, "incrementQueueRequestCount", (queryKey) => { const count = this.requestCount.get(queryKey) || 0; this.requestCount.set(queryKey, count + 1); }); /** * Create storage element from request */ // eslint-disable-next-line class-methods-use-this __publicField(this, "createStorageItem", (request) => { const requestId = this.client.unstable_requestIdMapper(request); const storageItem = { requestId, timestamp: +/* @__PURE__ */ new Date(), request, retries: 0, stopped: false, resolved: false }; return storageItem; }); // ********************************************************************* // ********************************************************************* // Dispatching // ********************************************************************* // ********************************************************************* /** * Add request to the dispatcher handler */ __publicField(this, "add", (request) => { const { queryKey } = request; const storageItem = this.createStorageItem(request); const { requestId } = storageItem; const queue = this.getQueue(queryKey); const [latestRequest] = queue.requests.slice(-1); const requestType = getRequestType(request, latestRequest); this.logger.debug({ title: "Adding request to queue", type: "system", extra: { requestType, request, requestId } }); switch (requestType) { case "one-by-one" /* ONE_BY_ONE */: { this.addQueueItem(queryKey, storageItem); this.flushQueue(queryKey); return requestId; } case "previous-canceled" /* PREVIOUS_CANCELED */: { this.cancelRunningRequests(queryKey); this.clearQueue(queryKey); this.addQueueItem(queryKey, storageItem); this.flushQueue(queryKey); return requestId; } case "deduplicated" /* DEDUPLICATED */: { this.client.requestManager.events.emitDeduplicated({ request: latestRequest.request, requestId, deduplicatedRequest: request }); return latestRequest.requestId; } default: { this.addQueueItem(queryKey, storageItem); this.flushQueue(queryKey); return requestId; } } }); /** * Delete from the storage and cancel request */ __publicField(this, "delete", (queryKey, requestId, abortKey) => { this.logger.debug({ title: "Deleting request", type: "system", extra: { queryKey, requestId, abortKey } }); const queue = this.getQueue(queryKey); const queueItem = queue.requests.find((req) => req.requestId === requestId); if (!queueItem) return; queue.requests = queue.requests.filter((req) => req.requestId !== requestId); this.storage.set(queryKey, queue); if (this.hasRunningRequest(queryKey, requestId)) { this.deleteRunningRequest(queryKey, requestId); this.client.requestManager.abortByRequestId(abortKey, requestId); } this.client.triggerPlugins("onDispatcherItemDeleted", { queue, dispatcher: this, queueItem }); this.events.emitQueueChanged(queue); this.client.requestManager.events.emitRemove({ requestId, request: queueItem.request, resolved: queueItem.resolved }); if (!queue.requests.length) { this.client.triggerPlugins("onDispatcherQueueDrained", { queue, dispatcher: this }); this.events.emitDrained(queue); } return queue; }); /** * Request can run for some time, once it's done, we have to check if it's successful or if it was aborted * It can be different once the previous call was set as cancelled and removed from queue before this request got resolved */ __publicField(this, "performRequest", (storageItem) => __async(this, null, function* () { var _a; const { request, requestId } = storageItem; this.logger.debug({ title: "Performing request", type: "system", extra: { request, requestId } }); const { retry, retryTime, queryKey, abortKey, offline } = request; const { adapter, requestManager, cache, appManager } = this.client; const canRetry = canRetryRequest(storageItem.retries, retry); const isOffline = !appManager.isOnline && offline; const isAlreadyRunning = this.hasRunningRequest(queryKey, requestId); const isStopped = storageItem.stopped; if (isOffline || isAlreadyRunning || isStopped) { return this.logger.warning({ title: "Unable to perform request", type: "system", extra: { isOffline, isAlreadyRunning, isStopped } }); } const runningRequest = this.addRunningRequest(queryKey, requestId, request); requestManager.events.emitLoading({ request, requestId, loading: true, isRetry: !!storageItem.retries, isOffline }); this.incrementQueueRequestCount(queryKey); requestManager.addAbortController(abortKey, requestId); const response = yield adapter.fetch(request, requestId); storageItem.resolved = true; requestManager.removeAbortController(abortKey, requestId); const isOfflineResponseStatus = !appManager.isOnline; const isCancelMessage = getErrorMessage("abort").message === ((_a = response.error) == null ? void 0 : _a.message); const isCanceled = !this.hasRunningRequest(queryKey, requestId) || isCancelMessage; this.deleteRunningRequest(queryKey, requestId); const requestDetails = { isCanceled, isOffline: isOfflineResponseStatus, retries: storageItem.retries, addedTimestamp: storageItem.timestamp, triggerTimestamp: runningRequest.timestamp, requestTimestamp: response.requestTimestamp, responseTimestamp: response.responseTimestamp }; requestManager.events.emitResponse({ request, requestId, response, details: requestDetails }); requestManager.events.emitLoading({ request, requestId, loading: false, isRetry: !!storageItem.retries, isOffline }); cache.set(request, __spreadValues(__spreadValues({}, response), requestDetails)); this.logger.debug({ title: "Dispatcher processing response", type: "system", extra: { requestId, request, response, details: requestDetails } }); if (isCanceled) { const queue = this.getQueue(queryKey); const queueItem = queue.requests.find((req) => req.requestId === requestId); if (!queue.stopped && !(queueItem == null ? void 0 : queueItem.stopped)) { this.logger.debug({ title: "Request paused", type: "system", extra: { response, requestDetails, request } }); return this.delete(queryKey, requestId, abortKey); } return this.logger.debug({ title: "Request canceled", type: "system", extra: { response, requestDetails, request } }); } if (!response.success && isOfflineResponseStatus) { if (!offline) { this.logger.warning({ title: "Removing non-offline request", type: "system", extra: { response, requestDetails, request } }); return this.delete(queryKey, requestId, abortKey); } return this.logger.debug({ title: "Awaiting for network restoration", type: "system", extra: { response, requestDetails, request } }); } if (response.success) { this.delete(queryKey, requestId, abortKey); return this.logger.debug({ title: "Successful response, removing request from queue.", type: "system", extra: { response, requestDetails, request } }); } if (!response.success && canRetry) { this.logger.debug({ title: "Waiting for retry", type: "system", extra: { response, requestDetails, request } }); setTimeout(() => { this.logger.warning({ title: "Error response, performing retry", type: "request", extra: { response, requestDetails, request, requestId } }); this.performRequest(__spreadProps(__spreadValues({}, storageItem), { retries: storageItem.retries + 1 })); }, retryTime || 0); } else { if (request.retry) { this.logger.error({ title: "All retries have been used. Removing request from queue.", type: "request", extra: { response, requestDetails, request, requestId } }); } this.delete(queryKey, requestId, abortKey); } })); var _a, _b; (_a = this.emitter) == null ? void 0 : _a.setMaxListeners(1e3); if ((_b = this.options) == null ? void 0 : _b.storage) { this.storage = this.options.storage; } } }; // src/dispatcher/dispatcher.utils.ts var getDispatcherDrainedKey = () => { return `drained-event`; }; var getDispatcherDrainedByKey = (key) => { return `${key}-drained-event`; }; var getDispatcherStatusKey = () => { return `status-event`; }; var getDispatcherStatusByKey = (key) => { return `${key}-status-event`; }; var getDispatcherChangeKey = () => { return `change-event`; }; var getDispatcherChangeByKey = (key) => { return `${key}-change-event`; }; var getIsEqualTimestamp = (currentTimestamp, threshold, queueTimestamp) => { if (!queueTimestamp) { return false; } return queueTimestamp - currentTimestamp <= threshold; }; var canRetryRequest = (currentRetries, retry) => { if (retry && currentRetries < retry) { return true; } return false; }; var isInDeduplicateRange = (request, latestRequest) => { if (request.deduplicateTime) { return +/* @__PURE__ */ new Date() - latestRequest.timestamp <= request.deduplicateTime; } return true; }; var getRequestType = (request, latestRequest) => { const { queued, cancelable, deduplicate } = request; const canDeduplicate = latestRequest ? isInDeduplicateRange(request, latestRequest) : false; if (queued) { return "one-by-one" /* ONE_BY_ONE */; } if (cancelable) { return "previous-canceled" /* PREVIOUS_CANCELED */; } if (canDeduplicate && deduplicate) { re