@krainovsd/js-helpers 0.19.0-beta.1 → 0.19.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cjs/index.cjs CHANGED
@@ -1757,13 +1757,14 @@ function throttle(func, interval) {
1757
1757
 
1758
1758
  const RESPONSE_DATA_SYMBOL = Symbol("response data");
1759
1759
  const REQUEST_ERROR = {
1760
- HTTP_ERROR: "http",
1761
- NETWORK_ERROR: "network",
1762
- TIMEOUT_ERROR: "timeout",
1763
- ABORT_ERROR: "abort",
1764
- VALIDATION_ERROR: "validation",
1765
- CACHE_ERROR: "cache",
1766
- UNKNOWN_ERROR: "unknown",
1760
+ Http: "http",
1761
+ Network: "network",
1762
+ Timeout: "timeout",
1763
+ Abort: "abort",
1764
+ Validation: "validation",
1765
+ Cache: "cache",
1766
+ Unknown: "unknown",
1767
+ Disabled: "disabled",
1767
1768
  };
1768
1769
  class OauthState {
1769
1770
  _fetching = false;
@@ -1789,6 +1790,7 @@ function createFetchClient(options) {
1789
1790
  let retries = options.retries;
1790
1791
  let timeout = options.timeout;
1791
1792
  let refetchAfterAuthGlobal = options.refetchAfterAuth;
1793
+ let enabled = options.enabled ?? true;
1792
1794
  function recreate(options) {
1793
1795
  if ("client" in options) {
1794
1796
  client = options.client;
@@ -1808,8 +1810,18 @@ function createFetchClient(options) {
1808
1810
  if ("refetchAfterAuth" in options) {
1809
1811
  refetchAfterAuthGlobal = options.refetchAfterAuth;
1810
1812
  }
1813
+ if ("enabled" in options) {
1814
+ enabled = options.enabled ?? true;
1815
+ }
1811
1816
  }
1812
1817
  async function handleRequest(request) {
1818
+ if (!enabled) {
1819
+ return {
1820
+ response: null,
1821
+ data: new ResponseError({ message: "requests disabled", status: 0 }),
1822
+ error: REQUEST_ERROR.Disabled,
1823
+ };
1824
+ }
1813
1825
  const timeoutController = new AbortController();
1814
1826
  const requestController = new AbortController();
1815
1827
  const requestTimeout = request.timeout ?? timeout;
@@ -1918,12 +1930,12 @@ function createFetchClient(options) {
1918
1930
  if (!response.ok) {
1919
1931
  if (response.status === 304) {
1920
1932
  const error = new ResponseError({
1921
- message: REQUEST_ERROR.CACHE_ERROR,
1933
+ message: REQUEST_ERROR.Cache,
1922
1934
  status: response.status,
1923
1935
  headers: Object.fromEntries(response.headers.entries()),
1924
1936
  });
1925
- request.onError?.(REQUEST_ERROR.CACHE_ERROR, error);
1926
- return { data: error, error: REQUEST_ERROR.CACHE_ERROR, response };
1937
+ request.onError?.(REQUEST_ERROR.Cache, error);
1938
+ return { data: error, error: REQUEST_ERROR.Cache, response };
1927
1939
  }
1928
1940
  /** if you need observe other than only 401 status, you can override some statuses in after handler middleware to 401 */
1929
1941
  if (response.status === 401 && refetchAfterAuth) {
@@ -1966,14 +1978,14 @@ function createFetchClient(options) {
1966
1978
  catch { }
1967
1979
  const error = new ResponseError({
1968
1980
  status: response.status,
1969
- message: REQUEST_ERROR.HTTP_ERROR,
1981
+ message: REQUEST_ERROR.Http,
1970
1982
  description: result,
1971
1983
  headers: Object.fromEntries(response.headers.entries()),
1972
1984
  });
1973
- request.onError?.(REQUEST_ERROR.HTTP_ERROR, error);
1985
+ request.onError?.(REQUEST_ERROR.Http, error);
1974
1986
  return {
1975
1987
  data: error,
1976
- error: REQUEST_ERROR.HTTP_ERROR,
1988
+ error: REQUEST_ERROR.Http,
1977
1989
  response,
1978
1990
  };
1979
1991
  }
@@ -2042,36 +2054,36 @@ function createFetchClient(options) {
2042
2054
  }
2043
2055
  if (err instanceof TypeError) {
2044
2056
  const error = new ResponseError({
2045
- message: REQUEST_ERROR.NETWORK_ERROR,
2057
+ message: REQUEST_ERROR.Network,
2046
2058
  status: 0,
2047
2059
  description: String(err.message),
2048
2060
  });
2049
- request.onError?.(REQUEST_ERROR.NETWORK_ERROR, error);
2050
- return { data: error, error: REQUEST_ERROR.NETWORK_ERROR, response: null };
2061
+ request.onError?.(REQUEST_ERROR.Network, error);
2062
+ return { data: error, error: REQUEST_ERROR.Network, response: null };
2051
2063
  }
2052
2064
  if (err instanceof Error && err.name === "AbortError") {
2053
2065
  if (timeoutController.signal.aborted) {
2054
2066
  const error = new ResponseError({
2055
- message: REQUEST_ERROR.TIMEOUT_ERROR,
2067
+ message: REQUEST_ERROR.Timeout,
2056
2068
  status: 0,
2057
2069
  });
2058
- request.onError?.(REQUEST_ERROR.TIMEOUT_ERROR, error);
2059
- return { data: error, error: REQUEST_ERROR.TIMEOUT_ERROR, response: null };
2070
+ request.onError?.(REQUEST_ERROR.Timeout, error);
2071
+ return { data: error, error: REQUEST_ERROR.Timeout, response: null };
2060
2072
  }
2061
2073
  const error = new ResponseError({
2062
- message: REQUEST_ERROR.ABORT_ERROR,
2074
+ message: REQUEST_ERROR.Abort,
2063
2075
  status: 0,
2064
2076
  });
2065
- request.onError?.(REQUEST_ERROR.ABORT_ERROR, error);
2066
- return { data: error, error: REQUEST_ERROR.ABORT_ERROR, response: null };
2077
+ request.onError?.(REQUEST_ERROR.Abort, error);
2078
+ return { data: error, error: REQUEST_ERROR.Abort, response: null };
2067
2079
  }
2068
2080
  const error = new ResponseError({
2069
- message: REQUEST_ERROR.UNKNOWN_ERROR,
2081
+ message: REQUEST_ERROR.Unknown,
2070
2082
  status: 0,
2071
2083
  description: String(err),
2072
2084
  });
2073
- request.onError?.(REQUEST_ERROR.UNKNOWN_ERROR, error);
2074
- return { data: error, error: REQUEST_ERROR.UNKNOWN_ERROR, response: null };
2085
+ request.onError?.(REQUEST_ERROR.Unknown, error);
2086
+ return { data: error, error: REQUEST_ERROR.Unknown, response: null };
2075
2087
  }
2076
2088
  finally {
2077
2089
  requestController.abort();
@@ -2112,18 +2124,48 @@ function executeAfterHandlers(handlers, request, response) {
2112
2124
  }
2113
2125
 
2114
2126
  function createOauthProvider(opts = {}) {
2115
- const { refreshTokenWindowUrl = window.origin, closeObserveSubWindowInterval = 500, waitSubWindow = 8000, loginUrl = undefined, logoutUrl = undefined, clearPageUrl = OAUTH_CLEAR_PAGE_URL, errorPageUrl = OAUTH_ERROR_PAGE_URL, logoutPageUrl = OAUTH_LOGOUT_PAGE_URL, afterClearPageUrl = "/", expiresTokenQueryName = OAUTH_TOKEN_EXPIRES_STORAGE_NAME, tokenStorageName = OAUTH_TOKEN_STORAGE_NAME, expiresTokenStorageName = OAUTH_TOKEN_EXPIRES_STORAGE_NAME, tokenRequest = undefined, forceSetToken = false, } = opts;
2127
+ const { refreshTokenWindowUrl = window.origin, closeObserveSubWindowInterval = 500, waitSubWindow = 15000, loginUrl = undefined, logoutUrl = undefined, clearPageUrl = OAUTH_CLEAR_PAGE_URL, errorPageUrl = OAUTH_ERROR_PAGE_URL, logoutPageUrl = OAUTH_LOGOUT_PAGE_URL, afterClearPageUrl = "/", expiresTokenQueryName = OAUTH_TOKEN_EXPIRES_STORAGE_NAME, tokenStorageName = OAUTH_TOKEN_STORAGE_NAME, expiresTokenStorageName = OAUTH_TOKEN_EXPIRES_STORAGE_NAME, tokenRequest = undefined, forceSetToken = false, subWindow = true, refreshToken = false, } = opts;
2116
2128
  let processing = false;
2129
+ function getExpiresDate(expires) {
2130
+ return Date.now() + (expires - 30) * 1000;
2131
+ }
2132
+ function checkExpires(expires) {
2133
+ return expires != undefined && !Number.isNaN(+expires) && Date.now() < +expires;
2134
+ }
2117
2135
  async function refetchAfterRefreshFlow(request) {
2118
2136
  if (processing) {
2119
- await beforeHandler(request);
2137
+ await beforeHandlerSetToken(request);
2120
2138
  return request;
2121
2139
  }
2122
- void startRefreshFlowInSubWindow();
2123
- await beforeHandler(request);
2140
+ processing = true;
2141
+ if (refreshToken && tokenRequest) {
2142
+ const tokenInfo = await startRefreshFlow();
2143
+ if (tokenInfo == undefined) {
2144
+ if (subWindow) {
2145
+ await startLoginFlowInSubWindow();
2146
+ }
2147
+ else {
2148
+ await startLoginFlow();
2149
+ }
2150
+ processing = false;
2151
+ await beforeHandlerSetToken(request);
2152
+ return request;
2153
+ }
2154
+ processing = false;
2155
+ await beforeHandlerSetToken(request);
2156
+ return request;
2157
+ }
2158
+ if (subWindow) {
2159
+ await startLoginFlowInSubWindow();
2160
+ }
2161
+ else {
2162
+ await startLoginFlow();
2163
+ }
2164
+ processing = false;
2165
+ await beforeHandlerSetToken(request);
2124
2166
  return request;
2125
2167
  }
2126
- async function startRefreshFlowInSubWindow() {
2168
+ async function startLoginFlowInSubWindow() {
2127
2169
  processing = true;
2128
2170
  let waiting = true;
2129
2171
  const url = new URL(typeof refreshTokenWindowUrl === "function" ? refreshTokenWindowUrl() : refreshTokenWindowUrl);
@@ -2165,7 +2207,7 @@ function createOauthProvider(opts = {}) {
2165
2207
  if ("onSubWindowOpenError" in opts)
2166
2208
  opts.onSubWindowOpenError?.();
2167
2209
  else {
2168
- startLoginFlow();
2210
+ await startLoginFlow();
2169
2211
  }
2170
2212
  waiting = false;
2171
2213
  return;
@@ -2174,21 +2216,34 @@ function createOauthProvider(opts = {}) {
2174
2216
  processing = false;
2175
2217
  }
2176
2218
  // Trigger an oauth flow through some proxy server
2177
- function startLoginFlow(loginUrlArg = loginUrl) {
2219
+ async function startLoginFlow(loginUrlArg = loginUrl, delay = 2000) {
2178
2220
  loginUrlArg ??= `/api/v1/auth?frontend_protocol=${window.location.protocol.replace(":", "")}&frontend_host=${window.location.host}&comeback_path=${window.location.pathname}${encodeURIComponent(window.location.search)}`;
2179
2221
  window.location.replace(typeof loginUrlArg === "function" ? loginUrlArg() : loginUrlArg);
2222
+ await wait(delay);
2180
2223
  return null;
2181
2224
  }
2182
2225
  // Trigger an oauth flow through some proxy server
2183
- function startLogoutFlow(logoutUrlArg = logoutUrl) {
2184
- logoutUrlArg ??= `/api/v1/auth/logout?frontend_protocol=${window.location.protocol.replace(":", "")}&frontend_host=${window.location.host}&comeback_path=${window.location.pathname}${encodeURIComponent(window.location.search)}`;
2226
+ async function startLogoutFlow(logoutUrlArg = logoutUrl, delay = 2000) {
2227
+ logoutUrlArg ??= `/api/v1/auth/logout?frontend_protocol=${window.location.protocol.replace(":", "")}&frontend_host=${window.location.host}`;
2185
2228
  window.location.replace(typeof logoutUrlArg === "function" ? logoutUrlArg() : logoutUrlArg);
2229
+ await wait(delay);
2186
2230
  return null;
2187
2231
  }
2232
+ async function startRefreshFlow() {
2233
+ const tokenInfo = await tokenRequest?.();
2234
+ if (tokenInfo) {
2235
+ localStorage.setItem(tokenStorageName, tokenInfo.token);
2236
+ if (tokenInfo.expires !== 0) {
2237
+ tokenInfo.expires = getExpiresDate(tokenInfo.expires);
2238
+ localStorage.setItem(expiresTokenStorageName, String(tokenInfo.expires));
2239
+ }
2240
+ }
2241
+ return tokenInfo;
2242
+ }
2188
2243
  // Processing an oauth flow: extract expires, get token, close sub windows, check if logout/clear/error url
2189
2244
  async function register() {
2190
2245
  if (window.location.pathname === logoutPageUrl) {
2191
- startLogoutFlow();
2246
+ await startLogoutFlow();
2192
2247
  return false;
2193
2248
  }
2194
2249
  if (window.location.pathname === clearPageUrl) {
@@ -2212,30 +2267,29 @@ function createOauthProvider(opts = {}) {
2212
2267
  ? refreshQuery[refreshQuery.length - 1] === "true"
2213
2268
  : false;
2214
2269
  /** Expires token */
2215
- const expires = isString(expiresQuery)
2270
+ const expiresStr = isString(expiresQuery)
2216
2271
  ? expiresQuery
2217
2272
  : isArray(expiresQuery)
2218
2273
  ? expiresQuery[expiresQuery.length - 1]
2219
- : false;
2220
- if (expires && !Number.isNaN(+expires) && Date.now() < +expires) {
2221
- localStorage.setItem(expiresTokenStorageName, expires);
2274
+ : null;
2275
+ const expires = expiresStr ? getExpiresDate(+expiresStr) : null;
2276
+ if (checkExpires(expires)) {
2277
+ localStorage.setItem(expiresTokenStorageName, String(expires));
2222
2278
  if (tokenRequest) {
2223
- const token = await tokenRequest();
2224
- if (token != undefined && tokenStorageName) {
2225
- localStorage.setItem(tokenStorageName, token);
2279
+ const tokenInfo = await tokenRequest();
2280
+ if (tokenInfo != undefined) {
2281
+ localStorage.setItem(tokenStorageName, tokenInfo.token);
2226
2282
  }
2227
2283
  }
2228
- }
2229
- /** Close if OnlyRefresh window */
2230
- if (isRefresh) {
2231
- const channel = new BroadcastChannel(OAUTH_REFRESH_QUERY);
2232
- channel.postMessage(true);
2233
- channel.close();
2234
- window.close();
2235
- return false;
2236
- }
2237
- /** Delete expires query */
2238
- if (expires) {
2284
+ /** Close if OnlyRefresh window */
2285
+ if (isRefresh) {
2286
+ const channel = new BroadcastChannel(OAUTH_REFRESH_QUERY);
2287
+ channel.postMessage(true);
2288
+ channel.close();
2289
+ window.close();
2290
+ return false;
2291
+ }
2292
+ /** Delete expires query */
2239
2293
  const url = new URL(window.location.href);
2240
2294
  url.searchParams.delete(expiresTokenQueryName);
2241
2295
  window.location.replace(url.toString());
@@ -2243,7 +2297,14 @@ function createOauthProvider(opts = {}) {
2243
2297
  }
2244
2298
  return true;
2245
2299
  }
2246
- async function beforeHandler(request) {
2300
+ async function beforeHandlerCheckExpires(request) {
2301
+ const expires = localStorage.getItem(expiresTokenStorageName);
2302
+ if (checkExpires(expires)) {
2303
+ return;
2304
+ }
2305
+ await refetchAfterRefreshFlow(request);
2306
+ }
2307
+ async function beforeHandlerSetToken(request) {
2247
2308
  if (processing)
2248
2309
  await waitUntil(() => processing);
2249
2310
  const token = request.token ?? localStorage.getItem(tokenStorageName);
@@ -2255,12 +2316,17 @@ function createOauthProvider(opts = {}) {
2255
2316
  };
2256
2317
  }
2257
2318
  return {
2258
- startRefreshFlowInSubWindow,
2319
+ startRefreshFlow,
2320
+ startLoginFlowInSubWindow,
2259
2321
  startLoginFlow,
2260
2322
  startLogoutFlow,
2261
- register,
2262
2323
  refetchAfterRefreshFlow,
2263
- beforeHandler,
2324
+ beforeHandlerSetToken,
2325
+ beforeHandlerCheckExpires,
2326
+ register,
2327
+ get isServicePage() {
2328
+ return [errorPageUrl, clearPageUrl, logoutPageUrl].some((u) => document.location.pathname === u);
2329
+ },
2264
2330
  get processing() {
2265
2331
  return processing;
2266
2332
  },