@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
JavaScript
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