@mints/request
Advanced tools
+24
-0
| # Changelog | ||
| ## [2.1.0] - 2025-08-25 | ||
| ⚠️ **Note:** version `2.0.0` was essentially broken due to severe bugs. | ||
| Upgrade to `2.1.0` immediately. | ||
| ### ✨ Features | ||
| - Added `login()` and `logout()` helpers to manage tokens automatically. | ||
| - Added `init()` and `reset()` methods to `request` for probe/reset flows. | ||
| - Added `tokenField` option in `createCookieStrategy` and `createTokenStrategy`. | ||
| - Added default handling for HTTP `419` / `440`. | ||
| ### 🐛 Fixes | ||
| - Fixed missing `storage` export. | ||
| - Fixed infinite loop in `ensureRefreshed` (refresh never exited). | ||
| - Fixed `mountedRef.current` not reset (broke `useRequest` under React Strict Mode). | ||
| ### 🛠 Refactors | ||
| - Removed `isAccessTokenValid` param from `AuthStrategy` (backend decides validity). | ||
| - Removed `useRequest` throwing on cancellation errors (`AbortError`, `ERR_CANCELED`, `CanceledError`). | ||
| - Internally refactored `useRequest` to rely on `status` instead of `loading`. | ||
| ## [2.0.0] - 2025-08-22 | ||
@@ -4,0 +28,0 @@ |
+90
-31
@@ -36,2 +36,6 @@ "use strict"; | ||
| getGlobalRequestConfig: () => getGlobalRequestConfig, | ||
| localStorageStorage: () => localStorageStorage, | ||
| login: () => login, | ||
| logout: () => logout, | ||
| memoryStorage: () => memoryStorage, | ||
| operator: () => operator, | ||
@@ -50,3 +54,3 @@ request: () => request, | ||
| retryAfterRefresh: 1, | ||
| shouldRefreshOnStatus: (s) => s === 401 | ||
| shouldRefreshOnStatus: (s) => s === 401 || s === 419 || s === 440 | ||
| }; | ||
@@ -87,5 +91,3 @@ var setupRequest = (config) => { | ||
| } finally { | ||
| const p = refreshPromise; | ||
| refreshPromise = null; | ||
| await (p == null ? void 0 : p.catch(() => void 0)); | ||
| } | ||
@@ -117,3 +119,3 @@ })(); | ||
| async (error) => { | ||
| var _a, _b, _c, _d, _e, _f, _g, _h, _i; | ||
| var _a, _b, _c, _d, _e, _f, _g; | ||
| const global = getGlobalRequestConfig(); | ||
@@ -127,5 +129,2 @@ const cfg = error.config || {}; | ||
| } | ||
| if ((_e = (_d = global.auth).isAccessTokenValid) == null ? void 0 : _e.call(_d)) { | ||
| return Promise.reject(error); | ||
| } | ||
| try { | ||
@@ -136,5 +135,5 @@ await ensureRefreshed(cfg.signal); | ||
| } catch (refreshErr) { | ||
| (_g = (_f = global.auth).onRefreshFailed) == null ? void 0 : _g.call(_f, refreshErr); | ||
| if (!((_h = cfg.meta) == null ? void 0 : _h.skipUnauthorizedHandler)) { | ||
| (_i = global.onUnauthorized) == null ? void 0 : _i.call(global); | ||
| (_e = (_d = global.auth).onRefreshFailed) == null ? void 0 : _e.call(_d, refreshErr); | ||
| if (!((_f = cfg.meta) == null ? void 0 : _f.skipUnauthorizedHandler)) { | ||
| (_g = global.onUnauthorized) == null ? void 0 : _g.call(global); | ||
| } | ||
@@ -176,2 +175,36 @@ return Promise.reject(error); | ||
| }; | ||
| var didInitSoftRefresh = false; | ||
| async function probe(url, config) { | ||
| return request.auth(url, { | ||
| ...config, | ||
| noRefresh: true, | ||
| meta: { | ||
| ...(config == null ? void 0 : config.meta) || {}, | ||
| skipUnauthorizedHandler: true | ||
| } | ||
| }); | ||
| } | ||
| request.init = async function(url, config) { | ||
| var _a; | ||
| try { | ||
| return await probe(url, config); | ||
| } catch (e) { | ||
| const soft = (_a = config == null ? void 0 : config.soft) != null ? _a : true; | ||
| if (!soft || didInitSoftRefresh) | ||
| return null; | ||
| const auth = getGlobalRequestConfig().auth; | ||
| if (!auth) | ||
| return null; | ||
| try { | ||
| await auth.refresh(config == null ? void 0 : config.signal); | ||
| didInitSoftRefresh = true; | ||
| return await probe(url, config); | ||
| } catch (e2) { | ||
| return null; | ||
| } | ||
| } | ||
| }; | ||
| request.reset = function() { | ||
| didInitSoftRefresh = false; | ||
| }; | ||
@@ -289,16 +322,6 @@ // src/core/operator.ts | ||
| } | ||
| function isJwtValid(t, leewaySec = 30) { | ||
| if (!t) | ||
| return false; | ||
| const [, payload] = t.split("."); | ||
| try { | ||
| const { exp } = JSON.parse(atob(payload)); | ||
| return typeof exp === "number" && Date.now() / 1e3 < exp - leewaySec; | ||
| } catch (e) { | ||
| return false; | ||
| } | ||
| } | ||
| function createCookieStrategy(opts) { | ||
| var _a, _b; | ||
| const storage = (_a = opts.storage) != null ? _a : memoryStorage; | ||
| const accessKey = (_b = opts == null ? void 0 : opts.tokenField) != null ? _b : "access_token"; | ||
| return { | ||
@@ -308,3 +331,2 @@ addAuthToRequest(config) { | ||
| }, | ||
| isAccessTokenValid: (_b = opts.isTokenValid) != null ? _b : () => isJwtValid(storage.getAccessToken()), | ||
| async refresh(signal) { | ||
@@ -323,4 +345,4 @@ const res = await fetch(opts.refreshPath, { | ||
| const json = await res.json(); | ||
| if (json == null ? void 0 : json.access_token) { | ||
| storage.setAccessToken(json.access_token); | ||
| if (json == null ? void 0 : json[accessKey]) { | ||
| storage.setAccessToken(json[accessKey]); | ||
| } | ||
@@ -330,5 +352,13 @@ } catch (e) { | ||
| }, | ||
| onRefreshFailed(reason) { | ||
| setToken({ accessToken, refreshToken }) { | ||
| var _a2; | ||
| if (accessToken) | ||
| storage.setAccessToken(accessToken); | ||
| if (refreshToken) | ||
| (_a2 = storage.setRefreshToken) == null ? void 0 : _a2.call(storage, refreshToken); | ||
| }, | ||
| clearToken() { | ||
| var _a2; | ||
| storage.setAccessToken(null); | ||
| console.warn("Refresh failed", reason); | ||
| (_a2 = storage.setRefreshToken) == null ? void 0 : _a2.call(storage, null); | ||
| } | ||
@@ -340,2 +370,3 @@ }; | ||
| const storage = (_a = opts.storage) != null ? _a : localStorageStorage; | ||
| const accessKey = (_b = opts == null ? void 0 : opts.tokenField) != null ? _b : "access_token"; | ||
| return { | ||
@@ -345,3 +376,2 @@ addAuthToRequest(config) { | ||
| }, | ||
| isAccessTokenValid: (_b = opts.isTokenValid) != null ? _b : () => isJwtValid(storage.getAccessToken()), | ||
| async refresh(signal) { | ||
@@ -364,16 +394,41 @@ var _a2, _b2; | ||
| const json = await res.json(); | ||
| if (!(json == null ? void 0 : json.access_token)) | ||
| if (!(json == null ? void 0 : json[accessKey])) | ||
| throw new Error("Malformed refresh response"); | ||
| storage.setAccessToken(json.access_token); | ||
| storage.setAccessToken(json[accessKey]); | ||
| if (json.refresh_token) | ||
| (_b2 = storage.setRefreshToken) == null ? void 0 : _b2.call(storage, json.refresh_token); | ||
| }, | ||
| onRefreshFailed(reason) { | ||
| setToken({ accessToken, refreshToken }) { | ||
| var _a2; | ||
| if (accessToken) | ||
| storage.setAccessToken(accessToken); | ||
| if (refreshToken) | ||
| (_a2 = storage.setRefreshToken) == null ? void 0 : _a2.call(storage, refreshToken); | ||
| }, | ||
| clearToken() { | ||
| var _a2; | ||
| storage.setAccessToken(null); | ||
| (_a2 = storage.setRefreshToken) == null ? void 0 : _a2.call(storage, null); | ||
| console.warn("Refresh failed", reason); | ||
| } | ||
| }; | ||
| } | ||
| // src/core/auth-helper.ts | ||
| async function login(perform) { | ||
| var _a; | ||
| const auth = getGlobalRequestConfig().auth; | ||
| const json = await perform(); | ||
| (_a = auth == null ? void 0 : auth.setToken) == null ? void 0 : _a.call(auth, json); | ||
| return json; | ||
| } | ||
| async function logout(perform) { | ||
| var _a; | ||
| const auth = getGlobalRequestConfig().auth; | ||
| try { | ||
| if (perform) | ||
| await perform(); | ||
| } finally { | ||
| (_a = auth == null ? void 0 : auth.clearToken) == null ? void 0 : _a.call(auth); | ||
| } | ||
| } | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
@@ -384,2 +439,6 @@ 0 && (module.exports = { | ||
| getGlobalRequestConfig, | ||
| localStorageStorage, | ||
| login, | ||
| logout, | ||
| memoryStorage, | ||
| operator, | ||
@@ -386,0 +445,0 @@ request, |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/core/index.ts","../src/core/request.ts","../src/core/config.ts","../src/core/operator.ts","../src/core/auth.ts","../src/core/storage.ts"],"sourcesContent":["export { request } from './request';\nexport {\n setupRequest,\n updateRequestConfig,\n getGlobalRequestConfig,\n type AuthStrategy,\n type GlobalRequestConfig,\n} from './config';\nexport { operator, type OperateConfig } from './operator';\nexport { createCookieStrategy, createTokenStrategy } from './auth';\n","import axios, {\n AxiosHeaders,\n type InternalAxiosRequestConfig,\n type AxiosError,\n type AxiosRequestConfig,\n type AxiosResponse,\n} from 'axios';\n\nimport { getGlobalRequestConfig } from './config';\n\n/** Per-request behavior toggles */\nexport type RequestMeta = {\n /** Do not attach credentials (skip strategy.addAuthToRequest) */\n skipAuth?: boolean;\n /** Do not attempt refresh on 401/419 */\n skipRefresh?: boolean;\n /** Do not call global onUnauthorized when unauthorized */\n skipUnauthorizedHandler?: boolean;\n /** Internal guard to avoid infinite retry loops */\n _retried?: number;\n};\n\n// Axios module augmentation to allow config.meta\ndeclare module 'axios' {\n export interface AxiosRequestConfig {\n meta?: RequestMeta;\n }\n}\n\nconst instance = axios.create();\n\n// ---- request interceptor: baseURL, headers, auth attachment ----\ninstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {\n const global = getGlobalRequestConfig();\n\n // Merge headers (defaultHeaders first so explicit config wins)\n const mergedHeaders = {\n ...(global.defaultHeaders?.() || {}),\n ...(config.headers || {}),\n };\n\n config.headers = AxiosHeaders.from(mergedHeaders);\n config.baseURL = config.baseURL || global.baseURL || '/api';\n\n // Attach Authorization (or other credentials) if strategy is present and not skipped\n if (!config.meta?.skipAuth && global.auth) {\n config = global.auth.addAuthToRequest(config);\n }\n\n return config;\n});\n\n// ---- refresh de-duplication ----\nlet refreshPromise: Promise<void> | null = null;\n\nasync function ensureRefreshed(signal?: AbortSignal) {\n const { auth } = getGlobalRequestConfig();\n if (!auth) return;\n\n if (!refreshPromise) {\n refreshPromise = (async () => {\n try {\n await auth.refresh(signal);\n } finally {\n // Reset promise regardless of success/failure\n const p = refreshPromise;\n refreshPromise = null;\n await (p as Promise<void> | null)?.catch(() => undefined);\n }\n })();\n }\n return refreshPromise;\n}\n\nfunction shouldAttemptRefresh(error: AxiosError): boolean {\n const { shouldRefreshOnStatus } = getGlobalRequestConfig();\n const status = error.response?.status;\n return typeof status === 'number' && !!shouldRefreshOnStatus?.(status);\n}\n\nasync function retryOnce<T>(\n original: InternalAxiosRequestConfig,\n): Promise<AxiosResponse<T>> {\n const global = getGlobalRequestConfig();\n const retryMax = Math.max(1, global.retryAfterRefresh ?? 1);\n\n const meta = original.meta || (original.meta = {});\n const count = meta._retried ?? 0;\n\n if (count >= retryMax) {\n throw new Error('Max retries after refresh exceeded');\n }\n\n meta._retried = count + 1;\n return instance.request<T>(original);\n}\n\n// ---- response interceptor: 401 -> refresh -> retry ----\ninstance.interceptors.response.use(\n (res) => res,\n async (error: AxiosError) => {\n const global = getGlobalRequestConfig();\n const cfg = (error.config || {}) as InternalAxiosRequestConfig;\n\n // If no strategy or refresh is skipped, do not handle here\n if (cfg.meta?.skipRefresh || !global.auth || !shouldAttemptRefresh(error)) {\n if (shouldAttemptRefresh(error) && !cfg.meta?.skipUnauthorizedHandler) {\n global.onUnauthorized?.();\n }\n return Promise.reject(error);\n }\n\n // Optional proactive validity check (if provided)\n if (global.auth.isAccessTokenValid?.()) {\n // Token claims still \"valid\" but server says unauthorized -> bubble up\n return Promise.reject(error);\n }\n\n try {\n await ensureRefreshed(cfg.signal as AbortSignal | undefined);\n const retried = await retryOnce(cfg);\n return retried;\n } catch (refreshErr) {\n global.auth.onRefreshFailed?.(refreshErr);\n if (!cfg.meta?.skipUnauthorizedHandler) {\n global.onUnauthorized?.();\n }\n return Promise.reject(error);\n }\n },\n);\n\n// ---- public/auth helpers on top of base request() ----\n\ntype CredentialsMode = 'auto' | 'always' | 'never';\n\nexport type PublicOptions = { credentials?: CredentialsMode };\nexport type AuthOptions = {\n noRefresh?: boolean;\n credentials?: CredentialsMode;\n};\n\n// Core callable function (default = public)\nexport type RequestFn = <T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n) => Promise<T>;\n\nasync function baseRequest<T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n): Promise<T> {\n const res = await instance.request<T>({ url, ...config });\n return res.data;\n}\n\n/**\n * The exported request is both callable and has .public/.auth shortcuts.\n * Calling request(url, config) equals request.public(url, config).\n */\nconst request = ((url: string, config?: AxiosRequestConfig) => {\n return request.public(url, config);\n}) as RequestFn & {\n public: <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & PublicOptions,\n ) => Promise<T>;\n auth: <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & AuthOptions,\n ) => Promise<T>;\n};\n\nrequest.public = async function <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & PublicOptions,\n): Promise<T> {\n const withCredentials = config?.credentials === 'always' ? true : undefined;\n return baseRequest<T>(url, {\n ...config,\n withCredentials,\n meta: {\n ...(config?.meta || {}),\n skipAuth: true,\n skipRefresh: true,\n skipUnauthorizedHandler: true,\n },\n });\n};\n\nrequest.auth = async function <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & AuthOptions,\n): Promise<T> {\n const withCredentials = config?.credentials === 'always' ? true : undefined;\n return baseRequest<T>(url, {\n ...config,\n withCredentials,\n meta: {\n ...(config?.meta || {}),\n ...(config?.noRefresh ? { skipRefresh: true } : {}),\n },\n });\n};\n\nexport { request };\nexport type { AxiosRequestConfig } from 'axios';\n","import type { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';\n\nexport type ToastFn = (msg: string) => void;\n\nexport interface AuthStrategy {\n /** Attach credentials (e.g., Authorization header) */\n addAuthToRequest: <T extends AxiosRequestConfig | InternalAxiosRequestConfig>(\n config: T,\n ) => T;\n\n /** Optional: return true if access token is still valid */\n isAccessTokenValid?: () => boolean;\n\n /** Refresh credentials; may rely on http-only cookie or token exchange */\n refresh: (signal?: AbortSignal) => Promise<void>;\n\n /** Optional: called when refresh ultimately fails */\n onRefreshFailed?: (reason: unknown) => void;\n}\n\nexport interface GlobalRequestConfig {\n toast?: {\n success?: ToastFn;\n error?: ToastFn;\n };\n baseURL?: string;\n defaultHeaders?: () => Record<string, string>;\n onUnauthorized?: () => void;\n\n /** Pluggable authentication behavior */\n auth?: AuthStrategy;\n\n /** Max retries after a successful refresh (default: 1) */\n retryAfterRefresh?: number;\n\n /** Status codes that should trigger a refresh attempt (default: 401) */\n shouldRefreshOnStatus?: (status: number) => boolean;\n}\n\nconst globalConfig: GlobalRequestConfig = {\n retryAfterRefresh: 1,\n shouldRefreshOnStatus: (s) => s === 401,\n};\n\nexport const setupRequest = (config: GlobalRequestConfig) => {\n Object.assign(globalConfig, config);\n};\n\nexport const updateRequestConfig = (patch: Partial<GlobalRequestConfig>) => {\n Object.assign(globalConfig, patch);\n};\n\nexport const getGlobalRequestConfig = () => globalConfig;\n","import axios, { type AxiosError } from 'axios';\n\nimport { getGlobalRequestConfig, type ToastFn } from './config';\n\nexport type OperateConfig<E> = {\n setOperating?: (loading: boolean) => void;\n formatMessage?: () => string;\n formatReason?: (err: E) => string;\n hideToast?: boolean;\n toast?: {\n success?: ToastFn;\n error?: ToastFn;\n };\n};\n\ntype ApiErrorBody = {\n message?: string;\n error?: string;\n detail?: string;\n errors?: string | string[];\n};\n\nfunction extractAxiosMessage(err: AxiosError<ApiErrorBody>): string {\n const data = err.response?.data;\n\n if (typeof data === 'string') return data;\n\n if (data && typeof data === 'object') {\n const { message, error, detail, errors } = data;\n const merged =\n message ??\n error ??\n detail ??\n (Array.isArray(errors) ? errors.join(', ') : errors);\n if (merged) return merged;\n }\n\n return err.message || 'Unknown error';\n}\n\nexport async function operator<T, E = unknown>(\n request: () => Promise<T>,\n config: OperateConfig<E> = {},\n): Promise<[true, T?] | [false, E?]> {\n const global = getGlobalRequestConfig();\n const {\n setOperating,\n formatMessage,\n formatReason = (err: E) => {\n if (axios.isAxiosError<ApiErrorBody>(err)) {\n return extractAxiosMessage(err);\n }\n if (err instanceof Error) return err.message;\n return String(err ?? 'Unknown error');\n },\n hideToast,\n toast: localToast,\n } = config;\n\n const toast = localToast ?? global.toast;\n\n try {\n setOperating?.(true);\n const res = await request();\n if (!hideToast) toast?.success?.(formatMessage?.() ?? 'Success');\n return [true, res];\n } catch (err) {\n const typed = err as E;\n if (!hideToast) toast?.error?.(formatReason(typed));\n return [false, typed];\n } finally {\n setOperating?.(false);\n }\n}\n","import { AxiosHeaders, type AxiosRequestConfig } from 'axios';\n\nimport type { AuthStrategy } from './config';\nimport {\n memoryStorage,\n localStorageStorage,\n type TokenStorage,\n} from './storage';\n\nfunction addAuthHeader<T extends AxiosRequestConfig>(\n config: T,\n token: string | null,\n): T {\n if (token) {\n config.headers = AxiosHeaders.from({\n ...(config.headers || {}),\n Authorization: `Bearer ${token}`,\n });\n }\n return config;\n}\n\nfunction isJwtValid(t: string | null, leewaySec = 30) {\n if (!t) return false;\n const [, payload] = t.split('.');\n try {\n const { exp } = JSON.parse(atob(payload));\n return typeof exp === 'number' && Date.now() / 1000 < exp - leewaySec;\n } catch {\n return false;\n }\n}\n\n/**\n * Cookie-based strategy:\n * - Access token lives in app state (memory/storage).\n * - Refresh relies on http-only cookie via a server endpoint.\n */\nexport function createCookieStrategy(opts: {\n storage?: TokenStorage;\n refreshPath: string; // e.g., '/auth/refresh'\n isTokenValid?: () => boolean;\n}): AuthStrategy {\n const storage = opts.storage ?? memoryStorage;\n\n return {\n addAuthToRequest<T extends AxiosRequestConfig>(config: T): T {\n return addAuthHeader(config, storage.getAccessToken());\n },\n isAccessTokenValid:\n opts.isTokenValid ?? (() => isJwtValid(storage.getAccessToken())),\n async refresh(signal?: AbortSignal) {\n const res = await fetch(opts.refreshPath, {\n method: 'POST',\n credentials: 'include',\n headers: {\n Accept: 'application/json',\n },\n signal,\n });\n if (!res.ok) throw new Error(`Refresh failed: ${res.status}`);\n try {\n const json = await res.json();\n if (json?.access_token) {\n storage.setAccessToken(json.access_token);\n }\n } catch {\n // Some backends may not return JSON; cookie-only session is fine.\n }\n },\n onRefreshFailed(reason) {\n storage.setAccessToken(null);\n console.warn('Refresh failed', reason);\n },\n };\n}\n\n/**\n * Token-exchange strategy:\n * - Both access and refresh tokens are readable by JS (NOT http-only).\n */\nexport function createTokenStrategy(opts: {\n storage?: TokenStorage;\n refreshPath: string;\n isTokenValid?: () => boolean;\n}): AuthStrategy {\n const storage = opts.storage ?? localStorageStorage;\n\n return {\n addAuthToRequest<T extends AxiosRequestConfig>(config: T): T {\n return addAuthHeader(config, storage.getAccessToken());\n },\n isAccessTokenValid:\n opts.isTokenValid ?? (() => isJwtValid(storage.getAccessToken())),\n async refresh(signal?: AbortSignal) {\n const rt = storage.getRefreshToken?.();\n if (!rt) throw new Error('No refresh token');\n const res = await fetch(opts.refreshPath, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({ refresh_token: rt }),\n signal,\n });\n if (!res.ok) throw new Error(`Refresh failed: ${res.status}`);\n const json = await res.json();\n if (!json?.access_token) throw new Error('Malformed refresh response');\n storage.setAccessToken(json.access_token);\n if (json.refresh_token) storage.setRefreshToken?.(json.refresh_token);\n },\n onRefreshFailed(reason) {\n storage.setAccessToken(null);\n storage.setRefreshToken?.(null);\n console.warn('Refresh failed', reason);\n },\n };\n}\n","export interface TokenStorage {\n getAccessToken(): string | null;\n setAccessToken(token: string | null): void;\n getRefreshToken?(): string | null;\n setRefreshToken?(token: string | null): void;\n}\n\nexport const memoryStorage: TokenStorage = (() => {\n let access: string | null = null;\n let refresh: string | null = null;\n return {\n getAccessToken: () => access,\n setAccessToken: (t) => (access = t),\n getRefreshToken: () => refresh,\n setRefreshToken: (t) => (refresh = t),\n };\n})();\n\nconst safeLocal = () => {\n try {\n return typeof window !== 'undefined' ? window.localStorage : null;\n } catch {\n return null;\n }\n};\n\nexport const localStorageStorage: TokenStorage = {\n getAccessToken: () => safeLocal()?.getItem('access_token') ?? null,\n setAccessToken: (t) => {\n const ls = safeLocal();\n if (!ls) return;\n if (t) {\n ls.setItem('access_token', t);\n } else {\n ls.removeItem('access_token');\n }\n },\n getRefreshToken: () => safeLocal()?.getItem('refresh_token') ?? null,\n setRefreshToken: (t) => {\n const ls = safeLocal();\n if (!ls) return;\n if (t) {\n ls.setItem('refresh_token', t);\n } else {\n ls.removeItem('refresh_token');\n }\n },\n};\n"],"mappings":"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","names":["axios","import_axios","request","axios","import_axios","_a","_b"]} | ||
| {"version":3,"sources":["../src/core/index.ts","../src/core/request.ts","../src/core/config.ts","../src/core/operator.ts","../src/core/auth.ts","../src/core/storage.ts","../src/core/auth-helper.ts"],"sourcesContent":["export { request } from './request';\nexport {\n setupRequest,\n updateRequestConfig,\n getGlobalRequestConfig,\n type AuthStrategy,\n type GlobalRequestConfig,\n} from './config';\nexport { operator, type OperateConfig } from './operator';\nexport { createCookieStrategy, createTokenStrategy } from './auth';\nexport { memoryStorage, localStorageStorage } from './storage';\nexport { login, logout } from './auth-helper';\n","import axios, {\n AxiosHeaders,\n type InternalAxiosRequestConfig,\n type AxiosError,\n type AxiosRequestConfig,\n type AxiosResponse,\n} from 'axios';\n\nimport { getGlobalRequestConfig } from './config';\n\n/** Per-request behavior toggles */\nexport type RequestMeta = {\n /** Do not attach credentials (skip strategy.addAuthToRequest) */\n skipAuth?: boolean;\n /** Do not attempt refresh on 401/419 */\n skipRefresh?: boolean;\n /** Do not call global onUnauthorized when unauthorized */\n skipUnauthorizedHandler?: boolean;\n /** Internal guard to avoid infinite retry loops */\n _retried?: number;\n};\n\n// Axios module augmentation to allow config.meta\ndeclare module 'axios' {\n export interface AxiosRequestConfig {\n meta?: RequestMeta;\n }\n}\n\nconst instance = axios.create();\n\n// ---- request interceptor: baseURL, headers, auth attachment ----\ninstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {\n const global = getGlobalRequestConfig();\n\n // Merge headers (defaultHeaders first so explicit config wins)\n const mergedHeaders = {\n ...(global.defaultHeaders?.() || {}),\n ...(config.headers || {}),\n };\n\n config.headers = AxiosHeaders.from(mergedHeaders);\n config.baseURL = config.baseURL || global.baseURL || '/api';\n\n // Attach Authorization (or other credentials) if strategy is present and not skipped\n if (!config.meta?.skipAuth && global.auth) {\n config = global.auth.addAuthToRequest(config);\n }\n\n return config;\n});\n\n// ---- refresh de-duplication ----\nlet refreshPromise: Promise<void> | null = null;\n\nasync function ensureRefreshed(signal?: AbortSignal) {\n const { auth } = getGlobalRequestConfig();\n if (!auth) return;\n\n if (!refreshPromise) {\n refreshPromise = (async () => {\n try {\n await auth.refresh(signal);\n } finally {\n refreshPromise = null;\n }\n })();\n }\n return refreshPromise;\n}\n\nfunction shouldAttemptRefresh(error: AxiosError): boolean {\n const { shouldRefreshOnStatus } = getGlobalRequestConfig();\n const status = error.response?.status;\n return typeof status === 'number' && !!shouldRefreshOnStatus?.(status);\n}\n\nasync function retryOnce<T>(\n original: InternalAxiosRequestConfig,\n): Promise<AxiosResponse<T>> {\n const global = getGlobalRequestConfig();\n const retryMax = Math.max(1, global.retryAfterRefresh ?? 1);\n\n const meta = original.meta || (original.meta = {});\n const count = meta._retried ?? 0;\n\n if (count >= retryMax) {\n throw new Error('Max retries after refresh exceeded');\n }\n\n meta._retried = count + 1;\n return instance.request<T>(original);\n}\n\n// ---- response interceptor: 401 -> refresh -> retry ----\ninstance.interceptors.response.use(\n (res) => res,\n async (error: AxiosError) => {\n const global = getGlobalRequestConfig();\n const cfg = (error.config || {}) as InternalAxiosRequestConfig;\n\n // If no strategy or refresh is skipped, do not handle here\n if (cfg.meta?.skipRefresh || !global.auth || !shouldAttemptRefresh(error)) {\n if (shouldAttemptRefresh(error) && !cfg.meta?.skipUnauthorizedHandler) {\n global.onUnauthorized?.();\n }\n return Promise.reject(error);\n }\n\n try {\n await ensureRefreshed(cfg.signal as AbortSignal | undefined);\n const retried = await retryOnce(cfg);\n return retried;\n } catch (refreshErr) {\n global.auth.onRefreshFailed?.(refreshErr);\n if (!cfg.meta?.skipUnauthorizedHandler) {\n global.onUnauthorized?.();\n }\n return Promise.reject(error);\n }\n },\n);\n\n// ---- public/auth helpers on top of base request() ----\n\ntype CredentialsMode = 'auto' | 'always' | 'never';\n\nexport type PublicOptions = { credentials?: CredentialsMode };\nexport type AuthOptions = {\n noRefresh?: boolean;\n credentials?: CredentialsMode;\n};\n\n// Core callable function (default = public)\nexport type RequestFn = <T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n) => Promise<T>;\n\nasync function baseRequest<T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n): Promise<T> {\n const res = await instance.request<T>({ url, ...config });\n return res.data;\n}\n\n/**\n * The exported request is both callable and has .public/.auth shortcuts.\n * Calling request(url, config) equals request.public(url, config).\n */\nconst request = ((url: string, config?: AxiosRequestConfig) => {\n return request.public(url, config);\n}) as RequestFn & {\n public: <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & PublicOptions,\n ) => Promise<T>;\n auth: <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & AuthOptions,\n ) => Promise<T>;\n init: <T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n ) => Promise<T | null>;\n reset: () => void;\n};\n\nrequest.public = async function <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & PublicOptions,\n): Promise<T> {\n const withCredentials = config?.credentials === 'always' ? true : undefined;\n return baseRequest<T>(url, {\n ...config,\n withCredentials,\n meta: {\n ...(config?.meta || {}),\n skipAuth: true,\n skipRefresh: true,\n skipUnauthorizedHandler: true,\n },\n });\n};\n\nrequest.auth = async function <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & AuthOptions,\n): Promise<T> {\n const withCredentials = config?.credentials === 'always' ? true : undefined;\n return baseRequest<T>(url, {\n ...config,\n withCredentials,\n meta: {\n ...(config?.meta || {}),\n ...(config?.noRefresh ? { skipRefresh: true } : {}),\n },\n });\n};\n\nlet didInitSoftRefresh = false;\n\nasync function probe<T>(\n url: string,\n config?: AxiosRequestConfig,\n): Promise<T | null> {\n return request.auth<T>(url, {\n ...config,\n noRefresh: true,\n meta: {\n ...(config?.meta || {}),\n skipUnauthorizedHandler: true,\n },\n });\n}\n\nrequest.init = async function <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & { soft?: boolean },\n): Promise<T | null> {\n try {\n return await probe<T>(url, config);\n } catch {\n const soft = config?.soft ?? true; // default: allow one soft refresh this load\n if (!soft || didInitSoftRefresh) return null;\n\n const auth = getGlobalRequestConfig().auth;\n if (!auth) return null;\n\n try {\n await auth.refresh(config?.signal as AbortSignal | undefined);\n didInitSoftRefresh = true;\n return await probe<T>(url, config);\n } catch {\n return null;\n }\n }\n};\n\nrequest.reset = function () {\n didInitSoftRefresh = false;\n};\n\nexport { request };\nexport type { AxiosRequestConfig } from 'axios';\n","import type { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';\n\nexport type ToastFn = (msg: string) => void;\n\nexport interface AuthStrategy {\n /** Attach credentials (e.g., Authorization header) */\n addAuthToRequest: <T extends AxiosRequestConfig | InternalAxiosRequestConfig>(\n config: T,\n ) => T;\n\n /** Refresh credentials; may rely on http-only cookie or token exchange */\n refresh: (signal?: AbortSignal) => Promise<void>;\n\n /** Optional: called when refresh ultimately fails */\n onRefreshFailed?: (reason: unknown) => void;\n\n /** Optional: persist tokens to local storage or app state */\n setToken?: (json: { accessToken?: string; refreshToken?: string }) => void;\n\n /** Optional: clear local credentials */\n clearToken?: () => void;\n}\n\nexport interface GlobalRequestConfig {\n toast?: {\n success?: ToastFn;\n error?: ToastFn;\n };\n baseURL?: string;\n defaultHeaders?: () => Record<string, string>;\n onUnauthorized?: () => void;\n\n /** Pluggable authentication behavior */\n auth?: AuthStrategy;\n\n /** Max retries after a successful refresh (default: 1) */\n retryAfterRefresh?: number;\n\n /** Status codes that should trigger a refresh attempt (default: 401) */\n shouldRefreshOnStatus?: (status: number) => boolean;\n}\n\nconst globalConfig: GlobalRequestConfig = {\n retryAfterRefresh: 1,\n shouldRefreshOnStatus: (s) => s === 401 || s === 419 || s === 440,\n};\n\nexport const setupRequest = (config: GlobalRequestConfig) => {\n Object.assign(globalConfig, config);\n};\n\nexport const updateRequestConfig = (patch: Partial<GlobalRequestConfig>) => {\n Object.assign(globalConfig, patch);\n};\n\nexport const getGlobalRequestConfig = () => globalConfig;\n","import axios, { type AxiosError } from 'axios';\n\nimport { getGlobalRequestConfig, type ToastFn } from './config';\n\nexport type OperateConfig<E> = {\n setOperating?: (loading: boolean) => void;\n formatMessage?: () => string;\n formatReason?: (err: E) => string;\n hideToast?: boolean;\n toast?: {\n success?: ToastFn;\n error?: ToastFn;\n };\n};\n\ntype ApiErrorBody = {\n message?: string;\n error?: string;\n detail?: string;\n errors?: string | string[];\n};\n\nfunction extractAxiosMessage(err: AxiosError<ApiErrorBody>): string {\n const data = err.response?.data;\n\n if (typeof data === 'string') return data;\n\n if (data && typeof data === 'object') {\n const { message, error, detail, errors } = data;\n const merged =\n message ??\n error ??\n detail ??\n (Array.isArray(errors) ? errors.join(', ') : errors);\n if (merged) return merged;\n }\n\n return err.message || 'Unknown error';\n}\n\nexport async function operator<T, E = unknown>(\n request: () => Promise<T>,\n config: OperateConfig<E> = {},\n): Promise<[true, T?] | [false, E?]> {\n const global = getGlobalRequestConfig();\n const {\n setOperating,\n formatMessage,\n formatReason = (err: E) => {\n if (axios.isAxiosError<ApiErrorBody>(err)) {\n return extractAxiosMessage(err);\n }\n if (err instanceof Error) return err.message;\n return String(err ?? 'Unknown error');\n },\n hideToast,\n toast: localToast,\n } = config;\n\n const toast = localToast ?? global.toast;\n\n try {\n setOperating?.(true);\n const res = await request();\n if (!hideToast) toast?.success?.(formatMessage?.() ?? 'Success');\n return [true, res];\n } catch (err) {\n const typed = err as E;\n if (!hideToast) toast?.error?.(formatReason(typed));\n return [false, typed];\n } finally {\n setOperating?.(false);\n }\n}\n","import { AxiosHeaders, type AxiosRequestConfig } from 'axios';\n\nimport type { AuthStrategy } from './config';\nimport {\n memoryStorage,\n localStorageStorage,\n type TokenStorage,\n} from './storage';\n\nfunction addAuthHeader<T extends AxiosRequestConfig>(\n config: T,\n token: string | null,\n): T {\n if (token) {\n config.headers = AxiosHeaders.from({\n ...(config.headers || {}),\n Authorization: `Bearer ${token}`,\n });\n }\n return config;\n}\n\n/**\n * Cookie-based strategy:\n * - Access token lives in app state (memory/storage).\n * - Refresh relies on http-only cookie via a server endpoint.\n */\nexport function createCookieStrategy(opts: {\n storage?: TokenStorage;\n tokenField?: string; // default: \"access_token\"\n refreshPath: string; // e.g., '/auth/refresh'\n}): AuthStrategy {\n const storage = opts.storage ?? memoryStorage;\n const accessKey = opts?.tokenField ?? 'access_token';\n\n return {\n addAuthToRequest<T extends AxiosRequestConfig>(config: T): T {\n return addAuthHeader(config, storage.getAccessToken());\n },\n async refresh(signal?: AbortSignal) {\n const res = await fetch(opts.refreshPath, {\n method: 'POST',\n credentials: 'include',\n headers: {\n Accept: 'application/json',\n },\n signal,\n });\n if (!res.ok) throw new Error(`Refresh failed: ${res.status}`);\n try {\n const json = await res.json();\n if (json?.[accessKey]) {\n storage.setAccessToken(json[accessKey]);\n }\n } catch {\n // Some backends may not return JSON; cookie-only session is fine.\n }\n },\n setToken({ accessToken, refreshToken }) {\n if (accessToken) storage.setAccessToken(accessToken);\n if (refreshToken) storage.setRefreshToken?.(refreshToken);\n },\n clearToken() {\n storage.setAccessToken(null);\n storage.setRefreshToken?.(null);\n },\n };\n}\n\n/**\n * Token-exchange strategy:\n * - Both access and refresh tokens are readable by JS (NOT http-only).\n */\nexport function createTokenStrategy(opts: {\n storage?: TokenStorage;\n tokenField?: string; // default: \"access_token\"\n refreshPath: string;\n}): AuthStrategy {\n const storage = opts.storage ?? localStorageStorage;\n const accessKey = opts?.tokenField ?? 'access_token';\n\n return {\n addAuthToRequest<T extends AxiosRequestConfig>(config: T): T {\n return addAuthHeader(config, storage.getAccessToken());\n },\n async refresh(signal?: AbortSignal) {\n const rt = storage.getRefreshToken?.();\n if (!rt) throw new Error('No refresh token');\n const res = await fetch(opts.refreshPath, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({ refresh_token: rt }),\n signal,\n });\n if (!res.ok) throw new Error(`Refresh failed: ${res.status}`);\n const json = await res.json();\n if (!json?.[accessKey]) throw new Error('Malformed refresh response');\n storage.setAccessToken(json[accessKey]);\n if (json.refresh_token) storage.setRefreshToken?.(json.refresh_token);\n },\n setToken({ accessToken, refreshToken }) {\n if (accessToken) storage.setAccessToken(accessToken);\n if (refreshToken) storage.setRefreshToken?.(refreshToken);\n },\n clearToken() {\n storage.setAccessToken(null);\n storage.setRefreshToken?.(null);\n },\n };\n}\n","export interface TokenStorage {\n getAccessToken(): string | null;\n setAccessToken(token: string | null): void;\n getRefreshToken?(): string | null;\n setRefreshToken?(token: string | null): void;\n}\n\nexport const memoryStorage: TokenStorage = (() => {\n let access: string | null = null;\n let refresh: string | null = null;\n return {\n getAccessToken: () => access,\n setAccessToken: (t) => (access = t),\n getRefreshToken: () => refresh,\n setRefreshToken: (t) => (refresh = t),\n };\n})();\n\nconst safeLocal = () => {\n try {\n return typeof window !== 'undefined' ? window.localStorage : null;\n } catch {\n return null;\n }\n};\n\nexport const localStorageStorage: TokenStorage = {\n getAccessToken: () => safeLocal()?.getItem('access_token') ?? null,\n setAccessToken: (t) => {\n const ls = safeLocal();\n if (!ls) return;\n if (t) {\n ls.setItem('access_token', t);\n } else {\n ls.removeItem('access_token');\n }\n },\n getRefreshToken: () => safeLocal()?.getItem('refresh_token') ?? null,\n setRefreshToken: (t) => {\n const ls = safeLocal();\n if (!ls) return;\n if (t) {\n ls.setItem('refresh_token', t);\n } else {\n ls.removeItem('refresh_token');\n }\n },\n};\n","import { getGlobalRequestConfig } from './config';\n\nexport async function login(\n perform: () => Promise<{ accessToken?: string; refreshToken?: string }>,\n) {\n const auth = getGlobalRequestConfig().auth;\n const json = await perform();\n auth?.setToken?.(json);\n return json;\n}\n\nexport async function logout<T>(perform?: () => Promise<T>) {\n const auth = getGlobalRequestConfig().auth;\n try {\n if (perform) await perform();\n } finally {\n auth?.clearToken?.();\n }\n}\n"],"mappings":"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","names":["axios","e","import_axios","request","axios","import_axios","_a","_b"]} |
+23
-5
@@ -35,2 +35,4 @@ import { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios'; | ||
| auth: <T = unknown>(url: string, config?: AxiosRequestConfig & AuthOptions) => Promise<T>; | ||
| init: <T = unknown>(url: string, config?: AxiosRequestConfig) => Promise<T | null>; | ||
| reset: () => void; | ||
| }; | ||
@@ -42,4 +44,2 @@ | ||
| addAuthToRequest: <T extends AxiosRequestConfig | InternalAxiosRequestConfig>(config: T) => T; | ||
| /** Optional: return true if access token is still valid */ | ||
| isAccessTokenValid?: () => boolean; | ||
| /** Refresh credentials; may rely on http-only cookie or token exchange */ | ||
@@ -49,2 +49,9 @@ refresh: (signal?: AbortSignal) => Promise<void>; | ||
| onRefreshFailed?: (reason: unknown) => void; | ||
| /** Optional: persist tokens to local storage or app state */ | ||
| setToken?: (json: { | ||
| accessToken?: string; | ||
| refreshToken?: string; | ||
| }) => void; | ||
| /** Optional: clear local credentials */ | ||
| clearToken?: () => void; | ||
| } | ||
@@ -88,2 +95,4 @@ interface GlobalRequestConfig { | ||
| } | ||
| declare const memoryStorage: TokenStorage; | ||
| declare const localStorageStorage: TokenStorage; | ||
@@ -97,4 +106,4 @@ /** | ||
| storage?: TokenStorage; | ||
| tokenField?: string; | ||
| refreshPath: string; | ||
| isTokenValid?: () => boolean; | ||
| }): AuthStrategy; | ||
@@ -107,6 +116,15 @@ /** | ||
| storage?: TokenStorage; | ||
| tokenField?: string; | ||
| refreshPath: string; | ||
| isTokenValid?: () => boolean; | ||
| }): AuthStrategy; | ||
| export { AuthStrategy, GlobalRequestConfig, OperateConfig, createCookieStrategy, createTokenStrategy, getGlobalRequestConfig, operator, request, setupRequest, updateRequestConfig }; | ||
| declare function login(perform: () => Promise<{ | ||
| accessToken?: string; | ||
| refreshToken?: string; | ||
| }>): Promise<{ | ||
| accessToken?: string; | ||
| refreshToken?: string; | ||
| }>; | ||
| declare function logout<T>(perform?: () => Promise<T>): Promise<void>; | ||
| export { AuthStrategy, GlobalRequestConfig, OperateConfig, createCookieStrategy, createTokenStrategy, getGlobalRequestConfig, localStorageStorage, login, logout, memoryStorage, operator, request, setupRequest, updateRequestConfig }; |
+23
-5
@@ -35,2 +35,4 @@ import { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios'; | ||
| auth: <T = unknown>(url: string, config?: AxiosRequestConfig & AuthOptions) => Promise<T>; | ||
| init: <T = unknown>(url: string, config?: AxiosRequestConfig) => Promise<T | null>; | ||
| reset: () => void; | ||
| }; | ||
@@ -42,4 +44,2 @@ | ||
| addAuthToRequest: <T extends AxiosRequestConfig | InternalAxiosRequestConfig>(config: T) => T; | ||
| /** Optional: return true if access token is still valid */ | ||
| isAccessTokenValid?: () => boolean; | ||
| /** Refresh credentials; may rely on http-only cookie or token exchange */ | ||
@@ -49,2 +49,9 @@ refresh: (signal?: AbortSignal) => Promise<void>; | ||
| onRefreshFailed?: (reason: unknown) => void; | ||
| /** Optional: persist tokens to local storage or app state */ | ||
| setToken?: (json: { | ||
| accessToken?: string; | ||
| refreshToken?: string; | ||
| }) => void; | ||
| /** Optional: clear local credentials */ | ||
| clearToken?: () => void; | ||
| } | ||
@@ -88,2 +95,4 @@ interface GlobalRequestConfig { | ||
| } | ||
| declare const memoryStorage: TokenStorage; | ||
| declare const localStorageStorage: TokenStorage; | ||
@@ -97,4 +106,4 @@ /** | ||
| storage?: TokenStorage; | ||
| tokenField?: string; | ||
| refreshPath: string; | ||
| isTokenValid?: () => boolean; | ||
| }): AuthStrategy; | ||
@@ -107,6 +116,15 @@ /** | ||
| storage?: TokenStorage; | ||
| tokenField?: string; | ||
| refreshPath: string; | ||
| isTokenValid?: () => boolean; | ||
| }): AuthStrategy; | ||
| export { AuthStrategy, GlobalRequestConfig, OperateConfig, createCookieStrategy, createTokenStrategy, getGlobalRequestConfig, operator, request, setupRequest, updateRequestConfig }; | ||
| declare function login(perform: () => Promise<{ | ||
| accessToken?: string; | ||
| refreshToken?: string; | ||
| }>): Promise<{ | ||
| accessToken?: string; | ||
| refreshToken?: string; | ||
| }>; | ||
| declare function logout<T>(perform?: () => Promise<T>): Promise<void>; | ||
| export { AuthStrategy, GlobalRequestConfig, OperateConfig, createCookieStrategy, createTokenStrategy, getGlobalRequestConfig, localStorageStorage, login, logout, memoryStorage, operator, request, setupRequest, updateRequestConfig }; |
+86
-31
@@ -9,3 +9,3 @@ // src/core/request.ts | ||
| retryAfterRefresh: 1, | ||
| shouldRefreshOnStatus: (s) => s === 401 | ||
| shouldRefreshOnStatus: (s) => s === 401 || s === 419 || s === 440 | ||
| }; | ||
@@ -46,5 +46,3 @@ var setupRequest = (config) => { | ||
| } finally { | ||
| const p = refreshPromise; | ||
| refreshPromise = null; | ||
| await (p == null ? void 0 : p.catch(() => void 0)); | ||
| } | ||
@@ -76,3 +74,3 @@ })(); | ||
| async (error) => { | ||
| var _a, _b, _c, _d, _e, _f, _g, _h, _i; | ||
| var _a, _b, _c, _d, _e, _f, _g; | ||
| const global = getGlobalRequestConfig(); | ||
@@ -86,5 +84,2 @@ const cfg = error.config || {}; | ||
| } | ||
| if ((_e = (_d = global.auth).isAccessTokenValid) == null ? void 0 : _e.call(_d)) { | ||
| return Promise.reject(error); | ||
| } | ||
| try { | ||
@@ -95,5 +90,5 @@ await ensureRefreshed(cfg.signal); | ||
| } catch (refreshErr) { | ||
| (_g = (_f = global.auth).onRefreshFailed) == null ? void 0 : _g.call(_f, refreshErr); | ||
| if (!((_h = cfg.meta) == null ? void 0 : _h.skipUnauthorizedHandler)) { | ||
| (_i = global.onUnauthorized) == null ? void 0 : _i.call(global); | ||
| (_e = (_d = global.auth).onRefreshFailed) == null ? void 0 : _e.call(_d, refreshErr); | ||
| if (!((_f = cfg.meta) == null ? void 0 : _f.skipUnauthorizedHandler)) { | ||
| (_g = global.onUnauthorized) == null ? void 0 : _g.call(global); | ||
| } | ||
@@ -135,2 +130,36 @@ return Promise.reject(error); | ||
| }; | ||
| var didInitSoftRefresh = false; | ||
| async function probe(url, config) { | ||
| return request.auth(url, { | ||
| ...config, | ||
| noRefresh: true, | ||
| meta: { | ||
| ...(config == null ? void 0 : config.meta) || {}, | ||
| skipUnauthorizedHandler: true | ||
| } | ||
| }); | ||
| } | ||
| request.init = async function(url, config) { | ||
| var _a; | ||
| try { | ||
| return await probe(url, config); | ||
| } catch (e) { | ||
| const soft = (_a = config == null ? void 0 : config.soft) != null ? _a : true; | ||
| if (!soft || didInitSoftRefresh) | ||
| return null; | ||
| const auth = getGlobalRequestConfig().auth; | ||
| if (!auth) | ||
| return null; | ||
| try { | ||
| await auth.refresh(config == null ? void 0 : config.signal); | ||
| didInitSoftRefresh = true; | ||
| return await probe(url, config); | ||
| } catch (e2) { | ||
| return null; | ||
| } | ||
| } | ||
| }; | ||
| request.reset = function() { | ||
| didInitSoftRefresh = false; | ||
| }; | ||
@@ -248,16 +277,6 @@ // src/core/operator.ts | ||
| } | ||
| function isJwtValid(t, leewaySec = 30) { | ||
| if (!t) | ||
| return false; | ||
| const [, payload] = t.split("."); | ||
| try { | ||
| const { exp } = JSON.parse(atob(payload)); | ||
| return typeof exp === "number" && Date.now() / 1e3 < exp - leewaySec; | ||
| } catch (e) { | ||
| return false; | ||
| } | ||
| } | ||
| function createCookieStrategy(opts) { | ||
| var _a, _b; | ||
| const storage = (_a = opts.storage) != null ? _a : memoryStorage; | ||
| const accessKey = (_b = opts == null ? void 0 : opts.tokenField) != null ? _b : "access_token"; | ||
| return { | ||
@@ -267,3 +286,2 @@ addAuthToRequest(config) { | ||
| }, | ||
| isAccessTokenValid: (_b = opts.isTokenValid) != null ? _b : () => isJwtValid(storage.getAccessToken()), | ||
| async refresh(signal) { | ||
@@ -282,4 +300,4 @@ const res = await fetch(opts.refreshPath, { | ||
| const json = await res.json(); | ||
| if (json == null ? void 0 : json.access_token) { | ||
| storage.setAccessToken(json.access_token); | ||
| if (json == null ? void 0 : json[accessKey]) { | ||
| storage.setAccessToken(json[accessKey]); | ||
| } | ||
@@ -289,5 +307,13 @@ } catch (e) { | ||
| }, | ||
| onRefreshFailed(reason) { | ||
| setToken({ accessToken, refreshToken }) { | ||
| var _a2; | ||
| if (accessToken) | ||
| storage.setAccessToken(accessToken); | ||
| if (refreshToken) | ||
| (_a2 = storage.setRefreshToken) == null ? void 0 : _a2.call(storage, refreshToken); | ||
| }, | ||
| clearToken() { | ||
| var _a2; | ||
| storage.setAccessToken(null); | ||
| console.warn("Refresh failed", reason); | ||
| (_a2 = storage.setRefreshToken) == null ? void 0 : _a2.call(storage, null); | ||
| } | ||
@@ -299,2 +325,3 @@ }; | ||
| const storage = (_a = opts.storage) != null ? _a : localStorageStorage; | ||
| const accessKey = (_b = opts == null ? void 0 : opts.tokenField) != null ? _b : "access_token"; | ||
| return { | ||
@@ -304,3 +331,2 @@ addAuthToRequest(config) { | ||
| }, | ||
| isAccessTokenValid: (_b = opts.isTokenValid) != null ? _b : () => isJwtValid(storage.getAccessToken()), | ||
| async refresh(signal) { | ||
@@ -323,16 +349,41 @@ var _a2, _b2; | ||
| const json = await res.json(); | ||
| if (!(json == null ? void 0 : json.access_token)) | ||
| if (!(json == null ? void 0 : json[accessKey])) | ||
| throw new Error("Malformed refresh response"); | ||
| storage.setAccessToken(json.access_token); | ||
| storage.setAccessToken(json[accessKey]); | ||
| if (json.refresh_token) | ||
| (_b2 = storage.setRefreshToken) == null ? void 0 : _b2.call(storage, json.refresh_token); | ||
| }, | ||
| onRefreshFailed(reason) { | ||
| setToken({ accessToken, refreshToken }) { | ||
| var _a2; | ||
| if (accessToken) | ||
| storage.setAccessToken(accessToken); | ||
| if (refreshToken) | ||
| (_a2 = storage.setRefreshToken) == null ? void 0 : _a2.call(storage, refreshToken); | ||
| }, | ||
| clearToken() { | ||
| var _a2; | ||
| storage.setAccessToken(null); | ||
| (_a2 = storage.setRefreshToken) == null ? void 0 : _a2.call(storage, null); | ||
| console.warn("Refresh failed", reason); | ||
| } | ||
| }; | ||
| } | ||
| // src/core/auth-helper.ts | ||
| async function login(perform) { | ||
| var _a; | ||
| const auth = getGlobalRequestConfig().auth; | ||
| const json = await perform(); | ||
| (_a = auth == null ? void 0 : auth.setToken) == null ? void 0 : _a.call(auth, json); | ||
| return json; | ||
| } | ||
| async function logout(perform) { | ||
| var _a; | ||
| const auth = getGlobalRequestConfig().auth; | ||
| try { | ||
| if (perform) | ||
| await perform(); | ||
| } finally { | ||
| (_a = auth == null ? void 0 : auth.clearToken) == null ? void 0 : _a.call(auth); | ||
| } | ||
| } | ||
| export { | ||
@@ -342,2 +393,6 @@ createCookieStrategy, | ||
| getGlobalRequestConfig, | ||
| localStorageStorage, | ||
| login, | ||
| logout, | ||
| memoryStorage, | ||
| operator, | ||
@@ -344,0 +399,0 @@ request, |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/core/request.ts","../src/core/config.ts","../src/core/operator.ts","../src/core/auth.ts","../src/core/storage.ts"],"sourcesContent":["import axios, {\n AxiosHeaders,\n type InternalAxiosRequestConfig,\n type AxiosError,\n type AxiosRequestConfig,\n type AxiosResponse,\n} from 'axios';\n\nimport { getGlobalRequestConfig } from './config';\n\n/** Per-request behavior toggles */\nexport type RequestMeta = {\n /** Do not attach credentials (skip strategy.addAuthToRequest) */\n skipAuth?: boolean;\n /** Do not attempt refresh on 401/419 */\n skipRefresh?: boolean;\n /** Do not call global onUnauthorized when unauthorized */\n skipUnauthorizedHandler?: boolean;\n /** Internal guard to avoid infinite retry loops */\n _retried?: number;\n};\n\n// Axios module augmentation to allow config.meta\ndeclare module 'axios' {\n export interface AxiosRequestConfig {\n meta?: RequestMeta;\n }\n}\n\nconst instance = axios.create();\n\n// ---- request interceptor: baseURL, headers, auth attachment ----\ninstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {\n const global = getGlobalRequestConfig();\n\n // Merge headers (defaultHeaders first so explicit config wins)\n const mergedHeaders = {\n ...(global.defaultHeaders?.() || {}),\n ...(config.headers || {}),\n };\n\n config.headers = AxiosHeaders.from(mergedHeaders);\n config.baseURL = config.baseURL || global.baseURL || '/api';\n\n // Attach Authorization (or other credentials) if strategy is present and not skipped\n if (!config.meta?.skipAuth && global.auth) {\n config = global.auth.addAuthToRequest(config);\n }\n\n return config;\n});\n\n// ---- refresh de-duplication ----\nlet refreshPromise: Promise<void> | null = null;\n\nasync function ensureRefreshed(signal?: AbortSignal) {\n const { auth } = getGlobalRequestConfig();\n if (!auth) return;\n\n if (!refreshPromise) {\n refreshPromise = (async () => {\n try {\n await auth.refresh(signal);\n } finally {\n // Reset promise regardless of success/failure\n const p = refreshPromise;\n refreshPromise = null;\n await (p as Promise<void> | null)?.catch(() => undefined);\n }\n })();\n }\n return refreshPromise;\n}\n\nfunction shouldAttemptRefresh(error: AxiosError): boolean {\n const { shouldRefreshOnStatus } = getGlobalRequestConfig();\n const status = error.response?.status;\n return typeof status === 'number' && !!shouldRefreshOnStatus?.(status);\n}\n\nasync function retryOnce<T>(\n original: InternalAxiosRequestConfig,\n): Promise<AxiosResponse<T>> {\n const global = getGlobalRequestConfig();\n const retryMax = Math.max(1, global.retryAfterRefresh ?? 1);\n\n const meta = original.meta || (original.meta = {});\n const count = meta._retried ?? 0;\n\n if (count >= retryMax) {\n throw new Error('Max retries after refresh exceeded');\n }\n\n meta._retried = count + 1;\n return instance.request<T>(original);\n}\n\n// ---- response interceptor: 401 -> refresh -> retry ----\ninstance.interceptors.response.use(\n (res) => res,\n async (error: AxiosError) => {\n const global = getGlobalRequestConfig();\n const cfg = (error.config || {}) as InternalAxiosRequestConfig;\n\n // If no strategy or refresh is skipped, do not handle here\n if (cfg.meta?.skipRefresh || !global.auth || !shouldAttemptRefresh(error)) {\n if (shouldAttemptRefresh(error) && !cfg.meta?.skipUnauthorizedHandler) {\n global.onUnauthorized?.();\n }\n return Promise.reject(error);\n }\n\n // Optional proactive validity check (if provided)\n if (global.auth.isAccessTokenValid?.()) {\n // Token claims still \"valid\" but server says unauthorized -> bubble up\n return Promise.reject(error);\n }\n\n try {\n await ensureRefreshed(cfg.signal as AbortSignal | undefined);\n const retried = await retryOnce(cfg);\n return retried;\n } catch (refreshErr) {\n global.auth.onRefreshFailed?.(refreshErr);\n if (!cfg.meta?.skipUnauthorizedHandler) {\n global.onUnauthorized?.();\n }\n return Promise.reject(error);\n }\n },\n);\n\n// ---- public/auth helpers on top of base request() ----\n\ntype CredentialsMode = 'auto' | 'always' | 'never';\n\nexport type PublicOptions = { credentials?: CredentialsMode };\nexport type AuthOptions = {\n noRefresh?: boolean;\n credentials?: CredentialsMode;\n};\n\n// Core callable function (default = public)\nexport type RequestFn = <T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n) => Promise<T>;\n\nasync function baseRequest<T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n): Promise<T> {\n const res = await instance.request<T>({ url, ...config });\n return res.data;\n}\n\n/**\n * The exported request is both callable and has .public/.auth shortcuts.\n * Calling request(url, config) equals request.public(url, config).\n */\nconst request = ((url: string, config?: AxiosRequestConfig) => {\n return request.public(url, config);\n}) as RequestFn & {\n public: <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & PublicOptions,\n ) => Promise<T>;\n auth: <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & AuthOptions,\n ) => Promise<T>;\n};\n\nrequest.public = async function <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & PublicOptions,\n): Promise<T> {\n const withCredentials = config?.credentials === 'always' ? true : undefined;\n return baseRequest<T>(url, {\n ...config,\n withCredentials,\n meta: {\n ...(config?.meta || {}),\n skipAuth: true,\n skipRefresh: true,\n skipUnauthorizedHandler: true,\n },\n });\n};\n\nrequest.auth = async function <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & AuthOptions,\n): Promise<T> {\n const withCredentials = config?.credentials === 'always' ? true : undefined;\n return baseRequest<T>(url, {\n ...config,\n withCredentials,\n meta: {\n ...(config?.meta || {}),\n ...(config?.noRefresh ? { skipRefresh: true } : {}),\n },\n });\n};\n\nexport { request };\nexport type { AxiosRequestConfig } from 'axios';\n","import type { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';\n\nexport type ToastFn = (msg: string) => void;\n\nexport interface AuthStrategy {\n /** Attach credentials (e.g., Authorization header) */\n addAuthToRequest: <T extends AxiosRequestConfig | InternalAxiosRequestConfig>(\n config: T,\n ) => T;\n\n /** Optional: return true if access token is still valid */\n isAccessTokenValid?: () => boolean;\n\n /** Refresh credentials; may rely on http-only cookie or token exchange */\n refresh: (signal?: AbortSignal) => Promise<void>;\n\n /** Optional: called when refresh ultimately fails */\n onRefreshFailed?: (reason: unknown) => void;\n}\n\nexport interface GlobalRequestConfig {\n toast?: {\n success?: ToastFn;\n error?: ToastFn;\n };\n baseURL?: string;\n defaultHeaders?: () => Record<string, string>;\n onUnauthorized?: () => void;\n\n /** Pluggable authentication behavior */\n auth?: AuthStrategy;\n\n /** Max retries after a successful refresh (default: 1) */\n retryAfterRefresh?: number;\n\n /** Status codes that should trigger a refresh attempt (default: 401) */\n shouldRefreshOnStatus?: (status: number) => boolean;\n}\n\nconst globalConfig: GlobalRequestConfig = {\n retryAfterRefresh: 1,\n shouldRefreshOnStatus: (s) => s === 401,\n};\n\nexport const setupRequest = (config: GlobalRequestConfig) => {\n Object.assign(globalConfig, config);\n};\n\nexport const updateRequestConfig = (patch: Partial<GlobalRequestConfig>) => {\n Object.assign(globalConfig, patch);\n};\n\nexport const getGlobalRequestConfig = () => globalConfig;\n","import axios, { type AxiosError } from 'axios';\n\nimport { getGlobalRequestConfig, type ToastFn } from './config';\n\nexport type OperateConfig<E> = {\n setOperating?: (loading: boolean) => void;\n formatMessage?: () => string;\n formatReason?: (err: E) => string;\n hideToast?: boolean;\n toast?: {\n success?: ToastFn;\n error?: ToastFn;\n };\n};\n\ntype ApiErrorBody = {\n message?: string;\n error?: string;\n detail?: string;\n errors?: string | string[];\n};\n\nfunction extractAxiosMessage(err: AxiosError<ApiErrorBody>): string {\n const data = err.response?.data;\n\n if (typeof data === 'string') return data;\n\n if (data && typeof data === 'object') {\n const { message, error, detail, errors } = data;\n const merged =\n message ??\n error ??\n detail ??\n (Array.isArray(errors) ? errors.join(', ') : errors);\n if (merged) return merged;\n }\n\n return err.message || 'Unknown error';\n}\n\nexport async function operator<T, E = unknown>(\n request: () => Promise<T>,\n config: OperateConfig<E> = {},\n): Promise<[true, T?] | [false, E?]> {\n const global = getGlobalRequestConfig();\n const {\n setOperating,\n formatMessage,\n formatReason = (err: E) => {\n if (axios.isAxiosError<ApiErrorBody>(err)) {\n return extractAxiosMessage(err);\n }\n if (err instanceof Error) return err.message;\n return String(err ?? 'Unknown error');\n },\n hideToast,\n toast: localToast,\n } = config;\n\n const toast = localToast ?? global.toast;\n\n try {\n setOperating?.(true);\n const res = await request();\n if (!hideToast) toast?.success?.(formatMessage?.() ?? 'Success');\n return [true, res];\n } catch (err) {\n const typed = err as E;\n if (!hideToast) toast?.error?.(formatReason(typed));\n return [false, typed];\n } finally {\n setOperating?.(false);\n }\n}\n","import { AxiosHeaders, type AxiosRequestConfig } from 'axios';\n\nimport type { AuthStrategy } from './config';\nimport {\n memoryStorage,\n localStorageStorage,\n type TokenStorage,\n} from './storage';\n\nfunction addAuthHeader<T extends AxiosRequestConfig>(\n config: T,\n token: string | null,\n): T {\n if (token) {\n config.headers = AxiosHeaders.from({\n ...(config.headers || {}),\n Authorization: `Bearer ${token}`,\n });\n }\n return config;\n}\n\nfunction isJwtValid(t: string | null, leewaySec = 30) {\n if (!t) return false;\n const [, payload] = t.split('.');\n try {\n const { exp } = JSON.parse(atob(payload));\n return typeof exp === 'number' && Date.now() / 1000 < exp - leewaySec;\n } catch {\n return false;\n }\n}\n\n/**\n * Cookie-based strategy:\n * - Access token lives in app state (memory/storage).\n * - Refresh relies on http-only cookie via a server endpoint.\n */\nexport function createCookieStrategy(opts: {\n storage?: TokenStorage;\n refreshPath: string; // e.g., '/auth/refresh'\n isTokenValid?: () => boolean;\n}): AuthStrategy {\n const storage = opts.storage ?? memoryStorage;\n\n return {\n addAuthToRequest<T extends AxiosRequestConfig>(config: T): T {\n return addAuthHeader(config, storage.getAccessToken());\n },\n isAccessTokenValid:\n opts.isTokenValid ?? (() => isJwtValid(storage.getAccessToken())),\n async refresh(signal?: AbortSignal) {\n const res = await fetch(opts.refreshPath, {\n method: 'POST',\n credentials: 'include',\n headers: {\n Accept: 'application/json',\n },\n signal,\n });\n if (!res.ok) throw new Error(`Refresh failed: ${res.status}`);\n try {\n const json = await res.json();\n if (json?.access_token) {\n storage.setAccessToken(json.access_token);\n }\n } catch {\n // Some backends may not return JSON; cookie-only session is fine.\n }\n },\n onRefreshFailed(reason) {\n storage.setAccessToken(null);\n console.warn('Refresh failed', reason);\n },\n };\n}\n\n/**\n * Token-exchange strategy:\n * - Both access and refresh tokens are readable by JS (NOT http-only).\n */\nexport function createTokenStrategy(opts: {\n storage?: TokenStorage;\n refreshPath: string;\n isTokenValid?: () => boolean;\n}): AuthStrategy {\n const storage = opts.storage ?? localStorageStorage;\n\n return {\n addAuthToRequest<T extends AxiosRequestConfig>(config: T): T {\n return addAuthHeader(config, storage.getAccessToken());\n },\n isAccessTokenValid:\n opts.isTokenValid ?? (() => isJwtValid(storage.getAccessToken())),\n async refresh(signal?: AbortSignal) {\n const rt = storage.getRefreshToken?.();\n if (!rt) throw new Error('No refresh token');\n const res = await fetch(opts.refreshPath, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({ refresh_token: rt }),\n signal,\n });\n if (!res.ok) throw new Error(`Refresh failed: ${res.status}`);\n const json = await res.json();\n if (!json?.access_token) throw new Error('Malformed refresh response');\n storage.setAccessToken(json.access_token);\n if (json.refresh_token) storage.setRefreshToken?.(json.refresh_token);\n },\n onRefreshFailed(reason) {\n storage.setAccessToken(null);\n storage.setRefreshToken?.(null);\n console.warn('Refresh failed', reason);\n },\n };\n}\n","export interface TokenStorage {\n getAccessToken(): string | null;\n setAccessToken(token: string | null): void;\n getRefreshToken?(): string | null;\n setRefreshToken?(token: string | null): void;\n}\n\nexport const memoryStorage: TokenStorage = (() => {\n let access: string | null = null;\n let refresh: string | null = null;\n return {\n getAccessToken: () => access,\n setAccessToken: (t) => (access = t),\n getRefreshToken: () => refresh,\n setRefreshToken: (t) => (refresh = t),\n };\n})();\n\nconst safeLocal = () => {\n try {\n return typeof window !== 'undefined' ? window.localStorage : null;\n } catch {\n return null;\n }\n};\n\nexport const localStorageStorage: TokenStorage = {\n getAccessToken: () => safeLocal()?.getItem('access_token') ?? null,\n setAccessToken: (t) => {\n const ls = safeLocal();\n if (!ls) return;\n if (t) {\n ls.setItem('access_token', t);\n } else {\n ls.removeItem('access_token');\n }\n },\n getRefreshToken: () => safeLocal()?.getItem('refresh_token') ?? null,\n setRefreshToken: (t) => {\n const ls = safeLocal();\n if (!ls) return;\n if (t) {\n ls.setItem('refresh_token', t);\n } else {\n ls.removeItem('refresh_token');\n }\n },\n};\n"],"mappings":"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","names":["axios","request","axios","AxiosHeaders","AxiosHeaders","_a","_b"]} | ||
| {"version":3,"sources":["../src/core/request.ts","../src/core/config.ts","../src/core/operator.ts","../src/core/auth.ts","../src/core/storage.ts","../src/core/auth-helper.ts"],"sourcesContent":["import axios, {\n AxiosHeaders,\n type InternalAxiosRequestConfig,\n type AxiosError,\n type AxiosRequestConfig,\n type AxiosResponse,\n} from 'axios';\n\nimport { getGlobalRequestConfig } from './config';\n\n/** Per-request behavior toggles */\nexport type RequestMeta = {\n /** Do not attach credentials (skip strategy.addAuthToRequest) */\n skipAuth?: boolean;\n /** Do not attempt refresh on 401/419 */\n skipRefresh?: boolean;\n /** Do not call global onUnauthorized when unauthorized */\n skipUnauthorizedHandler?: boolean;\n /** Internal guard to avoid infinite retry loops */\n _retried?: number;\n};\n\n// Axios module augmentation to allow config.meta\ndeclare module 'axios' {\n export interface AxiosRequestConfig {\n meta?: RequestMeta;\n }\n}\n\nconst instance = axios.create();\n\n// ---- request interceptor: baseURL, headers, auth attachment ----\ninstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {\n const global = getGlobalRequestConfig();\n\n // Merge headers (defaultHeaders first so explicit config wins)\n const mergedHeaders = {\n ...(global.defaultHeaders?.() || {}),\n ...(config.headers || {}),\n };\n\n config.headers = AxiosHeaders.from(mergedHeaders);\n config.baseURL = config.baseURL || global.baseURL || '/api';\n\n // Attach Authorization (or other credentials) if strategy is present and not skipped\n if (!config.meta?.skipAuth && global.auth) {\n config = global.auth.addAuthToRequest(config);\n }\n\n return config;\n});\n\n// ---- refresh de-duplication ----\nlet refreshPromise: Promise<void> | null = null;\n\nasync function ensureRefreshed(signal?: AbortSignal) {\n const { auth } = getGlobalRequestConfig();\n if (!auth) return;\n\n if (!refreshPromise) {\n refreshPromise = (async () => {\n try {\n await auth.refresh(signal);\n } finally {\n refreshPromise = null;\n }\n })();\n }\n return refreshPromise;\n}\n\nfunction shouldAttemptRefresh(error: AxiosError): boolean {\n const { shouldRefreshOnStatus } = getGlobalRequestConfig();\n const status = error.response?.status;\n return typeof status === 'number' && !!shouldRefreshOnStatus?.(status);\n}\n\nasync function retryOnce<T>(\n original: InternalAxiosRequestConfig,\n): Promise<AxiosResponse<T>> {\n const global = getGlobalRequestConfig();\n const retryMax = Math.max(1, global.retryAfterRefresh ?? 1);\n\n const meta = original.meta || (original.meta = {});\n const count = meta._retried ?? 0;\n\n if (count >= retryMax) {\n throw new Error('Max retries after refresh exceeded');\n }\n\n meta._retried = count + 1;\n return instance.request<T>(original);\n}\n\n// ---- response interceptor: 401 -> refresh -> retry ----\ninstance.interceptors.response.use(\n (res) => res,\n async (error: AxiosError) => {\n const global = getGlobalRequestConfig();\n const cfg = (error.config || {}) as InternalAxiosRequestConfig;\n\n // If no strategy or refresh is skipped, do not handle here\n if (cfg.meta?.skipRefresh || !global.auth || !shouldAttemptRefresh(error)) {\n if (shouldAttemptRefresh(error) && !cfg.meta?.skipUnauthorizedHandler) {\n global.onUnauthorized?.();\n }\n return Promise.reject(error);\n }\n\n try {\n await ensureRefreshed(cfg.signal as AbortSignal | undefined);\n const retried = await retryOnce(cfg);\n return retried;\n } catch (refreshErr) {\n global.auth.onRefreshFailed?.(refreshErr);\n if (!cfg.meta?.skipUnauthorizedHandler) {\n global.onUnauthorized?.();\n }\n return Promise.reject(error);\n }\n },\n);\n\n// ---- public/auth helpers on top of base request() ----\n\ntype CredentialsMode = 'auto' | 'always' | 'never';\n\nexport type PublicOptions = { credentials?: CredentialsMode };\nexport type AuthOptions = {\n noRefresh?: boolean;\n credentials?: CredentialsMode;\n};\n\n// Core callable function (default = public)\nexport type RequestFn = <T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n) => Promise<T>;\n\nasync function baseRequest<T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n): Promise<T> {\n const res = await instance.request<T>({ url, ...config });\n return res.data;\n}\n\n/**\n * The exported request is both callable and has .public/.auth shortcuts.\n * Calling request(url, config) equals request.public(url, config).\n */\nconst request = ((url: string, config?: AxiosRequestConfig) => {\n return request.public(url, config);\n}) as RequestFn & {\n public: <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & PublicOptions,\n ) => Promise<T>;\n auth: <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & AuthOptions,\n ) => Promise<T>;\n init: <T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n ) => Promise<T | null>;\n reset: () => void;\n};\n\nrequest.public = async function <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & PublicOptions,\n): Promise<T> {\n const withCredentials = config?.credentials === 'always' ? true : undefined;\n return baseRequest<T>(url, {\n ...config,\n withCredentials,\n meta: {\n ...(config?.meta || {}),\n skipAuth: true,\n skipRefresh: true,\n skipUnauthorizedHandler: true,\n },\n });\n};\n\nrequest.auth = async function <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & AuthOptions,\n): Promise<T> {\n const withCredentials = config?.credentials === 'always' ? true : undefined;\n return baseRequest<T>(url, {\n ...config,\n withCredentials,\n meta: {\n ...(config?.meta || {}),\n ...(config?.noRefresh ? { skipRefresh: true } : {}),\n },\n });\n};\n\nlet didInitSoftRefresh = false;\n\nasync function probe<T>(\n url: string,\n config?: AxiosRequestConfig,\n): Promise<T | null> {\n return request.auth<T>(url, {\n ...config,\n noRefresh: true,\n meta: {\n ...(config?.meta || {}),\n skipUnauthorizedHandler: true,\n },\n });\n}\n\nrequest.init = async function <T = unknown>(\n url: string,\n config?: AxiosRequestConfig & { soft?: boolean },\n): Promise<T | null> {\n try {\n return await probe<T>(url, config);\n } catch {\n const soft = config?.soft ?? true; // default: allow one soft refresh this load\n if (!soft || didInitSoftRefresh) return null;\n\n const auth = getGlobalRequestConfig().auth;\n if (!auth) return null;\n\n try {\n await auth.refresh(config?.signal as AbortSignal | undefined);\n didInitSoftRefresh = true;\n return await probe<T>(url, config);\n } catch {\n return null;\n }\n }\n};\n\nrequest.reset = function () {\n didInitSoftRefresh = false;\n};\n\nexport { request };\nexport type { AxiosRequestConfig } from 'axios';\n","import type { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';\n\nexport type ToastFn = (msg: string) => void;\n\nexport interface AuthStrategy {\n /** Attach credentials (e.g., Authorization header) */\n addAuthToRequest: <T extends AxiosRequestConfig | InternalAxiosRequestConfig>(\n config: T,\n ) => T;\n\n /** Refresh credentials; may rely on http-only cookie or token exchange */\n refresh: (signal?: AbortSignal) => Promise<void>;\n\n /** Optional: called when refresh ultimately fails */\n onRefreshFailed?: (reason: unknown) => void;\n\n /** Optional: persist tokens to local storage or app state */\n setToken?: (json: { accessToken?: string; refreshToken?: string }) => void;\n\n /** Optional: clear local credentials */\n clearToken?: () => void;\n}\n\nexport interface GlobalRequestConfig {\n toast?: {\n success?: ToastFn;\n error?: ToastFn;\n };\n baseURL?: string;\n defaultHeaders?: () => Record<string, string>;\n onUnauthorized?: () => void;\n\n /** Pluggable authentication behavior */\n auth?: AuthStrategy;\n\n /** Max retries after a successful refresh (default: 1) */\n retryAfterRefresh?: number;\n\n /** Status codes that should trigger a refresh attempt (default: 401) */\n shouldRefreshOnStatus?: (status: number) => boolean;\n}\n\nconst globalConfig: GlobalRequestConfig = {\n retryAfterRefresh: 1,\n shouldRefreshOnStatus: (s) => s === 401 || s === 419 || s === 440,\n};\n\nexport const setupRequest = (config: GlobalRequestConfig) => {\n Object.assign(globalConfig, config);\n};\n\nexport const updateRequestConfig = (patch: Partial<GlobalRequestConfig>) => {\n Object.assign(globalConfig, patch);\n};\n\nexport const getGlobalRequestConfig = () => globalConfig;\n","import axios, { type AxiosError } from 'axios';\n\nimport { getGlobalRequestConfig, type ToastFn } from './config';\n\nexport type OperateConfig<E> = {\n setOperating?: (loading: boolean) => void;\n formatMessage?: () => string;\n formatReason?: (err: E) => string;\n hideToast?: boolean;\n toast?: {\n success?: ToastFn;\n error?: ToastFn;\n };\n};\n\ntype ApiErrorBody = {\n message?: string;\n error?: string;\n detail?: string;\n errors?: string | string[];\n};\n\nfunction extractAxiosMessage(err: AxiosError<ApiErrorBody>): string {\n const data = err.response?.data;\n\n if (typeof data === 'string') return data;\n\n if (data && typeof data === 'object') {\n const { message, error, detail, errors } = data;\n const merged =\n message ??\n error ??\n detail ??\n (Array.isArray(errors) ? errors.join(', ') : errors);\n if (merged) return merged;\n }\n\n return err.message || 'Unknown error';\n}\n\nexport async function operator<T, E = unknown>(\n request: () => Promise<T>,\n config: OperateConfig<E> = {},\n): Promise<[true, T?] | [false, E?]> {\n const global = getGlobalRequestConfig();\n const {\n setOperating,\n formatMessage,\n formatReason = (err: E) => {\n if (axios.isAxiosError<ApiErrorBody>(err)) {\n return extractAxiosMessage(err);\n }\n if (err instanceof Error) return err.message;\n return String(err ?? 'Unknown error');\n },\n hideToast,\n toast: localToast,\n } = config;\n\n const toast = localToast ?? global.toast;\n\n try {\n setOperating?.(true);\n const res = await request();\n if (!hideToast) toast?.success?.(formatMessage?.() ?? 'Success');\n return [true, res];\n } catch (err) {\n const typed = err as E;\n if (!hideToast) toast?.error?.(formatReason(typed));\n return [false, typed];\n } finally {\n setOperating?.(false);\n }\n}\n","import { AxiosHeaders, type AxiosRequestConfig } from 'axios';\n\nimport type { AuthStrategy } from './config';\nimport {\n memoryStorage,\n localStorageStorage,\n type TokenStorage,\n} from './storage';\n\nfunction addAuthHeader<T extends AxiosRequestConfig>(\n config: T,\n token: string | null,\n): T {\n if (token) {\n config.headers = AxiosHeaders.from({\n ...(config.headers || {}),\n Authorization: `Bearer ${token}`,\n });\n }\n return config;\n}\n\n/**\n * Cookie-based strategy:\n * - Access token lives in app state (memory/storage).\n * - Refresh relies on http-only cookie via a server endpoint.\n */\nexport function createCookieStrategy(opts: {\n storage?: TokenStorage;\n tokenField?: string; // default: \"access_token\"\n refreshPath: string; // e.g., '/auth/refresh'\n}): AuthStrategy {\n const storage = opts.storage ?? memoryStorage;\n const accessKey = opts?.tokenField ?? 'access_token';\n\n return {\n addAuthToRequest<T extends AxiosRequestConfig>(config: T): T {\n return addAuthHeader(config, storage.getAccessToken());\n },\n async refresh(signal?: AbortSignal) {\n const res = await fetch(opts.refreshPath, {\n method: 'POST',\n credentials: 'include',\n headers: {\n Accept: 'application/json',\n },\n signal,\n });\n if (!res.ok) throw new Error(`Refresh failed: ${res.status}`);\n try {\n const json = await res.json();\n if (json?.[accessKey]) {\n storage.setAccessToken(json[accessKey]);\n }\n } catch {\n // Some backends may not return JSON; cookie-only session is fine.\n }\n },\n setToken({ accessToken, refreshToken }) {\n if (accessToken) storage.setAccessToken(accessToken);\n if (refreshToken) storage.setRefreshToken?.(refreshToken);\n },\n clearToken() {\n storage.setAccessToken(null);\n storage.setRefreshToken?.(null);\n },\n };\n}\n\n/**\n * Token-exchange strategy:\n * - Both access and refresh tokens are readable by JS (NOT http-only).\n */\nexport function createTokenStrategy(opts: {\n storage?: TokenStorage;\n tokenField?: string; // default: \"access_token\"\n refreshPath: string;\n}): AuthStrategy {\n const storage = opts.storage ?? localStorageStorage;\n const accessKey = opts?.tokenField ?? 'access_token';\n\n return {\n addAuthToRequest<T extends AxiosRequestConfig>(config: T): T {\n return addAuthHeader(config, storage.getAccessToken());\n },\n async refresh(signal?: AbortSignal) {\n const rt = storage.getRefreshToken?.();\n if (!rt) throw new Error('No refresh token');\n const res = await fetch(opts.refreshPath, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({ refresh_token: rt }),\n signal,\n });\n if (!res.ok) throw new Error(`Refresh failed: ${res.status}`);\n const json = await res.json();\n if (!json?.[accessKey]) throw new Error('Malformed refresh response');\n storage.setAccessToken(json[accessKey]);\n if (json.refresh_token) storage.setRefreshToken?.(json.refresh_token);\n },\n setToken({ accessToken, refreshToken }) {\n if (accessToken) storage.setAccessToken(accessToken);\n if (refreshToken) storage.setRefreshToken?.(refreshToken);\n },\n clearToken() {\n storage.setAccessToken(null);\n storage.setRefreshToken?.(null);\n },\n };\n}\n","export interface TokenStorage {\n getAccessToken(): string | null;\n setAccessToken(token: string | null): void;\n getRefreshToken?(): string | null;\n setRefreshToken?(token: string | null): void;\n}\n\nexport const memoryStorage: TokenStorage = (() => {\n let access: string | null = null;\n let refresh: string | null = null;\n return {\n getAccessToken: () => access,\n setAccessToken: (t) => (access = t),\n getRefreshToken: () => refresh,\n setRefreshToken: (t) => (refresh = t),\n };\n})();\n\nconst safeLocal = () => {\n try {\n return typeof window !== 'undefined' ? window.localStorage : null;\n } catch {\n return null;\n }\n};\n\nexport const localStorageStorage: TokenStorage = {\n getAccessToken: () => safeLocal()?.getItem('access_token') ?? null,\n setAccessToken: (t) => {\n const ls = safeLocal();\n if (!ls) return;\n if (t) {\n ls.setItem('access_token', t);\n } else {\n ls.removeItem('access_token');\n }\n },\n getRefreshToken: () => safeLocal()?.getItem('refresh_token') ?? null,\n setRefreshToken: (t) => {\n const ls = safeLocal();\n if (!ls) return;\n if (t) {\n ls.setItem('refresh_token', t);\n } else {\n ls.removeItem('refresh_token');\n }\n },\n};\n","import { getGlobalRequestConfig } from './config';\n\nexport async function login(\n perform: () => Promise<{ accessToken?: string; refreshToken?: string }>,\n) {\n const auth = getGlobalRequestConfig().auth;\n const json = await perform();\n auth?.setToken?.(json);\n return json;\n}\n\nexport async function logout<T>(perform?: () => Promise<T>) {\n const auth = getGlobalRequestConfig().auth;\n try {\n if (perform) await perform();\n } finally {\n auth?.clearToken?.();\n }\n}\n"],"mappings":"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","names":["e","axios","request","axios","AxiosHeaders","AxiosHeaders","_a","_b"]} |
+23
-13
@@ -32,6 +32,9 @@ "use strict"; | ||
| const [error, setError] = (0, import_react.useState)(null); | ||
| const [loading, setLoading] = (0, import_react.useState)(false); | ||
| const [status, setStatus] = (0, import_react.useState)( | ||
| (opts == null ? void 0 : opts.lazy) ? "idle" : initialValue === void 0 ? "loading" : "idle" | ||
| ); | ||
| const abortRef = (0, import_react.useRef)(null); | ||
| const mountedRef = (0, import_react.useRef)(true); | ||
| (0, import_react.useEffect)(() => { | ||
| mountedRef.current = true; | ||
| return () => { | ||
@@ -49,3 +52,3 @@ var _a; | ||
| if (mountedRef.current) { | ||
| setLoading(true); | ||
| setStatus("loading"); | ||
| setError(null); | ||
@@ -55,16 +58,17 @@ } | ||
| const res = await request(ctrl.signal); | ||
| if (!mountedRef.current || ctrl.signal.aborted) | ||
| const isStale = !mountedRef.current || ctrl.signal.aborted || abortRef.current !== ctrl; | ||
| if (isStale) | ||
| return res; | ||
| setData(res); | ||
| setStatus("success"); | ||
| return res; | ||
| } catch (e) { | ||
| const isCanceled = (e == null ? void 0 : e.name) === "AbortError" || (e == null ? void 0 : e.name) === "CanceledError" || (e == null ? void 0 : e.code) === "ERR_CANCELED"; | ||
| if (!isCanceled && mountedRef.current && !ctrl.signal.aborted) { | ||
| setError(e); | ||
| const isStale = !mountedRef.current || ctrl.signal.aborted || abortRef.current !== ctrl; | ||
| if (isCanceled || isStale) { | ||
| return void 0; | ||
| } | ||
| setError(e); | ||
| setStatus("error"); | ||
| throw e; | ||
| } finally { | ||
| if (mountedRef.current && !ctrl.signal.aborted) { | ||
| setLoading(false); | ||
| } | ||
| } | ||
@@ -81,6 +85,12 @@ }, deps); | ||
| }, [run]); | ||
| return { data, error, loading, run, abort: () => { | ||
| var _a; | ||
| return (_a = abortRef.current) == null ? void 0 : _a.abort(); | ||
| } }; | ||
| return { | ||
| data, | ||
| error, | ||
| loading: status === "loading", | ||
| run, | ||
| abort: () => { | ||
| var _a; | ||
| return (_a = abortRef.current) == null ? void 0 : _a.abort(); | ||
| } | ||
| }; | ||
| } | ||
@@ -87,0 +97,0 @@ // Annotate the CommonJS export names for ESM import in node: |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/react/index.ts","../src/react/use-request.ts"],"sourcesContent":["export * from './use-request';\n","import { useState, useRef, useEffect, useCallback } from 'react';\n\nexport function useRequest<T, E = unknown>(\n request: (signal: AbortSignal) => Promise<T>,\n deps: React.DependencyList = [],\n initialValue?: T,\n opts?: { lazy?: boolean },\n) {\n const [data, setData] = useState<T | undefined>(initialValue);\n const [error, setError] = useState<E | null>(null);\n const [loading, setLoading] = useState(false);\n\n const abortRef = useRef<AbortController | null>(null);\n const mountedRef = useRef(true);\n\n // Track mounted state to avoid state updates after unmount\n useEffect(() => {\n return () => {\n mountedRef.current = false;\n abortRef.current?.abort();\n };\n }, []);\n\n const run = useCallback(async () => {\n abortRef.current?.abort();\n const ctrl = new AbortController();\n abortRef.current = ctrl;\n\n if (mountedRef.current) {\n setLoading(true);\n setError(null);\n }\n\n try {\n const res = await request(ctrl.signal);\n\n // Guard: do not update state if aborted or unmounted\n if (!mountedRef.current || ctrl.signal.aborted) return res;\n\n setData(res);\n return res;\n } catch (e) {\n const isCanceled =\n (e as { name: string })?.name === 'AbortError' ||\n (e as { name: string })?.name === 'CanceledError' ||\n (e as { code: string })?.code === 'ERR_CANCELED';\n\n if (!isCanceled && mountedRef.current && !ctrl.signal.aborted) {\n setError(e as E);\n }\n throw e;\n } finally {\n if (mountedRef.current && !ctrl.signal.aborted) {\n setLoading(false);\n }\n }\n }, deps);\n\n useEffect(() => {\n if (!opts?.lazy) {\n run();\n return () => abortRef.current?.abort();\n }\n }, [run]);\n\n return { data, error, loading, run, abort: () => abortRef.current?.abort() };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAyD;AAElD,SAAS,WACd,SACA,OAA6B,CAAC,GAC9B,cACA,MACA;AACA,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAwB,YAAY;AAC5D,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAmB,IAAI;AACjD,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAE5C,QAAM,eAAW,qBAA+B,IAAI;AACpD,QAAM,iBAAa,qBAAO,IAAI;AAG9B,8BAAU,MAAM;AACd,WAAO,MAAM;AAjBjB;AAkBM,iBAAW,UAAU;AACrB,qBAAS,YAAT,mBAAkB;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,UAAM,0BAAY,YAAY;AAvBtC;AAwBI,mBAAS,YAAT,mBAAkB;AAClB,UAAM,OAAO,IAAI,gBAAgB;AACjC,aAAS,UAAU;AAEnB,QAAI,WAAW,SAAS;AACtB,iBAAW,IAAI;AACf,eAAS,IAAI;AAAA,IACf;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,KAAK,MAAM;AAGrC,UAAI,CAAC,WAAW,WAAW,KAAK,OAAO;AAAS,eAAO;AAEvD,cAAQ,GAAG;AACX,aAAO;AAAA,IACT,SAAS,GAAG;AACV,YAAM,cACH,uBAAwB,UAAS,iBACjC,uBAAwB,UAAS,oBACjC,uBAAwB,UAAS;AAEpC,UAAI,CAAC,cAAc,WAAW,WAAW,CAAC,KAAK,OAAO,SAAS;AAC7D,iBAAS,CAAM;AAAA,MACjB;AACA,YAAM;AAAA,IACR,UAAE;AACA,UAAI,WAAW,WAAW,CAAC,KAAK,OAAO,SAAS;AAC9C,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,EACF,GAAG,IAAI;AAEP,8BAAU,MAAM;AACd,QAAI,EAAC,6BAAM,OAAM;AACf,UAAI;AACJ,aAAO,MAAG;AA7DhB;AA6DmB,8BAAS,YAAT,mBAAkB;AAAA;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,SAAO,EAAE,MAAM,OAAO,SAAS,KAAK,OAAO,MAAG;AAjEhD;AAiEmD,0BAAS,YAAT,mBAAkB;AAAA,IAAQ;AAC7E;","names":[]} | ||
| {"version":3,"sources":["../src/react/index.ts","../src/react/use-request.ts"],"sourcesContent":["export { useRequest } from './use-request';\n","import { useState, useRef, useEffect, useCallback } from 'react';\n\ntype Status = 'idle' | 'loading' | 'success' | 'error';\n\nexport function useRequest<T, E = unknown>(\n request: (signal: AbortSignal) => Promise<T>,\n deps: React.DependencyList = [],\n initialValue?: T,\n opts?: { lazy?: boolean },\n) {\n const [data, setData] = useState<T | undefined>(initialValue);\n const [error, setError] = useState<E | null>(null);\n const [status, setStatus] = useState<Status>(\n opts?.lazy ? 'idle' : initialValue === undefined ? 'loading' : 'idle',\n );\n\n const abortRef = useRef<AbortController | null>(null);\n const mountedRef = useRef(true);\n\n // Track mounted state to avoid state updates after unmount\n useEffect(() => {\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n abortRef.current?.abort();\n };\n }, []);\n\n const run = useCallback(async () => {\n abortRef.current?.abort();\n const ctrl = new AbortController();\n abortRef.current = ctrl;\n\n if (mountedRef.current) {\n setStatus('loading');\n setError(null);\n }\n\n try {\n const res = await request(ctrl.signal);\n\n // stale or canceled -> do not write\n const isStale =\n !mountedRef.current || ctrl.signal.aborted || abortRef.current !== ctrl;\n if (isStale) return res;\n\n setData(res);\n setStatus('success');\n return res;\n } catch (e) {\n const isCanceled =\n (e as { name?: string })?.name === 'AbortError' ||\n (e as { name?: string })?.name === 'CanceledError' ||\n (e as { code?: string })?.code === 'ERR_CANCELED';\n\n const isStale =\n !mountedRef.current || ctrl.signal.aborted || abortRef.current !== ctrl;\n\n if (isCanceled || isStale) {\n // swallow cancellations/stale completions; keep current status\n return undefined as unknown as T;\n }\n\n // real error (current generation)\n setError(e as E);\n setStatus('error');\n throw e;\n }\n }, deps);\n\n useEffect(() => {\n if (!opts?.lazy) {\n run();\n return () => abortRef.current?.abort();\n }\n }, [run]);\n\n return {\n data,\n error,\n loading: status === 'loading',\n run,\n abort: () => abortRef.current?.abort(),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAyD;AAIlD,SAAS,WACd,SACA,OAA6B,CAAC,GAC9B,cACA,MACA;AACA,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAwB,YAAY;AAC5D,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAmB,IAAI;AACjD,QAAM,CAAC,QAAQ,SAAS,QAAI;AAAA,KAC1B,6BAAM,QAAO,SAAS,iBAAiB,SAAY,YAAY;AAAA,EACjE;AAEA,QAAM,eAAW,qBAA+B,IAAI;AACpD,QAAM,iBAAa,qBAAO,IAAI;AAG9B,8BAAU,MAAM;AACd,eAAW,UAAU;AAErB,WAAO,MAAM;AAvBjB;AAwBM,iBAAW,UAAU;AACrB,qBAAS,YAAT,mBAAkB;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,UAAM,0BAAY,YAAY;AA7BtC;AA8BI,mBAAS,YAAT,mBAAkB;AAClB,UAAM,OAAO,IAAI,gBAAgB;AACjC,aAAS,UAAU;AAEnB,QAAI,WAAW,SAAS;AACtB,gBAAU,SAAS;AACnB,eAAS,IAAI;AAAA,IACf;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,KAAK,MAAM;AAGrC,YAAM,UACJ,CAAC,WAAW,WAAW,KAAK,OAAO,WAAW,SAAS,YAAY;AACrE,UAAI;AAAS,eAAO;AAEpB,cAAQ,GAAG;AACX,gBAAU,SAAS;AACnB,aAAO;AAAA,IACT,SAAS,GAAG;AACV,YAAM,cACH,uBAAyB,UAAS,iBAClC,uBAAyB,UAAS,oBAClC,uBAAyB,UAAS;AAErC,YAAM,UACJ,CAAC,WAAW,WAAW,KAAK,OAAO,WAAW,SAAS,YAAY;AAErE,UAAI,cAAc,SAAS;AAEzB,eAAO;AAAA,MACT;AAGA,eAAS,CAAM;AACf,gBAAU,OAAO;AACjB,YAAM;AAAA,IACR;AAAA,EACF,GAAG,IAAI;AAEP,8BAAU,MAAM;AACd,QAAI,EAAC,6BAAM,OAAM;AACf,UAAI;AACJ,aAAO,MAAG;AA1EhB;AA0EmB,8BAAS,YAAT,mBAAkB;AAAA;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS,WAAW;AAAA,IACpB;AAAA,IACA,OAAO,MAAG;AAnFd;AAmFiB,4BAAS,YAAT,mBAAkB;AAAA;AAAA,EACjC;AACF;","names":[]} |
+23
-13
@@ -6,6 +6,9 @@ // src/react/use-request.ts | ||
| const [error, setError] = useState(null); | ||
| const [loading, setLoading] = useState(false); | ||
| const [status, setStatus] = useState( | ||
| (opts == null ? void 0 : opts.lazy) ? "idle" : initialValue === void 0 ? "loading" : "idle" | ||
| ); | ||
| const abortRef = useRef(null); | ||
| const mountedRef = useRef(true); | ||
| useEffect(() => { | ||
| mountedRef.current = true; | ||
| return () => { | ||
@@ -23,3 +26,3 @@ var _a; | ||
| if (mountedRef.current) { | ||
| setLoading(true); | ||
| setStatus("loading"); | ||
| setError(null); | ||
@@ -29,16 +32,17 @@ } | ||
| const res = await request(ctrl.signal); | ||
| if (!mountedRef.current || ctrl.signal.aborted) | ||
| const isStale = !mountedRef.current || ctrl.signal.aborted || abortRef.current !== ctrl; | ||
| if (isStale) | ||
| return res; | ||
| setData(res); | ||
| setStatus("success"); | ||
| return res; | ||
| } catch (e) { | ||
| const isCanceled = (e == null ? void 0 : e.name) === "AbortError" || (e == null ? void 0 : e.name) === "CanceledError" || (e == null ? void 0 : e.code) === "ERR_CANCELED"; | ||
| if (!isCanceled && mountedRef.current && !ctrl.signal.aborted) { | ||
| setError(e); | ||
| const isStale = !mountedRef.current || ctrl.signal.aborted || abortRef.current !== ctrl; | ||
| if (isCanceled || isStale) { | ||
| return void 0; | ||
| } | ||
| setError(e); | ||
| setStatus("error"); | ||
| throw e; | ||
| } finally { | ||
| if (mountedRef.current && !ctrl.signal.aborted) { | ||
| setLoading(false); | ||
| } | ||
| } | ||
@@ -55,6 +59,12 @@ }, deps); | ||
| }, [run]); | ||
| return { data, error, loading, run, abort: () => { | ||
| var _a; | ||
| return (_a = abortRef.current) == null ? void 0 : _a.abort(); | ||
| } }; | ||
| return { | ||
| data, | ||
| error, | ||
| loading: status === "loading", | ||
| run, | ||
| abort: () => { | ||
| var _a; | ||
| return (_a = abortRef.current) == null ? void 0 : _a.abort(); | ||
| } | ||
| }; | ||
| } | ||
@@ -61,0 +71,0 @@ export { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/react/use-request.ts"],"sourcesContent":["import { useState, useRef, useEffect, useCallback } from 'react';\n\nexport function useRequest<T, E = unknown>(\n request: (signal: AbortSignal) => Promise<T>,\n deps: React.DependencyList = [],\n initialValue?: T,\n opts?: { lazy?: boolean },\n) {\n const [data, setData] = useState<T | undefined>(initialValue);\n const [error, setError] = useState<E | null>(null);\n const [loading, setLoading] = useState(false);\n\n const abortRef = useRef<AbortController | null>(null);\n const mountedRef = useRef(true);\n\n // Track mounted state to avoid state updates after unmount\n useEffect(() => {\n return () => {\n mountedRef.current = false;\n abortRef.current?.abort();\n };\n }, []);\n\n const run = useCallback(async () => {\n abortRef.current?.abort();\n const ctrl = new AbortController();\n abortRef.current = ctrl;\n\n if (mountedRef.current) {\n setLoading(true);\n setError(null);\n }\n\n try {\n const res = await request(ctrl.signal);\n\n // Guard: do not update state if aborted or unmounted\n if (!mountedRef.current || ctrl.signal.aborted) return res;\n\n setData(res);\n return res;\n } catch (e) {\n const isCanceled =\n (e as { name: string })?.name === 'AbortError' ||\n (e as { name: string })?.name === 'CanceledError' ||\n (e as { code: string })?.code === 'ERR_CANCELED';\n\n if (!isCanceled && mountedRef.current && !ctrl.signal.aborted) {\n setError(e as E);\n }\n throw e;\n } finally {\n if (mountedRef.current && !ctrl.signal.aborted) {\n setLoading(false);\n }\n }\n }, deps);\n\n useEffect(() => {\n if (!opts?.lazy) {\n run();\n return () => abortRef.current?.abort();\n }\n }, [run]);\n\n return { data, error, loading, run, abort: () => abortRef.current?.abort() };\n}\n"],"mappings":";AAAA,SAAS,UAAU,QAAQ,WAAW,mBAAmB;AAElD,SAAS,WACd,SACA,OAA6B,CAAC,GAC9B,cACA,MACA;AACA,QAAM,CAAC,MAAM,OAAO,IAAI,SAAwB,YAAY;AAC5D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAmB,IAAI;AACjD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,QAAM,WAAW,OAA+B,IAAI;AACpD,QAAM,aAAa,OAAO,IAAI;AAG9B,YAAU,MAAM;AACd,WAAO,MAAM;AAjBjB;AAkBM,iBAAW,UAAU;AACrB,qBAAS,YAAT,mBAAkB;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,MAAM,YAAY,YAAY;AAvBtC;AAwBI,mBAAS,YAAT,mBAAkB;AAClB,UAAM,OAAO,IAAI,gBAAgB;AACjC,aAAS,UAAU;AAEnB,QAAI,WAAW,SAAS;AACtB,iBAAW,IAAI;AACf,eAAS,IAAI;AAAA,IACf;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,KAAK,MAAM;AAGrC,UAAI,CAAC,WAAW,WAAW,KAAK,OAAO;AAAS,eAAO;AAEvD,cAAQ,GAAG;AACX,aAAO;AAAA,IACT,SAAS,GAAG;AACV,YAAM,cACH,uBAAwB,UAAS,iBACjC,uBAAwB,UAAS,oBACjC,uBAAwB,UAAS;AAEpC,UAAI,CAAC,cAAc,WAAW,WAAW,CAAC,KAAK,OAAO,SAAS;AAC7D,iBAAS,CAAM;AAAA,MACjB;AACA,YAAM;AAAA,IACR,UAAE;AACA,UAAI,WAAW,WAAW,CAAC,KAAK,OAAO,SAAS;AAC9C,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,EACF,GAAG,IAAI;AAEP,YAAU,MAAM;AACd,QAAI,EAAC,6BAAM,OAAM;AACf,UAAI;AACJ,aAAO,MAAG;AA7DhB;AA6DmB,8BAAS,YAAT,mBAAkB;AAAA;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,SAAO,EAAE,MAAM,OAAO,SAAS,KAAK,OAAO,MAAG;AAjEhD;AAiEmD,0BAAS,YAAT,mBAAkB;AAAA,IAAQ;AAC7E;","names":[]} | ||
| {"version":3,"sources":["../src/react/use-request.ts"],"sourcesContent":["import { useState, useRef, useEffect, useCallback } from 'react';\n\ntype Status = 'idle' | 'loading' | 'success' | 'error';\n\nexport function useRequest<T, E = unknown>(\n request: (signal: AbortSignal) => Promise<T>,\n deps: React.DependencyList = [],\n initialValue?: T,\n opts?: { lazy?: boolean },\n) {\n const [data, setData] = useState<T | undefined>(initialValue);\n const [error, setError] = useState<E | null>(null);\n const [status, setStatus] = useState<Status>(\n opts?.lazy ? 'idle' : initialValue === undefined ? 'loading' : 'idle',\n );\n\n const abortRef = useRef<AbortController | null>(null);\n const mountedRef = useRef(true);\n\n // Track mounted state to avoid state updates after unmount\n useEffect(() => {\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n abortRef.current?.abort();\n };\n }, []);\n\n const run = useCallback(async () => {\n abortRef.current?.abort();\n const ctrl = new AbortController();\n abortRef.current = ctrl;\n\n if (mountedRef.current) {\n setStatus('loading');\n setError(null);\n }\n\n try {\n const res = await request(ctrl.signal);\n\n // stale or canceled -> do not write\n const isStale =\n !mountedRef.current || ctrl.signal.aborted || abortRef.current !== ctrl;\n if (isStale) return res;\n\n setData(res);\n setStatus('success');\n return res;\n } catch (e) {\n const isCanceled =\n (e as { name?: string })?.name === 'AbortError' ||\n (e as { name?: string })?.name === 'CanceledError' ||\n (e as { code?: string })?.code === 'ERR_CANCELED';\n\n const isStale =\n !mountedRef.current || ctrl.signal.aborted || abortRef.current !== ctrl;\n\n if (isCanceled || isStale) {\n // swallow cancellations/stale completions; keep current status\n return undefined as unknown as T;\n }\n\n // real error (current generation)\n setError(e as E);\n setStatus('error');\n throw e;\n }\n }, deps);\n\n useEffect(() => {\n if (!opts?.lazy) {\n run();\n return () => abortRef.current?.abort();\n }\n }, [run]);\n\n return {\n data,\n error,\n loading: status === 'loading',\n run,\n abort: () => abortRef.current?.abort(),\n };\n}\n"],"mappings":";AAAA,SAAS,UAAU,QAAQ,WAAW,mBAAmB;AAIlD,SAAS,WACd,SACA,OAA6B,CAAC,GAC9B,cACA,MACA;AACA,QAAM,CAAC,MAAM,OAAO,IAAI,SAAwB,YAAY;AAC5D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAmB,IAAI;AACjD,QAAM,CAAC,QAAQ,SAAS,IAAI;AAAA,KAC1B,6BAAM,QAAO,SAAS,iBAAiB,SAAY,YAAY;AAAA,EACjE;AAEA,QAAM,WAAW,OAA+B,IAAI;AACpD,QAAM,aAAa,OAAO,IAAI;AAG9B,YAAU,MAAM;AACd,eAAW,UAAU;AAErB,WAAO,MAAM;AAvBjB;AAwBM,iBAAW,UAAU;AACrB,qBAAS,YAAT,mBAAkB;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,MAAM,YAAY,YAAY;AA7BtC;AA8BI,mBAAS,YAAT,mBAAkB;AAClB,UAAM,OAAO,IAAI,gBAAgB;AACjC,aAAS,UAAU;AAEnB,QAAI,WAAW,SAAS;AACtB,gBAAU,SAAS;AACnB,eAAS,IAAI;AAAA,IACf;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,KAAK,MAAM;AAGrC,YAAM,UACJ,CAAC,WAAW,WAAW,KAAK,OAAO,WAAW,SAAS,YAAY;AACrE,UAAI;AAAS,eAAO;AAEpB,cAAQ,GAAG;AACX,gBAAU,SAAS;AACnB,aAAO;AAAA,IACT,SAAS,GAAG;AACV,YAAM,cACH,uBAAyB,UAAS,iBAClC,uBAAyB,UAAS,oBAClC,uBAAyB,UAAS;AAErC,YAAM,UACJ,CAAC,WAAW,WAAW,KAAK,OAAO,WAAW,SAAS,YAAY;AAErE,UAAI,cAAc,SAAS;AAEzB,eAAO;AAAA,MACT;AAGA,eAAS,CAAM;AACf,gBAAU,OAAO;AACjB,YAAM;AAAA,IACR;AAAA,EACF,GAAG,IAAI;AAEP,YAAU,MAAM;AACd,QAAI,EAAC,6BAAM,OAAM;AACf,UAAI;AACJ,aAAO,MAAG;AA1EhB;AA0EmB,8BAAS,YAAT,mBAAkB;AAAA;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS,WAAW;AAAA,IACpB;AAAA,IACA,OAAO,MAAG;AAnFd;AAmFiB,4BAAS,YAAT,mBAAkB;AAAA;AAAA,EACjC;AACF;","names":[]} |
+1
-1
| { | ||
| "name": "@mints/request", | ||
| "packageManager": "yarn@4.9.2", | ||
| "version": "2.0.0", | ||
| "version": "2.1.0", | ||
| "type": "module", | ||
@@ -6,0 +6,0 @@ "description": "A lightweight Axios + operation wrapper with global config and toast integration", |
+21
-2
@@ -16,2 +16,3 @@ # @mints/request | ||
| - ✅ **React hook `useRequest`** for automatic requests with cancellation | ||
| - ✅ Helper functions `login` / `logout` for auto token management | ||
| - ✅ Minimal dependencies, framework agnostic (core) + optional React add-on | ||
@@ -48,2 +49,3 @@ | ||
| refreshPath: '/auth/refresh', | ||
| tokenField: 'jwt', // default: "access_token" | ||
| }), | ||
@@ -56,4 +58,5 @@ onUnauthorized: () => { | ||
| - By default `createCookieStrategy` stores `access_token` in memory (`memoryStorage`). | ||
| - By default `createCookieStrategy` stores `token` in memory (`memoryStorage`). | ||
| - You can pass a custom `storage` (e.g. `localStorageStorage`) if persistence is needed. | ||
| - Default refresh status codes: `401, 419, 440`. | ||
@@ -71,2 +74,4 @@ --- | ||
| const users = await request('/users'); | ||
| // or | ||
| const users = await request.public('/users'); | ||
@@ -79,2 +84,4 @@ // Authenticated API | ||
| - `request.auth(url, config)` → includes auth, retries after refresh if needed. | ||
| - `request.init(url, { soft?: boolean })` → probe request, optional `soft=true` skips refresh. | ||
| - `request.reset(url)` → reset probe state. | ||
@@ -95,2 +102,13 @@ --- | ||
| ### 🔹 Token management helpers | ||
| ```ts | ||
| import { login, logout } from '@mints/request/auth'; | ||
| await login(() => API.auth.login(form)); | ||
| await logout(() => API.auth.logout()); | ||
| ``` | ||
| --- | ||
| ## 🚀 Usage (React Add-on) | ||
@@ -138,3 +156,3 @@ | ||
| retryAfterRefresh?: number; // default 1 | ||
| shouldRefreshOnStatus?: (status: number) => boolean; // default: 401 | ||
| shouldRefreshOnStatus?: (status: number) => boolean; // default: 401, 419, 440 | ||
| }; | ||
@@ -208,2 +226,3 @@ ``` | ||
| - Use `request.init` for probe token | ||
| - Use `request.public` for endpoints that don't require auth. | ||
@@ -210,0 +229,0 @@ - Use `request.auth` for APIs with tokens; retries are automatic. |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
105999
12.39%1095
15.75%231
8.96%4
-20%