UNPKG

@hyper-fetch/core

Version:

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

1,301 lines (1,283 loc) 95.2 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; 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 __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[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 )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); 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/index.ts var src_exports = {}; __export(src_exports, { AppEvents: () => AppEvents, AppManager: () => AppManager, Cache: () => Cache, Client: () => Client, DateInterval: () => DateInterval, Dispatcher: () => Dispatcher, DispatcherRequestType: () => DispatcherRequestType, HttpMethodsEnum: () => HttpMethodsEnum, LoggerManager: () => LoggerManager, Request: () => Request, RequestEffect: () => RequestEffect, RequestManager: () => RequestManager, adapter: () => adapter3, appManagerInitialOptions: () => appManagerInitialOptions, canRetryRequest: () => canRetryRequest, defaultTimeout: () => defaultTimeout, getAbortByIdEventKey: () => getAbortByIdEventKey, getAbortEventKey: () => getAbortEventKey, getAdapterBindings: () => getAdapterBindings, getAdapterHeaders: () => getAdapterHeaders, getAdapterPayload: () => getAdapterPayload, getAppManagerEvents: () => getAppManagerEvents, getCacheData: () => getCacheData, getCacheEvents: () => getCacheEvents, getCacheIdKey: () => getCacheIdKey, getCacheKey: () => getCacheKey, getDispatcherChangeEventKey: () => getDispatcherChangeEventKey, getDispatcherDrainedEventKey: () => getDispatcherDrainedEventKey, getDispatcherEvents: () => getDispatcherEvents, getDispatcherStatusEventKey: () => getDispatcherStatusEventKey, getDownloadProgressEventKey: () => getDownloadProgressEventKey, getDownloadProgressIdEventKey: () => getDownloadProgressIdEventKey, getErrorMessage: () => getErrorMessage, getInvalidateEventKey: () => getInvalidateEventKey, getIsEqualTimestamp: () => getIsEqualTimestamp, getLoadingEventKey: () => getLoadingEventKey, getLoadingIdEventKey: () => getLoadingIdEventKey, getProgressData: () => getProgressData, getProgressValue: () => getProgressValue, getRemoveEventKey: () => getRemoveEventKey, getRemoveIdEventKey: () => getRemoveIdEventKey, getRequestDispatcher: () => getRequestDispatcher, getRequestEta: () => getRequestEta, getRequestKey: () => getRequestKey, getRequestManagerEvents: () => getRequestManagerEvents, getRequestStartEventKey: () => getRequestStartEventKey, getRequestStartIdEventKey: () => getRequestStartIdEventKey, getRequestType: () => getRequestType, getResponseEventKey: () => getResponseEventKey, getResponseHeaders: () => getResponseHeaders, getResponseIdEventKey: () => getResponseIdEventKey, getResponseStartEventKey: () => getResponseStartEventKey, getResponseStartIdEventKey: () => getResponseStartIdEventKey, getSimpleKey: () => getSimpleKey, getTime: () => getTime, getUniqueRequestId: () => getUniqueRequestId, getUploadProgressEventKey: () => getUploadProgressEventKey, getUploadProgressIdEventKey: () => getUploadProgressIdEventKey, hasDocument: () => hasDocument, hasWindow: () => hasWindow, interceptRequest: () => interceptRequest, interceptResponse: () => interceptResponse, logger: () => logger, loggerIconLevels: () => loggerIconLevels, loggerStyles: () => loggerStyles, mocker: () => mocker, onDocumentEvent: () => onDocumentEvent, onWindowEvent: () => onWindowEvent, parseErrorResponse: () => parseErrorResponse, parseResponse: () => parseResponse, sendRequest: () => sendRequest, severity: () => severity, stringifyDefaultOptions: () => stringifyDefaultOptions, stringifyKey: () => stringifyKey, stringifyQueryParams: () => stringifyQueryParams, stringifyValue: () => stringifyValue, xhrExtra: () => xhrExtra }); module.exports = __toCommonJS(src_exports); // src/effect/request.effect.ts var RequestEffect = class { constructor(config) { this.config = config; this.getEffectKey = () => { return this.config.effectKey; }; this.onTrigger = (request) => { var _a, _b; (_b = (_a = this.config).onTrigger) == null ? void 0 : _b.call(_a, request); }; this.onStart = (request) => { var _a, _b; (_b = (_a = this.config).onStart) == null ? void 0 : _b.call(_a, request); }; this.onSuccess = (response, request) => { var _a, _b; (_b = (_a = this.config).onSuccess) == null ? void 0 : _b.call(_a, response, request); }; this.onError = (response, request) => { var _a, _b; (_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, response, request); }; this.onFinished = (response, request) => { var _a, _b; (_b = (_a = this.config).onFinished) == null ? void 0 : _b.call(_a, response, request); }; } }; // src/constants/time.constants.ts var DateInterval = /* @__PURE__ */ ((DateInterval2) => { DateInterval2[DateInterval2["second"] = 1e3] = "second"; DateInterval2[DateInterval2["minute"] = 6e4] = "minute"; DateInterval2[DateInterval2["hour"] = 36e5] = "hour"; DateInterval2[DateInterval2["day"] = 864e5] = "day"; DateInterval2[DateInterval2["week"] = 6048e5] = "week"; DateInterval2[DateInterval2["month30"] = 2592e6] = "month30"; DateInterval2[DateInterval2["month31"] = 26784e5] = "month31"; DateInterval2[DateInterval2["year"] = 31536e6] = "year"; DateInterval2[DateInterval2["yearLeap"] = 316224e5] = "yearLeap"; return DateInterval2; })(DateInterval || {}); // src/adapter/adapter.constants.ts var defaultTimeout = 1e3 /* second */ * 5; var xhrExtra = { headers: {} }; // src/adapter/adapter.browser.ts var adapter = (request, requestId) => __async(void 0, null, function* () { const { makeRequest, fullUrl, config, payload, headers, onError, onResponseEnd, onTimeoutError, onRequestEnd, createAbortListener, onResponseProgress, onRequestProgress, onResponseStart, onBeforeRequest, onRequestStart, onSuccess } = yield getAdapterBindings(request, requestId, 0, { headers: {} }); const { method = "GET" } = request; return makeRequest((resolve) => { const xhr = new XMLHttpRequest(); xhr.timeout = defaultTimeout; const abort = () => xhr.abort(); Object.entries(config).forEach(([name, value]) => { xhr[name] = value; }); xhr.open(method, fullUrl, true); Object.entries(headers).forEach(([name, value]) => xhr.setRequestHeader(name, value)); const unmountListener = createAbortListener(0, xhrExtra, abort, resolve); xhr.upload.onprogress = onRequestProgress; xhr.onloadstart = () => { onRequestEnd(); onResponseStart(); }; xhr.onprogress = onResponseProgress; xhr.onloadend = () => { onResponseEnd(); unmountListener(); }; xhr.ontimeout = () => onTimeoutError(0, xhrExtra, resolve); xhr.onreadystatechange = (e) => { const event = e; const finishedState = 4; if (event.target && event.target.readyState === finishedState) { const { status } = event.target; const success = String(status).startsWith("2") || String(status).startsWith("3"); const responseHeaders = getResponseHeaders(xhr.getAllResponseHeaders()); if (success) { const data = parseResponse(event.target.response); onSuccess(data, status, { headers: responseHeaders }, resolve); } else { const data = parseErrorResponse(event.target.response); onError(data, status, { headers: responseHeaders }, resolve); } } }; onBeforeRequest(); onRequestStart(); xhr.send(payload); }); }); // src/adapter/adapter.server.ts var import_http = __toESM(require("http")); var import_https = __toESM(require("https")); // src/constants/http.constants.ts var HttpMethodsEnum = /* @__PURE__ */ ((HttpMethodsEnum2) => { HttpMethodsEnum2["get"] = "GET"; HttpMethodsEnum2["post"] = "POST"; HttpMethodsEnum2["put"] = "PUT"; HttpMethodsEnum2["patch"] = "PATCH"; HttpMethodsEnum2["delete"] = "DELETE"; return HttpMethodsEnum2; })(HttpMethodsEnum || {}); // src/adapter/adapter.server.ts var adapter2 = (request, requestId) => __async(void 0, null, function* () { const { makeRequest, fullUrl, config, headers, payload, onError, onResponseEnd, onTimeoutError, onRequestEnd, createAbortListener, onResponseProgress, onResponseStart, onBeforeRequest, onRequestStart, onSuccess } = yield getAdapterBindings(request, requestId, 0, xhrExtra); const { method = "GET" /* get */, client } = request; const httpClient = client.url.includes("https://") ? import_https.default : import_http.default; const options = { method, headers, timeout: defaultTimeout }; Object.entries(config).forEach(([name, value]) => { options[name] = value; }); let unmountListener = () => null; onBeforeRequest(); if (payload) { options.headers["Content-Length"] = Buffer.byteLength(JSON.stringify(payload)); } const requestUrl = !fullUrl.startsWith("http") ? `http://${fullUrl}` : fullUrl; return makeRequest((resolve) => { const httpRequest = httpClient.request(requestUrl, options, (response) => { response.setEncoding("utf8"); unmountListener = createAbortListener(0, xhrExtra, response.destroy, resolve); let chunks = ""; const totalDownloadBytes = Number(response.headers["content-length"]); let downloadedBytes = 0; onRequestStart(); response.on("data", (chunk) => { if (!chunks) { onRequestEnd(); onResponseStart(); } downloadedBytes += chunk.length; chunks += chunk; onResponseProgress({ total: totalDownloadBytes, loaded: downloadedBytes }); }); response.on("end", () => { const { statusCode } = response; const success = String(statusCode).startsWith("2") || String(statusCode).startsWith("3"); if (success) { const data = parseResponse(chunks); onSuccess(data, statusCode, { headers: response.headers }, resolve); } else { const data = parseErrorResponse(chunks); onError(data, statusCode, { headers: response.headers }, resolve); } unmountListener(); onResponseEnd(); }); }); httpRequest.on("timeout", () => onTimeoutError(0, xhrExtra, resolve)); httpRequest.on("error", (error) => onError(error, 0, xhrExtra, resolve)); if (payload) { httpRequest.write(payload); } httpRequest.end(); }); }); // src/adapter/adapter.ts var adapter3 = (request, requestId) => __async(void 0, null, function* () { if (typeof XMLHttpRequest !== "undefined") { return adapter(request, requestId); } return adapter2(request, requestId); }); // src/adapter/adapter.utils.ts var getErrorMessage = (errorCase) => { if (errorCase === "timeout") { return new Error("Request timeout"); } if (errorCase === "abort") { return new Error("Request cancelled"); } return new Error("Unexpected error"); }; var getResponseHeaders = (headersString) => { const arr = headersString.trim().split(/[\r\n]+/); const headers = {}; arr.forEach((line) => { const parts = line.split(": "); const header = parts.shift(); const value = parts.join(": "); headers[header] = value; }); return headers; }; var parseResponse = (response) => { try { return JSON.parse(response); } catch (err) { return response; } }; var parseErrorResponse = (response) => { return response ? parseResponse(response) : getErrorMessage(); }; // src/utils/uuid.utils.ts var getUniqueRequestId = (key) => { return `${key}_${Date.now().toString(36)}_${Math.random().toString(36).substring(2)}`; }; // src/request/request.ts var Request = class { constructor(client, requestOptions, requestJSON) { this.client = client; this.requestOptions = requestOptions; this.requestJSON = requestJSON; this.isMockEnabled = false; this.setHeaders = (headers) => { return this.clone({ headers }); }; this.setAuth = (auth) => { return this.clone({ auth }); }; this.setParams = (params) => { return this.clone({ params }); }; this.setData = (data) => { return this.clone({ data }); }; this.setQueryParams = (queryParams) => { return this.clone({ queryParams }); }; this.setOptions = (options) => { return this.clone({ options }); }; this.setCancelable = (cancelable) => { return this.clone({ cancelable }); }; this.setRetry = (retry) => { return this.clone({ retry }); }; this.setRetryTime = (retryTime) => { return this.clone({ retryTime }); }; this.setGarbageCollection = (garbageCollection) => { return this.clone({ garbageCollection }); }; this.setCache = (cache) => { return this.clone({ cache }); }; this.setCacheTime = (cacheTime) => { return this.clone({ cacheTime }); }; this.setQueued = (queued) => { return this.clone({ queued }); }; this.setAbortKey = (abortKey) => { this.updatedAbortKey = true; return this.clone({ abortKey }); }; this.setCacheKey = (cacheKey) => { this.updatedCacheKey = true; return this.clone({ cacheKey }); }; this.setQueueKey = (queueKey) => { this.updatedQueueKey = true; return this.clone({ queueKey }); }; this.setEffectKey = (effectKey) => { this.updatedEffectKey = true; return this.clone({ effectKey }); }; this.setDeduplicate = (deduplicate) => { return this.clone({ deduplicate }); }; this.setDeduplicateTime = (deduplicateTime) => { return this.clone({ deduplicateTime }); }; this.setUsed = (used) => { return this.clone({ used }); }; this.setOffline = (offline) => { return this.clone({ offline }); }; this.setMock = (mockData) => { const mockGenerator = function* mocked(mockedValues) { if (Array.isArray(mockData)) { let iteration = 0; while (true) { yield mockedValues[iteration]; iteration = mockData.length === iteration + 1 ? 0 : iteration + 1; } } else { while (true) { yield mockData; } } }; this.mockData = mockData; this.mock = mockGenerator(mockData); this.isMockEnabled = true; return this; }; this.removeMock = () => { this.mockData = null; this.mock = null; this.isMockEnabled = false; return this; }; this.setEnableMocking = (isMockEnabled) => { this.isMockEnabled = isMockEnabled; return this; }; this.setDataMapper = (dataMapper) => { const cloned = this.clone(void 0); cloned.dataMapper = dataMapper; return cloned; }; this.setRequestMapper = (requestMapper) => { const cloned = this.clone(void 0); cloned.requestMapper = requestMapper; return cloned; }; this.setResponseMapper = (responseMapper) => { const cloned = this.clone(); cloned.responseMapper = responseMapper; return cloned; }; this.paramsMapper = (params, queryParams) => { let endpoint = this.requestOptions.endpoint; if (params) { Object.entries(params).forEach(([key, value]) => { endpoint = endpoint.replace(new RegExp(`:${key}`, "g"), String(value)); }); } if (queryParams) { endpoint += this.client.stringifyQueryParams(queryParams); } return endpoint; }; this.abort = () => { const { requestManager } = this.client; requestManager.abortByKey(this.abortKey); return this.clone(); }; this.exec = (options) => __async(this, null, function* () { const { adapter: adapter4, requestManager } = this.client; const request = this.clone(options); const requestId = getUniqueRequestId(this.queueKey); requestManager.addAbortController(this.abortKey, requestId); const response = yield adapter4(request, requestId); requestManager.removeAbortController(this.abortKey, requestId); if (request.responseMapper) { return request.responseMapper(response); } return response; }); this.send = (options) => __async(this, null, function* () { const _a = options || {}, { dispatcherType } = _a, rest = __objRest(_a, ["dispatcherType"]); const request = this.clone(rest); 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, _A, _B, _C; const { endpoint, headers, auth = true, method = client.defaultMethod, options, cancelable = false, retry = 0, retryTime = 500, garbageCollection = 6e4 /* minute */ * 5, cache = true, cacheTime = 6e4 /* minute */ * 5, queued = false, offline = true, abortKey, cacheKey, queueKey, effectKey, deduplicate = false, deduplicateTime = 10 } = __spreadValues(__spreadValues({}, (_b = (_a = this.client).requestDefaultOptions) == null ? void 0 : _b.call(_a, requestOptions)), requestOptions); this.endpoint = (_c = requestJSON == null ? void 0 : requestJSON.endpoint) != null ? _c : endpoint; this.headers = (_d = requestJSON == null ? void 0 : requestJSON.headers) != null ? _d : headers; this.auth = (_e = requestJSON == null ? void 0 : requestJSON.auth) != null ? _e : auth; this.method = method; this.params = requestJSON == null ? void 0 : requestJSON.params; this.data = requestJSON == null ? void 0 : requestJSON.data; this.queryParams = requestJSON == null ? void 0 : requestJSON.queryParams; this.options = (_f = requestJSON == null ? void 0 : requestJSON.options) != null ? _f : options; this.cancelable = (_g = requestJSON == null ? void 0 : requestJSON.cancelable) != null ? _g : cancelable; this.retry = (_h = requestJSON == null ? void 0 : requestJSON.retry) != null ? _h : retry; this.retryTime = (_i = requestJSON == null ? void 0 : requestJSON.retryTime) != null ? _i : retryTime; this.garbageCollection = (_j = requestJSON == null ? void 0 : requestJSON.garbageCollection) != null ? _j : garbageCollection; this.cache = (_k = requestJSON == null ? void 0 : requestJSON.cache) != null ? _k : cache; this.cacheTime = (_l = requestJSON == null ? void 0 : requestJSON.cacheTime) != null ? _l : cacheTime; this.queued = (_m = requestJSON == null ? void 0 : requestJSON.queued) != null ? _m : queued; this.offline = (_n = requestJSON == null ? void 0 : requestJSON.offline) != null ? _n : offline; this.abortKey = (_p = (_o = requestJSON == null ? void 0 : requestJSON.abortKey) != null ? _o : abortKey) != null ? _p : this.client.abortKeyMapper(this); this.cacheKey = (_r = (_q = requestJSON == null ? void 0 : requestJSON.cacheKey) != null ? _q : cacheKey) != null ? _r : this.client.cacheKeyMapper(this); this.queueKey = (_t = (_s = requestJSON == null ? void 0 : requestJSON.queueKey) != null ? _s : queueKey) != null ? _t : this.client.queueKeyMapper(this); this.effectKey = (_v = (_u = requestJSON == null ? void 0 : requestJSON.effectKey) != null ? _u : effectKey) != null ? _v : this.client.effectKeyMapper(this); this.used = (_w = requestJSON == null ? void 0 : requestJSON.used) != null ? _w : false; this.deduplicate = (_x = requestJSON == null ? void 0 : requestJSON.deduplicate) != null ? _x : deduplicate; this.deduplicateTime = (_y = requestJSON == null ? void 0 : requestJSON.deduplicateTime) != null ? _y : deduplicateTime; this.updatedAbortKey = (_z = requestJSON == null ? void 0 : requestJSON.updatedAbortKey) != null ? _z : false; this.updatedCacheKey = (_A = requestJSON == null ? void 0 : requestJSON.updatedCacheKey) != null ? _A : false; this.updatedQueueKey = (_B = requestJSON == null ? void 0 : requestJSON.updatedQueueKey) != null ? _B : false; this.updatedEffectKey = (_C = requestJSON == null ? void 0 : requestJSON.updatedEffectKey) != null ? _C : false; } toJSON() { return { requestOptions: this.requestOptions, endpoint: this.endpoint, headers: this.headers, auth: this.auth, method: this.method, params: this.params, data: this.data, queryParams: this.queryParams, options: this.options, cancelable: this.cancelable, retry: this.retry, retryTime: this.retryTime, garbageCollection: this.garbageCollection, cache: this.cache, cacheTime: this.cacheTime, queued: this.queued, offline: this.offline, abortKey: this.abortKey, cacheKey: this.cacheKey, queueKey: this.queueKey, effectKey: this.effectKey, used: this.used, disableResponseInterceptors: this.requestOptions.disableResponseInterceptors, disableRequestInterceptors: this.requestOptions.disableRequestInterceptors, updatedAbortKey: this.updatedAbortKey, updatedCacheKey: this.updatedCacheKey, updatedQueueKey: this.updatedQueueKey, updatedEffectKey: this.updatedEffectKey, deduplicate: this.deduplicate, deduplicateTime: this.deduplicateTime }; } clone(options) { const json = this.toJSON(); const requestJSON = __spreadProps(__spreadValues(__spreadValues({}, json), options), { abortKey: this.updatedAbortKey ? (options == null ? void 0 : options.abortKey) || this.abortKey : void 0, cacheKey: this.updatedCacheKey ? (options == null ? void 0 : options.cacheKey) || this.cacheKey : void 0, queueKey: this.updatedQueueKey ? (options == null ? void 0 : options.queueKey) || this.queueKey : void 0, endpoint: this.paramsMapper((options == null ? void 0 : options.params) || this.params, (options == null ? void 0 : options.queryParams) || this.queryParams), queryParams: (options == null ? void 0 : options.queryParams) || this.queryParams, data: (options == null ? void 0 : options.data) || this.data }); const cloned = new Request( this.client, this.requestOptions, requestJSON ); cloned.dataMapper = this.dataMapper; cloned.responseMapper = this.responseMapper; cloned.requestMapper = this.requestMapper; cloned.mockData = this.mockData; cloned.mock = this.mock; cloned.isMockEnabled = this.isMockEnabled; return cloned; } }; // src/dispatcher/dispatcher.constants.ts var DispatcherRequestType = /* @__PURE__ */ ((DispatcherRequestType2) => { DispatcherRequestType2["oneByOne"] = "one-by-one"; DispatcherRequestType2["allAtOnce"] = "all-at-once"; DispatcherRequestType2["previousCanceled"] = "previous-canceled"; DispatcherRequestType2["deduplicated"] = "deduplicated"; return DispatcherRequestType2; })(DispatcherRequestType || {}); // src/dispatcher/dispatcher.events.ts var getDispatcherEvents = (emitter) => ({ setDrained: (queueKey, values) => { emitter.emit(getDispatcherDrainedEventKey(queueKey), values); }, setQueueStatus: (queueKey, values) => { emitter.emit(getDispatcherStatusEventKey(queueKey), values); }, setQueueChanged: (queueKey, values) => { emitter.emit(getDispatcherChangeEventKey(queueKey), values); }, onDrained: (queueKey, callback) => { emitter.on(getDispatcherDrainedEventKey(queueKey), callback); return () => emitter.removeListener(getDispatcherDrainedEventKey(queueKey), callback); }, onQueueStatus: (queueKey, callback) => { emitter.on(getDispatcherStatusEventKey(queueKey), callback); return () => emitter.removeListener(getDispatcherStatusEventKey(queueKey), callback); }, onQueueChange: (queueKey, callback) => { emitter.on(getDispatcherChangeEventKey(queueKey), callback); return () => emitter.removeListener(getDispatcherChangeEventKey(queueKey), callback); } }); // src/dispatcher/dispatcher.ts var import_events = __toESM(require("events")); var Dispatcher = class { constructor(client, options) { this.client = client; this.options = options; this.emitter = new import_events.default(); this.events = getDispatcherEvents(this.emitter); this.storage = /* @__PURE__ */ new Map(); this.requestCount = /* @__PURE__ */ new Map(); this.runningRequests = /* @__PURE__ */ new Map(); this.start = (queueKey) => { const queue = this.getQueue(queueKey); queue.stopped = false; this.setQueue(queueKey, queue); this.flushQueue(queueKey); this.events.setQueueStatus(queueKey, queue); }; this.pause = (queueKey) => { const queue = this.getQueue(queueKey); queue.stopped = true; this.setQueue(queueKey, queue); this.events.setQueueStatus(queueKey, queue); }; this.stop = (queueKey) => { const queue = this.getQueue(queueKey); queue.stopped = true; this.setQueue(queueKey, queue); this.cancelRunningRequests(queueKey); this.events.setQueueStatus(queueKey, queue); }; this.getQueuesKeys = () => { return Array.from(this.storage.keys()); }; this.getQueue = (queueKey) => { const initialQueueState = { requests: [], stopped: false }; const storedEntity = this.storage.get(queueKey); return storedEntity || initialQueueState; }; this.getRequest = (queueKey, requestId) => { const initialQueueState = { requests: [], stopped: false }; const storedEntity = this.storage.get(queueKey) || initialQueueState; return storedEntity.requests.find((req) => req.requestId === requestId); }; this.getIsActiveQueue = (queueKey) => { const queue = this.getQueue(queueKey); const hasAvailableRequests = queue.requests.some((req) => !req.stopped); const isRunningQueue = !queue.stopped; return hasAvailableRequests && isRunningQueue; }; this.addQueueElement = (queueKey, dispatcherDump) => { const queue = this.getQueue(queueKey); queue.requests.push(dispatcherDump); this.setQueue(queueKey, queue); }; this.setQueue = (queueKey, queue) => { var _a, _b; this.storage.set(queueKey, queue); (_b = (_a = this.options) == null ? void 0 : _a.onUpdateStorage) == null ? void 0 : _b.call(_a, queueKey, queue); this.events.setQueueChanged(queueKey, queue); return queue; }; this.clearQueue = (queueKey) => { var _a, _b; const queue = this.getQueue(queueKey); const newQueue = { requests: [], stopped: queue.stopped }; this.storage.set(queueKey, newQueue); (_b = (_a = this.options) == null ? void 0 : _a.onDeleteFromStorage) == null ? void 0 : _b.call(_a, queueKey, newQueue); this.events.setQueueChanged(queueKey, newQueue); return newQueue; }; this.flushQueue = (queueKey) => __async(this, null, function* () { const queue = this.getQueue(queueKey); const runningRequests = this.getRunningRequests(queueKey); const queueElement = queue.requests.find((request) => !request.stopped); const isStopped = queue && queue.stopped; const isOffline = !this.client.appManager.isOnline; const isConcurrent = !(queueElement == null ? void 0 : queueElement.request.queued); const isInactive = !runningRequests.length; const isEmpty = !queueElement; if (isStopped || isOffline || isEmpty) { this.logger.debug("Skipping queue trigger", { isStopped, isOffline, isEmpty }); } else if (isConcurrent) { queue.requests.forEach((element) => { if (!this.hasRunningRequest(queueKey, element.requestId)) { this.performRequest(element); } }); } else if (isInactive) { yield this.performRequest(queueElement); this.flushQueue(queueKey); } }); this.flush = () => __async(this, null, function* () { const keys = this.getQueuesKeys(); for (const key of keys) { const storageElement = this.getQueue(key); if (storageElement) { this.flushQueue(key); } } }); this.clear = () => { var _a, _b; const keys = this.getQueuesKeys(); keys.forEach((queueKey) => this.cancelRunningRequests(queueKey)); this.runningRequests.clear(); this.storage.clear(); (_b = (_a = this.options) == null ? void 0 : _a.onClearStorage) == null ? void 0 : _b.call(_a, this); }; this.startRequest = (queueKey, requestId) => { const queue = this.getQueue(queueKey); const request = queue.requests.find((element) => element.requestId === requestId); if (request) { request.stopped = false; this.setQueue(queueKey, queue); this.flushQueue(queueKey); this.events.setQueueStatus(queueKey, queue); } }; this.stopRequest = (queueKey, requestId) => { const queue = this.getQueue(queueKey); const request = queue.requests.find((element) => element.requestId === requestId); if (request) { request.stopped = true; this.setQueue(queueKey, queue); this.cancelRunningRequest(queueKey, requestId); this.events.setQueueStatus(queueKey, queue); } }; this.getAllRunningRequest = () => { return Array.from(this.runningRequests.values()).flat(); }; this.getRunningRequests = (queueKey) => { return this.runningRequests.get(queueKey) || []; }; this.getRunningRequest = (queueKey, requestId) => { const runningRequests = this.getRunningRequests(queueKey); return runningRequests.find((req) => req.requestId === requestId); }; this.addRunningRequest = (queueKey, requestId, request) => { const runningRequests = this.getRunningRequests(queueKey); runningRequests.push({ requestId, request, timestamp: Date.now() }); this.runningRequests.set(queueKey, runningRequests); }; this.hasRunningRequests = (queueKey) => { return !!this.getRunningRequests(queueKey).length; }; this.hasRunningRequest = (queueKey, requestId) => { const runningRequests = this.getRunningRequests(queueKey); return !!runningRequests.find((req) => req.requestId === requestId); }; this.cancelRunningRequests = (queueKey) => { var _a; (_a = this.runningRequests.get(queueKey)) == null ? void 0 : _a.forEach((request) => { this.client.requestManager.abortByRequestId(request.request.abortKey, request.requestId); }); this.deleteRunningRequests(queueKey); }; this.cancelRunningRequest = (queueKey, requestId) => { const requests = this.getRunningRequests(queueKey).filter((request) => { if (request.requestId === requestId) { this.client.requestManager.abortByRequestId(request.request.abortKey, request.requestId); return false; } return true; }); this.runningRequests.set(queueKey, requests); }; this.deleteRunningRequests = (queueKey) => { this.runningRequests.set(queueKey, []); }; this.deleteRunningRequest = (queueKey, requestId) => { const runningRequests = this.getRunningRequests(queueKey); this.runningRequests.set( queueKey, runningRequests.filter((req) => req.requestId !== requestId) ); }; this.getQueueRequestCount = (queueKey) => { return this.requestCount.get(queueKey) || 0; }; this.incrementQueueRequestCount = (queueKey) => { const count = this.requestCount.get(queueKey) || 0; this.requestCount.set(queueKey, count + 1); }; this.createStorageElement = (request) => { const requestId = getUniqueRequestId(request.queueKey); const storageElement = { requestId, timestamp: +new Date(), request, retries: 0, stopped: false }; return storageElement; }; this.add = (request) => { const { queueKey } = request; const storageElement = this.createStorageElement(request); const { requestId } = storageElement; const queue = this.getQueue(queueKey); const [latestRequest] = queue.requests.slice(-1); const requestType = getRequestType(request, latestRequest); this.logger.debug("Adding request to queue", { requestType, request, requestId }); switch (requestType) { case "one-by-one" /* oneByOne */: { this.addQueueElement(queueKey, storageElement); this.flushQueue(queueKey); return requestId; } case "previous-canceled" /* previousCanceled */: { this.cancelRunningRequests(queueKey); this.clearQueue(queueKey); this.addQueueElement(queueKey, storageElement); this.flushQueue(queueKey); return requestId; } case "deduplicated" /* deduplicated */: { return queue.requests[0].requestId; } default: { this.addQueueElement(queueKey, storageElement); this.flushQueue(queueKey); return requestId; } } }; this.delete = (queueKey, requestId, abortKey) => { var _a, _b; this.logger.debug("Deleting request", { queueKey, requestId, abortKey }); const queue = this.getQueue(queueKey); const queueElement = queue.requests.find((req) => req.requestId === requestId); if (!queueElement) return; queue.requests = queue.requests.filter((req) => req.requestId !== requestId); this.storage.set(queueKey, queue); if (this.hasRunningRequest(queueKey, requestId)) { this.deleteRunningRequest(queueKey, requestId); this.client.requestManager.abortByRequestId(abortKey, requestId); } (_b = (_a = this.options) == null ? void 0 : _a.onDeleteFromStorage) == null ? void 0 : _b.call(_a, queueKey, queue); this.events.setQueueChanged(queueKey, queue); this.client.requestManager.events.emitRemove(queueKey, requestId, { requestId, request: queueElement.request }); if (!queue.requests.length) { this.events.setDrained(queueKey, queue); } return queue; }; this.performRequest = (storageElement) => __async(this, null, function* () { var _a; const { request, requestId } = storageElement; this.logger.info("Performing request", { request, requestId }); const { retry, retryTime, queueKey, cacheKey, abortKey, offline } = request; const { adapter: adapter4, requestManager, cache, appManager } = this.client; const canRetry = canRetryRequest(storageElement.retries, retry); const isOffline = !appManager.isOnline && offline; const isAlreadyRunning = this.hasRunningRequest(queueKey, requestId); const isStopped = storageElement.stopped; if (isOffline || isAlreadyRunning || isStopped) { return this.logger.warning("Unable to perform request", { isOffline, isAlreadyRunning, isStopped }); } this.addRunningRequest(queueKey, requestId, request); requestManager.events.emitLoading(queueKey, requestId, { queueKey, requestId, loading: true, isRetry: !!storageElement.retries, isOffline }); this.incrementQueueRequestCount(queueKey); requestManager.addAbortController(abortKey, requestId); const response = yield adapter4(request, requestId); 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(queueKey, requestId) || isCancelMessage; this.deleteRunningRequest(queueKey, requestId); const requestDetails = { isCanceled, isOffline: isOfflineResponseStatus, retries: storageElement.retries, timestamp: +new Date() }; requestManager.events.emitResponse(cacheKey, requestId, response, requestDetails); requestManager.events.emitLoading(queueKey, requestId, { queueKey, requestId, loading: false, isRetry: !!storageElement.retries, isOffline }); cache.set(request, __spreadValues(__spreadValues({}, response), requestDetails)); this.logger.info("Request finished", { requestId, request, response, requestDetails }); if (isCanceled) { const queue = this.getQueue(queueKey); const queueElement = queue.requests.find((req) => req.requestId === requestId); if (!queue.stopped && !(queueElement == null ? void 0 : queueElement.stopped)) { this.logger.debug("Request paused", { response, requestDetails, request }); return this.delete(queueKey, requestId, abortKey); } return this.logger.debug("Request canceled", { response, requestDetails, request }); } if (!response.success && isOfflineResponseStatus) { if (!offline) { this.logger.warning("Removing non-offline request", { response, requestDetails, request }); return this.delete(queueKey, requestId, abortKey); } return this.logger.debug("Awaiting for network restoration", { response, requestDetails, request }); } if (response.success) { this.delete(queueKey, requestId, abortKey); return this.logger.debug("Successful response, removing request from queue.", { response, requestDetails, request }); } if (!response.success && canRetry) { this.logger.debug("Waiting for retry", { response, requestDetails, request }); setTimeout(() => { this.logger.warning("Error response, performing retry"); this.performRequest(__spreadProps(__spreadValues({}, storageElement), { retries: storageElement.retries + 1 })); }, retryTime || 0); } else { this.logger.error("All retries have been used. Removing request from queue."); this.delete(queueKey, requestId, abortKey); } }); var _a, _b, _c; this.logger = client.loggerManager.init("Dispatcher"); if ((_a = this.options) == null ? void 0 : _a.storage) { this.storage = this.options.storage; } this.client.appManager.events.onOnline(() => { this.flush(); }); (_c = (_b = this.options) == null ? void 0 : _b.onInitialization) == null ? void 0 : _c.call(_b, this); } }; // src/dispatcher/dispatcher.utils.ts var getDispatcherDrainedEventKey = (key) => { return `${key}-drained-event`; }; var getDispatcherStatusEventKey = (key) => { return `${key}-status-event`; }; var getDispatcherChangeEventKey = (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 getRequestType = (request, latestRequest) => { const { queued, cancelable, deduplicate } = request; const canDeduplicate = latestRequest ? +new Date() - latestRequest.timestamp <= request.deduplicateTime : false; if (queued) { return "one-by-one" /* oneByOne */; } if (cancelable) { return "previous-canceled" /* previousCanceled */; } if (canDeduplicate && deduplicate) { return "deduplicated" /* deduplicated */; } return "all-at-once" /* allAtOnce */; }; // src/request/request.utils.ts 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 timeElapsed = +progressDate - +startDate || 1; const uploadSpeed = loaded / timeElapsed; const totalValue = Math.max(total, loaded); const sizeLeft = totalValue - loaded; const estimatedTimeValue = uploadSpeed ? sizeLeft / uploadSpeed : null; const timeLeft = totalValue === loaded ? 0 : estimatedTimeValue; return { timeLeft, 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}`; }; var getRequestKey = (request, useInitialValues) => { const methodKey = stringifyKey(request.method); const endpointKey = useInitialValues ? request.requestOptions.endpoint : stringifyKey(request.endpoint); return `${methodKey}_${endpointKey}`; }; var getRequestDispatcher = (request, dispatcherType = "auto") => { const { fetchDispatcher, submitDispatcher } = request.client; const isGet = request.method === "GET" /* get */; const isFetchDispatcher = dispatcherType === "auto" && isGet || dispatcherType === "fetch"; const dispatcher = isFetchDispatcher ? fetchDispatcher : submitDispatcher; return [dispatcher, isFetchDispatcher]; }; var sendRequest = (request, options) => { const { requestManager } = request.client; const [dispatcher] = getRequestDispatcher(request, options == null ? void 0 : options.dispatcherType); return new Promise((resolve) => { var _a; let isResolved = false; const requestId = dispatcher.add(request); (_a = options == null ? void 0 : options.onSettle) == null ? void 0 : _a.call(options, requestId, request); const unmountRequestStart = requestManager.events.onRequestStartById( requestId, (...props) => { var _a2; return (_a2 = options == null ? void 0 : options.onRequestStart) == null ? void 0 : _a2.call(options, ...props); } ); const unmountResponseStart = requestManager.events.onResponseStartById( requestId, (...props) => { var _a2; return (_a2 = options == null ? void 0 : options.onResponseStart) == null ? void 0 : _a2.call(options, ...props); } ); const unmountUpload = requestManager.events.onUploadProgressById( requestId, (...props) => { var _a2; return (_a2 = options == null ? void 0 : options.onUploadProgress) == null ? void 0 : _a2.call(options, ...props); } ); const unmountDownload = requestManager.events.onDownloadProgressById( requestId, (...props) => { var _a2; return (_a2 = options == null ? void 0 : options.onDownloadProgress) == null ? void 0 : _a2.call(options, ...props); } ); const unmountResponse = requestManager.events.onResponseById(requestId, (response, details) => { var _a2; isResolved = true; const mapping = (_a2 = request.responseMapper) == null ? void 0 : _a2.call(request, response); const isOfflineStatus = request.offline && details.isOffline; const willRetry = canRetryRequest(details.retries, request.retry); const handleResponse = (success, data) => { var _a3; if (!success && isOfflineStatus) return; if (!success && willRetry) return; (_a3 = options == null ? void 0 : options.onResponse) == null ? void 0 : _a3.call(options, data, details); resolve(data); umountAll(); }; if (mapping instanceof Promise) { (() => __async(void 0, null, function* () { const responseData = yield mapping; const { success } = responseData; handleResponse(success, responseData); }))(); } else { const data = mapping || response; const { success } = data; handleResponse(success, data); } }); const unmountRemoveQueueElement = requestManager.events.onRemoveById(requestId, (...props) => { var _a2; if (!isResolved) { (_a2 = options == null ? void 0 : options.onRemove) == null ? void 0 : _a2.call(options, ...props); resolve({ data: null, status: null, success: null, error: getErrorMessage("deleted"), extra: request.client.defaultExtra }); umountAll(); } }); function umountAll() { unmountRequestStart(); unmountResponseStart(); unmountUpload(); unmountDownload(); unmountResponse(); unmountRemoveQueueElement(); } }); }; // src/mocker/mocker.ts var mocker = (_0, _1) => __async(void 0, [_0, _1], function* (request, { onError, onResponseEnd, onTimeoutError, onRequestEnd, createAbortListener, onResponseProgress, onRequestProgress, onResponseStart, onBeforeRequest, onRequestStart, onSuccess }) { const mock = request.mock.next(); const result = mock.value instanceof Function ? yield mock.value(request) : mock.value; return new Promise((resolve) => { const { data, status = 200, success = true, extra, config } = result; const { requestTime = 20, responseTime = 20, totalUploaded = 1, totalDownloaded = 1, timeout = false } = config || {}; createAbortListener(0, {}, () => { }, resolve); onBeforeRequest(); onRequestStart(); const progress = (totalTime, totalSize, progressFunction) => new Promise((resolveProgress) => { const interval = 20; const dataStart = +new Date(); const chunkSize = Math.floor(totalSize / Math.floor(totalTime / Math.min(totalTime, interval))); let currentlyLoaded = 0; const timer = setInterval(function handleProgressInterval() { const currentTime = Math.min(totalTime, +new Date() - dataStart); currentlyLoaded += currentlyLoaded + chunkSize >= totalSize ? totalSize - currentlyLoaded : chunkSize; if (currentTime >= totalTime) { resolveProgress(true); clearInterval(timer); } else { progressFunction({ total: totalSize, loaded: currentlyLoaded }); } }, interval); }); const getResponse = () => __async(void 0, null, function* () { yield progress(requestTime, totalUploaded, onRequestProgress); onRequestEnd(); onResponseStart(); yield progress(responseTime, totalDownloaded, onResponseProgress); if (success) { onSuccess(data, status, extra || {}, resolve); } else { onError(data, status, extra || {}, resolve); } }); if (timeout) { setTimeout(() => onTimeoutError(0, extra || {}, resolve), 1); } else { setTimeout(getResponse, requestTime + res