@swr-login/react 0.9.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -192,12 +192,14 @@ function useUser() {
192
192
  const prev = previousUserRef.current;
193
193
  if (Object.is(prev, data)) return;
194
194
  let source;
195
- if (prev === void 0) {
195
+ const now = Date.now();
196
+ const hintFresh = userChangeHint.source !== null && now - userChangeHint.timestamp <= USER_CHANGE_HINT_TTL_MS;
197
+ if (hintFresh) {
198
+ source = userChangeHint.source;
199
+ } else if (prev === void 0) {
196
200
  source = "initial";
197
201
  } else {
198
- const now = Date.now();
199
- const hintFresh = userChangeHint.source !== null && now - userChangeHint.timestamp <= USER_CHANGE_HINT_TTL_MS;
200
- source = hintFresh ? userChangeHint.source : "revalidate";
202
+ source = "revalidate";
201
203
  }
202
204
  const event = {
203
205
  source,
@@ -257,15 +259,18 @@ function useLogin(pluginName) {
257
259
  const [isLoading, setIsLoading] = react.useState(false);
258
260
  const [error, setError] = react.useState(null);
259
261
  const login = react.useCallback(
260
- async (credentialsOrPluginName, maybeCredentials) => {
262
+ async (credentialsOrPluginName, credentialsOrOptions, maybeOptions) => {
261
263
  let resolvedPlugin;
262
264
  let resolvedCredentials;
265
+ let resolvedOptions;
263
266
  if (typeof credentialsOrPluginName === "string" && !pluginName) {
264
267
  resolvedPlugin = credentialsOrPluginName;
265
- resolvedCredentials = maybeCredentials ?? {};
268
+ resolvedCredentials = credentialsOrOptions ?? {};
269
+ resolvedOptions = maybeOptions;
266
270
  } else if (pluginName) {
267
271
  resolvedPlugin = pluginName;
268
272
  resolvedCredentials = credentialsOrPluginName;
273
+ resolvedOptions = credentialsOrOptions;
269
274
  } else {
270
275
  throw new Error(
271
276
  "[swr-login] Plugin name is required. Provide it to useLogin() or login()."
@@ -285,7 +290,11 @@ function useLogin(pluginName) {
285
290
  throw err;
286
291
  }
287
292
  try {
288
- const response = await pluginManager.login(resolvedPlugin, resolvedCredentials);
293
+ const response = await pluginManager.login(
294
+ resolvedPlugin,
295
+ resolvedCredentials,
296
+ resolvedOptions
297
+ );
289
298
  let shouldSkipFetchUser = false;
290
299
  if (config.afterAuth) {
291
300
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/hooks/useUser.ts","../src/hooks/useLogin.ts","../src/hooks/useMultiStepLogin.ts","../src/hooks/useAuthInjector.ts","../src/hooks/useUserChange.ts","../src/hooks/useLogout.ts","../src/hooks/useAdapter.ts","../src/hooks/useSession.ts","../src/hooks/usePermission.ts","../src/components/AuthGuard.tsx"],"names":["createContext","useContext","useRef","useMemo","AuthEventEmitter","AuthStateMachine","TokenManager","PluginManager","BroadcastSync","useEffect","useState","useCallback","useSWR","mutate","isMultiStepPlugin","swrGlobalMutate","isTokenExpired","jsx","Fragment"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n AuthEventEmitter,\n AuthStateMachine,\n BroadcastSync,\n PluginManager,\n SWRLoginConfig,\n TokenManager,\n UserChangeSource,\n} from '@swr-login/core';\nimport { createContext, useContext } from 'react';\n\n/**\n * Mutable hint object shared between the Provider and `useUser()` to describe\n * *why* the next user-change event is expected to happen.\n *\n * The Provider writes into this object as it observes `login` / `logout` /\n * `external` events; `useUser()` reads and clears it when the SWR cache value\n * actually transitions. Unset/expired hint → the change is a passive\n * `revalidate` or the very first `initial` load.\n *\n * Using a mutable ref-like object (instead of React state) avoids re-renders\n * and is safe because it is written synchronously from event callbacks and\n * read synchronously during `useUser()`'s `useEffect`.\n *\n * @internal\n */\nexport interface UserChangeHint {\n source: UserChangeSource | null;\n /** `Date.now()` when the hint was written. Stale hints (>1s) are ignored. */\n timestamp: number;\n}\n\n/** Internal context value passed through SWRLoginProvider */\nexport interface AuthContextValue {\n pluginManager: PluginManager;\n tokenManager: TokenManager;\n emitter: AuthEventEmitter;\n stateMachine: AuthStateMachine;\n broadcastSync: BroadcastSync | null;\n config: SWRLoginConfig;\n /** @internal shared hint for the next user-change source */\n userChangeHint: UserChangeHint;\n}\n\nexport const AuthContext = createContext<AuthContextValue | null>(null);\n\n/**\n * Internal hook to access auth context.\n * Must be used within SWRLoginProvider.\n * @throws Error if used outside of SWRLoginProvider\n */\nexport function useAuthContext(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) {\n throw new Error(\n '[swr-login] useAuthContext must be used within <SWRLoginProvider>. ' +\n 'Wrap your app with <SWRLoginProvider> to use swr-login hooks.',\n );\n }\n return ctx;\n}\n","import {\n AuthEventEmitter,\n AuthStateMachine,\n BroadcastSync,\n PluginManager,\n type SWRLoginConfig,\n TokenManager,\n} from '@swr-login/core';\nimport type React from 'react';\nimport { useEffect, useMemo, useRef } from 'react';\nimport { AuthContext, type AuthContextValue, type UserChangeHint } from './context';\n\nexport interface SWRLoginProviderProps {\n /** Authentication configuration */\n config: SWRLoginConfig;\n children: React.ReactNode;\n}\n\n/**\n * SWRLoginProvider initializes the auth system and provides context to all child hooks.\n *\n * @example\n * ```tsx\n * import { SWRLoginProvider } from '@swr-login/react';\n * import { JWTAdapter } from '@swr-login/adapter-jwt';\n * import { PasswordPlugin } from '@swr-login/plugin-password';\n *\n * function App() {\n * return (\n * <SWRLoginProvider\n * config={{\n * adapter: JWTAdapter(),\n * plugins: [PasswordPlugin({ loginUrl: '/api/login' })],\n * }}\n * >\n * <YourApp />\n * </SWRLoginProvider>\n * );\n * }\n * ```\n */\nexport function SWRLoginProvider({ config, children }: SWRLoginProviderProps) {\n const initializedRef = useRef(false);\n\n const contextValue = useMemo<AuthContextValue>(() => {\n const emitter = new AuthEventEmitter();\n const stateMachine = new AuthStateMachine(emitter);\n const tokenManager = new TokenManager(config.adapter, emitter, stateMachine);\n const pluginManager = new PluginManager(tokenManager, emitter);\n\n // Register plugins\n pluginManager.register(...config.plugins);\n\n // Set up broadcast sync if enabled\n const enableSync = config.security?.enableBroadcastSync !== false;\n const broadcastSync = enableSync && typeof window !== 'undefined' ? new BroadcastSync() : null;\n\n // Mutable hint object shared with useUser() to label the next user-change\n // event with its causal source (login/logout/external).\n const userChangeHint: UserChangeHint = { source: null, timestamp: 0 };\n const markHint = (source: UserChangeHint['source']) => {\n userChangeHint.source = source;\n userChangeHint.timestamp = Date.now();\n };\n\n // Label user-change source based on lifecycle events. The actual\n // 'user-change' event is emitted by useUser() once SWR produces a\n // different value; these hints only tell us *why* it's about to change.\n emitter.on('login', () => markHint('login'));\n emitter.on('logout', () => markHint('logout'));\n\n // Wire up lifecycle callbacks\n if (config.onLogin) {\n emitter.on('login', ({ user }) => config.onLogin?.(user));\n }\n if (config.onLogout) {\n emitter.on('logout', () => config.onLogout?.());\n }\n if (config.onError) {\n emitter.on('error', ({ error }) => config.onError?.(error));\n }\n\n return {\n pluginManager,\n tokenManager,\n emitter,\n stateMachine,\n broadcastSync,\n config,\n userChangeHint,\n };\n }, [config]);\n\n // Initialize plugins and broadcast sync on mount\n useEffect(() => {\n if (initializedRef.current) return;\n initializedRef.current = true;\n\n const {\n pluginManager,\n broadcastSync,\n tokenManager,\n stateMachine,\n emitter,\n config: cfg,\n } = contextValue;\n\n // Initialize all plugins\n pluginManager.initializeAll().catch((err) => {\n console.error('[swr-login] Plugin initialization error:', err);\n });\n\n // Check if user has existing token -> restore session\n const existingToken = tokenManager.getAccessToken();\n const expiresAt = tokenManager.getExpiresAt();\n\n if (existingToken && !tokenManager.isExpired()) {\n stateMachine.transition('authenticated');\n } else if (existingToken && expiresAt === null) {\n // expiresAt 未知(如外部登录只设置了 token 但未设置过期时间)\n // 乐观地认为已认证,让 fetchUser / SWR revalidate 来验证\n stateMachine.transition('authenticated');\n } else if (existingToken && tokenManager.isExpired()) {\n stateMachine.transition('unauthenticated');\n emitter.emit('token-expired', undefined);\n }\n\n // Listen for cross-tab events\n if (broadcastSync) {\n const { userChangeHint } = contextValue;\n const markExternal = () => {\n userChangeHint.source = 'external';\n userChangeHint.timestamp = Date.now();\n };\n\n const unsubscribe = broadcastSync.onMessage((message) => {\n switch (message.type) {\n case 'LOGOUT':\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n // Emit-after-mark would be overwritten by the synchronous\n // 'logout' handler (which sets hint='logout'); re-mark to\n // 'external' so useUser labels this cross-tab logout correctly.\n markExternal();\n break;\n case 'LOGIN':\n case 'TOKEN_REFRESH':\n // Cross-tab login / refresh → refresh SWR cache; label the\n // upcoming user-change as 'external'.\n markExternal();\n if (cfg.cacheAdapter) {\n cfg.cacheAdapter.revalidate();\n }\n break;\n }\n });\n\n return () => {\n unsubscribe();\n broadcastSync.destroy();\n };\n }\n }, [contextValue]);\n\n // Visibility change handler for security\n useEffect(() => {\n const { config: cfg, tokenManager, stateMachine, emitter } = contextValue;\n\n if (!cfg.security?.clearOnHidden || typeof document === 'undefined') return;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const handleVisibilityChange = () => {\n if (document.hidden) {\n const delay = cfg.security?.clearOnHiddenDelay ?? 300_000;\n timeoutId = setTimeout(() => {\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n }, delay);\n } else {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n if (timeoutId) clearTimeout(timeoutId);\n };\n }, [contextValue]);\n\n return <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>;\n}\n","import type { User, UserChangeEvent, UserChangeSource } from '@swr-login/core';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport useSWR from 'swr';\nimport { useAuthContext } from '../context';\n\nconst AUTH_KEY = '__swr_login_user__';\n\n/**\n * Time window (ms) during which a hint written by the Provider remains\n * valid. If SWR produces a change later than this after the hint was\n * written, we treat it as a passive `revalidate` instead.\n */\nconst USER_CHANGE_HINT_TTL_MS = 1000;\n\nexport interface UseUserReturn<T extends User = User> {\n /** Current authenticated user, or null if not logged in */\n user: T | null;\n /** Whether user data is being fetched */\n isLoading: boolean;\n /** Whether user is authenticated */\n isAuthenticated: boolean;\n /** Error from fetching user data */\n error: Error | undefined;\n /**\n * Sticky error from the last `fetchUser` failure.\n *\n * Unlike `error`, this value is **not** cleared when the auth state\n * transitions (e.g. from `authenticated` → `unauthenticated`).\n * It persists until `fetchUser` succeeds again or `clearError()` is called.\n *\n * Useful for distinguishing \"not logged in\" from \"account disabled\" in\n * AuthGuard or error-boundary components.\n */\n lastError: Error | undefined;\n /**\n * Manually reset `lastError` to `undefined`.\n */\n clearError: () => void;\n /**\n * Manually update cached user data.\n * Call with `null` to clear, or with a user object to update.\n */\n mutate: (data?: T | null) => Promise<void>;\n /**\n * Why the most recent `user` transition happened.\n *\n * - `null` — No user-change has been observed yet in this component\n * (e.g. before the first `fetchUser` resolves on mount).\n * - `'initial'` — First time the Provider observed a user value\n * (including `null` for unauthenticated cold start).\n * - `'login'` — Triggered by an explicit `login()` / multi-step finalize\n * / `injectAuth()` call.\n * - `'logout'` — Triggered by an explicit `logout()` / `injectLogout()`.\n * - `'revalidate'` — SWR background revalidation produced a different user.\n * - `'external'` — Cross-tab sync via BroadcastChannel / storage events.\n *\n * Use this to differentiate user-initiated transitions from passive ones,\n * e.g. to suppress a welcome toast on page refresh.\n */\n lastChangeSource: UserChangeSource | null;\n /**\n * Full event object for the most recent `user` transition, including\n * `previousUser` and `timestamp`. `null` before the first transition.\n *\n * See `lastChangeSource` for a quick discriminator.\n */\n lastChangeEvent: UserChangeEvent<T> | null;\n}\n\n/**\n * Hook to access current user data via SWR cache.\n *\n * Uses SWR's stale-while-revalidate strategy:\n * - Immediately returns cached user data\n * - Revalidates in the background using the configured fetchUser function\n * - Automatically syncs across all components using this hook\n *\n * @example\n * ```tsx\n * const { user, isLoading, isAuthenticated } = useUser<MyUser>();\n *\n * if (isLoading) return <Spinner />;\n * if (!isAuthenticated) return <LoginPage />;\n * return <Dashboard user={user} />;\n * ```\n *\n * @example Distinguish user-change sources\n * ```tsx\n * const { user, lastChangeSource } = useUser();\n *\n * useEffect(() => {\n * // Only auto-redirect when we detect an *existing* session on mount,\n * // not when the user just pressed the \"Login\" button (the login form\n * // is responsible for its own redirect there).\n * if (user && lastChangeSource === 'initial') {\n * showRedirectOverlay();\n * }\n * }, [user, lastChangeSource]);\n * ```\n */\nexport function useUser<T extends User = User>(): UseUserReturn<T> {\n const { tokenManager, stateMachine, config, emitter, userChangeHint } = useAuthContext();\n\n // ── lastError 状态管理 ──────────────────────────────────────\n const lastErrorRef = useRef<Error | undefined>(undefined);\n const retryCountRef = useRef(0);\n const [, setTick] = useState(0);\n const forceUpdate = useCallback(() => setTick((t) => t + 1), []);\n\n const clearError = useCallback(() => {\n lastErrorRef.current = undefined;\n forceUpdate();\n }, [forceUpdate]);\n\n // ── user-change 事件:previousUser 跟踪 + lastChangeSource 状态 ──\n // Use a sentinel (`undefined`) to mean \"never observed a value yet\",\n // separate from `null` which means \"observed and was unauthenticated\".\n const previousUserRef = useRef<T | null | undefined>(undefined);\n const [lastChangeEvent, setLastChangeEvent] = useState<UserChangeEvent<T> | null>(null);\n\n const fetcher = async (): Promise<T | null> => {\n const token = tokenManager.getAccessToken();\n if (!token) return null;\n\n if (tokenManager.isExpired()) {\n try {\n await tokenManager.refresh();\n } catch {\n // refresh 失败,但 token 可能仍然有效(如 cookie-based 场景)\n // 如果 token 已被清除则放弃,否则继续尝试 fetchUser 让服务端验证\n const remainingToken = tokenManager.getAccessToken();\n if (!remainingToken) return null;\n }\n }\n\n if (config.fetchUser) {\n const currentToken = tokenManager.getAccessToken();\n if (!currentToken) return null;\n return (await config.fetchUser(currentToken)) as T;\n }\n\n return null;\n };\n\n const {\n data,\n error,\n isLoading,\n mutate: swrMutate,\n } = useSWR<T | null>(AUTH_KEY, fetcher, {\n revalidateOnFocus: config.swrOptions?.revalidateOnFocus ?? true,\n revalidateOnReconnect: config.swrOptions?.revalidateOnReconnect ?? true,\n shouldRetryOnError: false,\n ...(config.swrOptions?.dedupingInterval !== undefined && {\n dedupingInterval: config.swrOptions.dedupingInterval,\n }),\n ...(config.swrOptions?.focusThrottleInterval !== undefined && {\n focusThrottleInterval: config.swrOptions.focusThrottleInterval,\n }),\n ...(config.swrOptions?.refreshInterval !== undefined && {\n refreshInterval: config.swrOptions.refreshInterval,\n }),\n });\n\n // ── user-change 派发(每当 SWR 的 data 稳定为 user/null 时触发一次)──\n useEffect(() => {\n // SWR 尚未完成首次解析:data === undefined → 跳过\n if (data === undefined) return;\n\n const prev = previousUserRef.current;\n // 值未变化(含对象引用 + null → null)→ 不派发\n if (Object.is(prev, data)) return;\n\n // 选择 source:\n // 1) 首次(prev === undefined)→ 'initial'\n // 2) Provider 近期标记了 hint(TTL 内)→ 使用 hint.source\n // 3) 其余一律视为被动 revalidate\n let source: UserChangeSource;\n if (prev === undefined) {\n source = 'initial';\n } else {\n const now = Date.now();\n const hintFresh =\n userChangeHint.source !== null && now - userChangeHint.timestamp <= USER_CHANGE_HINT_TTL_MS;\n source = hintFresh ? (userChangeHint.source as UserChangeSource) : 'revalidate';\n }\n\n // NOTE: We intentionally do NOT clear the hint here. Multiple useUser()\n // consumers may each observe the same SWR data change within the same\n // tick; all of them should see the same source. The hint will naturally\n // expire via TTL before the next unrelated SWR revalidate arrives.\n\n const event: UserChangeEvent<T> = {\n source,\n user: data,\n previousUser: prev,\n timestamp: Date.now(),\n };\n\n previousUserRef.current = data;\n setLastChangeEvent(event);\n emitter.emit('user-change', event as UserChangeEvent);\n }, [data, emitter, userChangeHint]);\n\n // ── 同步 lastError + onFetchUserError 回调 ─────────────────\n useEffect(() => {\n if (error) {\n lastErrorRef.current = error;\n forceUpdate();\n\n // 调用 onFetchUserError 回调(如果已配置)\n if (config.onFetchUserError) {\n const action = config.onFetchUserError(error);\n\n if (action === 'retry' && retryCountRef.current < 1) {\n retryCountRef.current += 1;\n // 触发 SWR 重新请求\n swrMutate();\n } else if (action === 'logout') {\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n }\n // 'ignore' → 仅保留 lastError,不做额外操作\n }\n } else if (data !== undefined && !error) {\n // fetchUser 成功(data 可以是 null 或 user),重置 lastError 和重试计数\n if (lastErrorRef.current !== undefined) {\n lastErrorRef.current = undefined;\n forceUpdate();\n }\n retryCountRef.current = 0;\n }\n }, [error, data, forceUpdate, config, tokenManager, stateMachine, swrMutate]);\n\n const mutate = async (newData?: T | null): Promise<void> => {\n if (newData === undefined) {\n await swrMutate();\n } else {\n await swrMutate(newData, { revalidate: false });\n }\n };\n\n return {\n user: data ?? null,\n isLoading,\n isAuthenticated: !!data,\n error,\n lastError: lastErrorRef.current,\n clearError,\n mutate,\n lastChangeSource: lastChangeEvent?.source ?? null,\n lastChangeEvent,\n };\n}\n\n/** SWR cache key used for user data. Exported for advanced usage. */\nexport { AUTH_KEY };\n","import type { AuthResponse } from '@swr-login/core';\nimport { isMultiStepPlugin } from '@swr-login/core';\nimport { useCallback, useState } from 'react';\nimport { mutate as swrGlobalMutate } from 'swr';\nimport { useAuthContext } from '../context';\nimport { AUTH_KEY } from './useUser';\n\nexport interface UseLoginOptions {\n /** Plugin name to use for login */\n pluginName?: string;\n}\n\nexport interface UseLoginReturn<TCredentials = unknown> {\n /**\n * Trigger login with specified credentials.\n * If pluginName was not provided in options, it must be passed as first argument.\n */\n login: (\n credentialsOrPluginName: TCredentials | string,\n credentials?: TCredentials,\n ) => Promise<AuthResponse>;\n /** Whether a login request is in progress */\n isLoading: boolean;\n /** Last login error, if any */\n error: Error | null;\n /** Reset error state */\n reset: () => void;\n}\n\n/**\n * Hook to trigger login flow via a registered plugin.\n *\n * @param pluginName - Optional default plugin name\n *\n * @example\n * ```tsx\n * // With default plugin\n * const { login, isLoading, error } = useLogin('password');\n * await login({ username: 'alice', password: 'secret' });\n *\n * // Without default plugin (specify at call time)\n * const { login } = useLogin();\n * await login('oauth-google', { redirect: false });\n * ```\n */\nexport function useLogin<TCredentials = unknown>(\n pluginName?: string,\n): UseLoginReturn<TCredentials> {\n const { pluginManager, tokenManager, stateMachine, config } = useAuthContext();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const login = useCallback(\n async (\n credentialsOrPluginName: TCredentials | string,\n maybeCredentials?: TCredentials,\n ): Promise<AuthResponse> => {\n let resolvedPlugin: string;\n let resolvedCredentials: TCredentials;\n\n if (typeof credentialsOrPluginName === 'string' && !pluginName) {\n resolvedPlugin = credentialsOrPluginName;\n resolvedCredentials = (maybeCredentials ?? {}) as TCredentials;\n } else if (pluginName) {\n resolvedPlugin = pluginName;\n resolvedCredentials = credentialsOrPluginName as TCredentials;\n } else {\n throw new Error(\n '[swr-login] Plugin name is required. Provide it to useLogin() or login().',\n );\n }\n\n setIsLoading(true);\n setError(null);\n stateMachine.transition('authenticating');\n\n // 检查是否为多步骤插件,给出友好错误提示\n const targetPlugin = pluginManager.getPlugin(resolvedPlugin);\n if (targetPlugin && isMultiStepPlugin(targetPlugin)) {\n const err = new Error(\n `[swr-login] Plugin \"${resolvedPlugin}\" is a multi-step plugin. Use useMultiStepLogin() instead of useLogin() for multi-step login flows.`,\n );\n setError(err);\n stateMachine.transition('error');\n setIsLoading(false);\n throw err;\n }\n\n try {\n const response = await pluginManager.login(resolvedPlugin, resolvedCredentials);\n\n // ── afterAuth:在 plugin 成功后、fetchUser 之前执行自定义钩子 ──\n let shouldSkipFetchUser = false;\n if (config.afterAuth) {\n try {\n await config.afterAuth({\n pluginName: resolvedPlugin,\n authResponse: response,\n skipFetchUser: () => {\n shouldSkipFetchUser = true;\n },\n });\n } catch (afterAuthErr) {\n // afterAuth 抛错:回滚 token,login() reject\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n throw afterAuthErr;\n }\n }\n\n // ── validateUserOnLogin:在 plugin 成功后调用 fetchUser 验证用户状态 ──\n if (!shouldSkipFetchUser && config.fetchUser && config.validateUserOnLogin !== false) {\n try {\n const user = await config.fetchUser(response.accessToken);\n // 将 fetchUser 返回的用户写入 SWR 缓存,避免 useUser 重复请求\n await swrGlobalMutate(AUTH_KEY, user, { revalidate: false });\n } catch (fetchUserErr) {\n // fetchUser 失败:回滚 token,转为 unauthenticated\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n throw fetchUserErr;\n }\n }\n\n stateMachine.transition('authenticated');\n\n // Update cache adapter if available\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n\n return response;\n } catch (err) {\n const authError = err instanceof Error ? err : new Error('Login failed');\n setError(authError);\n stateMachine.transition('error');\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, tokenManager, stateMachine, config, pluginName],\n );\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return { login, isLoading, error, reset };\n}\n","import type { AuthResponse } from '@swr-login/core';\nimport { useCallback, useRef, useState } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface UseMultiStepLoginReturn<TCredentials = unknown> {\n /** 当前步骤索引(从 0 开始,-1 表示未开始) */\n currentStep: number;\n /** 当前步骤名称(未开始时为 null) */\n currentStepName: string | null;\n /** 总步骤数 */\n totalSteps: number;\n /** 启动流程(执行第一步) */\n start: (credentials: TCredentials) => Promise<void>;\n /** 继续下一步 */\n next: (input: unknown) => Promise<void>;\n /** 回到上一步(仅 UI 层回退,不重新执行步骤逻辑) */\n back: () => void;\n /** 取消整个流程,重置状态 */\n cancel: () => void;\n /** 当前步骤的输出数据(传给 UI 渲染) */\n stepData: unknown;\n /** 是否正在执行当前步骤 */\n isLoading: boolean;\n /** 当前步骤的错误 */\n error: Error | null;\n /** 重置错误状态 */\n reset: () => void;\n /** 流程是否已完成 */\n isComplete: boolean;\n /** 最终的 AuthResponse(流程完成后可用) */\n authResponse: AuthResponse | null;\n}\n\n/**\n * Hook 用于驱动多步骤登录插件的流程。\n *\n * 提供步骤状态管理、前进/后退/取消等操作,自动在最后一步完成后\n * 调用 finalizeAuth 并更新全局登录态。\n *\n * @param pluginName - 多步骤插件名称\n *\n * @example\n * ```tsx\n * function ClassCodeLoginFlow() {\n * const {\n * currentStepName, stepData, start, next, back, isLoading, error, isComplete,\n * } = useMultiStepLogin<{ classCode: string; loginCode: string }>('class-code');\n *\n * if (isComplete) {\n * return <Redirect to=\"/dashboard\" />;\n * }\n *\n * // 步骤 1:输入班级码\n * if (currentStepName === 'verify-code' || currentStep === -1) {\n * return <ClassCodeForm onSubmit={start} isLoading={isLoading} error={error} />;\n * }\n *\n * // 步骤 2:选择学生\n * if (currentStepName === 'select-student') {\n * return (\n * <StudentList\n * students={stepData.students}\n * onSelect={(userId) => next({ userId, classLoginToken: stepData.classLoginToken })}\n * onBack={back}\n * isLoading={isLoading}\n * />\n * );\n * }\n *\n * // 步骤 3:获取 Token(自动执行,显示 loading)\n * if (currentStepName === 'get-token') {\n * return <LoadingSpinner text=\"登录中...\" />;\n * }\n * }\n * ```\n */\nexport function useMultiStepLogin<TCredentials = unknown>(\n pluginName: string,\n): UseMultiStepLoginReturn<TCredentials> {\n const { pluginManager, stateMachine, config } = useAuthContext();\n\n const [currentStep, setCurrentStep] = useState(-1); // -1 表示未开始\n const [stepData, setStepData] = useState<unknown>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [isComplete, setIsComplete] = useState(false);\n const [authResponse, setAuthResponse] = useState<AuthResponse | null>(null);\n\n // 用 ref 保存中间步骤的累积数据,避免闭包问题\n const stepOutputsRef = useRef<unknown[]>([]);\n\n const steps = pluginManager.getSteps(pluginName);\n const totalSteps = steps?.length ?? 0;\n\n const currentStepName = currentStep >= 0 && steps ? (steps[currentStep]?.name ?? null) : null;\n\n const executeStepAt = useCallback(\n async (stepIndex: number, input: unknown) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const output = await pluginManager.executeStep(pluginName, stepIndex, input);\n stepOutputsRef.current[stepIndex] = output;\n setStepData(output);\n setCurrentStep(stepIndex);\n\n // 如果是最后一步,执行 finalizeAuth\n if (stepIndex === totalSteps - 1) {\n const response = await pluginManager.finalizeMultiStep(pluginName, output);\n stateMachine.transition('authenticated');\n\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n\n setAuthResponse(response);\n setIsComplete(true);\n }\n } catch (err) {\n const authError = err instanceof Error ? err : new Error('Step execution failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, pluginName, totalSteps, stateMachine, config],\n );\n\n const start = useCallback(\n async (credentials: TCredentials) => {\n // 重置状态\n stepOutputsRef.current = [];\n setCurrentStep(-1);\n setStepData(null);\n setIsComplete(false);\n setAuthResponse(null);\n setError(null);\n stateMachine.transition('authenticating');\n\n await executeStepAt(0, credentials);\n },\n [executeStepAt, stateMachine],\n );\n\n const next = useCallback(\n async (input: unknown) => {\n const nextIndex = currentStep + 1;\n if (nextIndex >= totalSteps) {\n throw new Error('[swr-login] No more steps to execute.');\n }\n await executeStepAt(nextIndex, input);\n },\n [currentStep, totalSteps, executeStepAt],\n );\n\n const back = useCallback(() => {\n if (currentStep > 0) {\n const prevStep = currentStep - 1;\n setCurrentStep(prevStep);\n setStepData(stepOutputsRef.current[prevStep] ?? null);\n setError(null);\n }\n }, [currentStep]);\n\n const cancel = useCallback(() => {\n setCurrentStep(-1);\n setStepData(null);\n setIsLoading(false);\n setError(null);\n setIsComplete(false);\n setAuthResponse(null);\n stepOutputsRef.current = [];\n stateMachine.transition('unauthenticated');\n }, [stateMachine]);\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return {\n currentStep,\n currentStepName,\n totalSteps,\n start,\n next,\n back,\n cancel,\n stepData,\n isLoading,\n error,\n reset,\n isComplete,\n authResponse,\n };\n}\n","import type { AuthInjector, AuthResponse } from '@swr-login/core';\nimport { useCallback } from 'react';\nimport { useAuthContext } from '../context';\n\n/**\n * Hook 用于从外部注入登录态到 swr-login 体系。\n *\n * 适用场景:\n * - 多步骤登录流程(如班级码登录、MFA)\n * - 第三方 SDK 登录完成后同步状态\n * - iframe / WebView 登录回调\n *\n * @example\n * ```tsx\n * function ExternalLoginCallback() {\n * const { injectAuth } = useAuthInjector();\n *\n * const handleLoginComplete = async (token: string, user: User) => {\n * await injectAuth({\n * user,\n * accessToken: token,\n * expiresAt: Date.now() + 86400000,\n * });\n * router.push('/dashboard');\n * };\n * }\n * ```\n */\nexport function useAuthInjector(): AuthInjector {\n const { tokenManager, emitter, stateMachine, config } = useAuthContext();\n\n const injectAuth = useCallback(\n async (response: AuthResponse): Promise<void> => {\n // 1. 存储 token\n tokenManager.setTokens({\n accessToken: response.accessToken,\n refreshToken: response.refreshToken,\n expiresAt: response.expiresAt,\n });\n\n // 2. 状态机转换为已认证。\n //\n // injectAuth 作为\"逃生舱\"应能从任何状态恢复到 authenticated,\n // 但状态机不允许 `error -> authenticated` / `unauthenticated -> authenticated` 直接转换。\n // 这两种状态都允许 `-> authenticating`,而 `authenticating -> authenticated` 合法,\n // 因此通过 `authenticating` 中转一次即可完成恢复,且每一步都符合状态机契约。\n const currentState = stateMachine.state;\n if (currentState === 'error' || currentState === 'unauthenticated') {\n stateMachine.transition('authenticating');\n }\n stateMachine.transition('authenticated');\n\n // 3. 发射登录事件(触发 onLogin 回调、跨标签页同步等)\n emitter.emit('login', { user: response.user });\n\n // 4. 更新缓存(使 useUser() 等 Hook 感知到用户信息)\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n },\n [tokenManager, emitter, stateMachine, config],\n );\n\n const injectLogout = useCallback(async (): Promise<void> => {\n // 1. 清除 token\n tokenManager.clearTokens();\n\n // 2. 状态机转换为未认证\n stateMachine.transition('unauthenticated');\n\n // 3. 发射登出事件\n emitter.emit('logout', undefined);\n\n // 4. 清除缓存\n if (config.cacheAdapter) {\n await config.cacheAdapter.clearUser();\n }\n }, [tokenManager, emitter, stateMachine, config]);\n\n return { injectAuth, injectLogout };\n}\n","import type { User, UserChangeEvent, UserChangeSource } from '@swr-login/core';\nimport { useEffect, useRef, useState } from 'react';\nimport { useAuthContext } from '../context';\n\n/**\n * Subscribe to `user-change` events as a **discrete event stream**.\n *\n * Unlike `useUser().user` (which reflects the *current* user value), this\n * hook returns the most recent transition event and re-renders the caller\n * only when a new transition occurs. It's intended for `useEffect`-driven\n * side effects that care about *when* and *why* the user changed rather\n * than the current user value itself.\n *\n * Returns `null` until the first transition is observed by the Provider.\n *\n * @typeParam T - Concrete user type (defaults to base `User`)\n *\n * @example\n * ```tsx\n * const change = useUserChange<MyUser>();\n *\n * useEffect(() => {\n * if (change?.source === 'login') {\n * toast.success(`Welcome back, ${change.user?.name}!`);\n * }\n * }, [change]);\n * ```\n *\n * @example Filter by source\n * ```tsx\n * const change = useUserChange();\n * useEffect(() => {\n * if (change?.source === 'external') {\n * // Another tab just logged in / out — refresh local UI\n * refreshSidebar();\n * }\n * }, [change]);\n * ```\n */\nexport function useUserChange<T extends User = User>(): UserChangeEvent<T> | null {\n const { emitter } = useAuthContext();\n const [event, setEvent] = useState<UserChangeEvent<T> | null>(null);\n\n useEffect(() => {\n const unsubscribe = emitter.on('user-change', (payload) => {\n setEvent(payload as UserChangeEvent<T>);\n });\n return unsubscribe;\n }, [emitter]);\n\n return event;\n}\n\n/**\n * Register a side-effect callback that fires on every `user-change` event,\n * **without** triggering a re-render of the calling component.\n *\n * Ideal for analytics / logging / imperative side effects that don't need\n * to participate in React's render cycle.\n *\n * The callback receives the full `UserChangeEvent` (including `previousUser`,\n * `timestamp`, and `source`). The listener is automatically unsubscribed on\n * unmount.\n *\n * Note: the callback reference is stored in a ref and always called with\n * the latest closure — you do NOT need to memoize it with `useCallback`.\n *\n * @example Analytics\n * ```tsx\n * useUserChangeEffect((e) => {\n * if (e.source === 'login') {\n * analytics.track('user_login', { userId: e.user?.id });\n * }\n * if (e.source === 'logout') {\n * analytics.track('user_logout', { userId: e.previousUser?.id });\n * }\n * });\n * ```\n *\n * @example Filter sources\n * ```tsx\n * useUserChangeEffect((e) => {\n * // Only react to passive revalidations that flipped the user to null\n * // (e.g. server returned 401 → session expired silently).\n * if (e.source === 'revalidate' && e.user === null && e.previousUser) {\n * showReLoginPrompt();\n * }\n * });\n * ```\n */\nexport function useUserChangeEffect<T extends User = User>(\n listener: (event: UserChangeEvent<T>) => void,\n): void {\n const { emitter } = useAuthContext();\n\n // Store latest callback in a ref so consumers don't need useCallback.\n const listenerRef = useRef(listener);\n listenerRef.current = listener;\n\n useEffect(() => {\n const unsubscribe = emitter.on('user-change', (payload) => {\n try {\n listenerRef.current(payload as UserChangeEvent<T>);\n } catch (err) {\n console.error('[swr-login] Error in useUserChangeEffect listener:', err);\n }\n });\n return unsubscribe;\n }, [emitter]);\n}\n\n/**\n * Convenience hook: fires the callback only when the change source matches\n * the given filter. Thin wrapper around {@link useUserChangeEffect}.\n *\n * @example\n * ```tsx\n * // Only run on explicit login (not on cold-start initial load)\n * useUserChangeOn('login', (e) => router.push('/dashboard'));\n *\n * // Subscribe to multiple sources at once\n * useUserChangeOn(['login', 'external'], (e) => refreshSidebar());\n * ```\n */\nexport function useUserChangeOn<T extends User = User>(\n source: UserChangeSource | UserChangeSource[],\n listener: (event: UserChangeEvent<T>) => void,\n): void {\n const sources = Array.isArray(source) ? source : [source];\n useUserChangeEffect<T>((event) => {\n if (sources.includes(event.source)) {\n listener(event);\n }\n });\n}\n","import { useCallback, useState } from 'react';\nimport { mutate } from 'swr';\nimport { useAuthContext } from '../context';\nimport { AUTH_KEY } from './useUser';\n\nexport interface UseLogoutOptions {\n /** Specific plugin to call logout on */\n pluginName?: string;\n /** Whether to broadcast logout to other tabs (default: true) */\n broadcast?: boolean;\n}\n\nexport interface UseLogoutReturn {\n /** Execute logout */\n logout: (options?: UseLogoutOptions) => Promise<void>;\n /** Whether logout is in progress */\n isLoading: boolean;\n}\n\n/**\n * Hook to perform secure logout with cross-tab sync.\n *\n * Logout flow:\n * 1. Call plugin logout (if applicable)\n * 2. Clear stored tokens\n * 3. Clear SWR cache (mutate user to null)\n * 4. Broadcast logout to other tabs\n * 5. Transition state to 'unauthenticated'\n *\n * @example\n * ```tsx\n * const { logout, isLoading } = useLogout();\n *\n * <button onClick={() => logout()} disabled={isLoading}>\n * Sign Out\n * </button>\n * ```\n */\nexport function useLogout(): UseLogoutReturn {\n const { pluginManager, stateMachine, broadcastSync } = useAuthContext();\n const [isLoading, setIsLoading] = useState(false);\n\n const logout = useCallback(\n async (options?: UseLogoutOptions) => {\n setIsLoading(true);\n\n try {\n // Call plugin logout\n await pluginManager.logout(options?.pluginName);\n\n // Clear SWR cache\n await mutate(AUTH_KEY, null, { revalidate: false });\n\n // Update state\n stateMachine.transition('unauthenticated');\n\n // Broadcast to other tabs\n const shouldBroadcast = options?.broadcast !== false;\n if (shouldBroadcast && broadcastSync) {\n broadcastSync.send('LOGOUT');\n }\n } catch (err) {\n console.error('[swr-login] Logout error:', err);\n // Even if logout API fails, clear local state\n await mutate(AUTH_KEY, null, { revalidate: false });\n stateMachine.transition('unauthenticated');\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, stateMachine, broadcastSync],\n );\n\n return { logout, isLoading };\n}\n","import type { TokenAdapter } from '@swr-login/core';\nimport { useCallback } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface UseAdapterReturn {\n /**\n * Synchronously check whether the adapter currently holds a valid auth credential.\n *\n * Uses `adapter.hasAuth()` if the adapter implements it,\n * otherwise falls back to `adapter.getAccessToken() !== null`.\n *\n * This is a **synchronous, non-reactive** check — it reads the adapter's\n * current state at call time. It does NOT trigger re-renders when the\n * underlying token changes. For reactive auth state, use `useUser()`.\n *\n * Typical use cases:\n * - Conditional rendering before SWR hydration completes\n * - Route guard pre-checks\n * - Analytics / logging\n *\n * @example\n * ```tsx\n * const { hasAuth } = useAdapter();\n *\n * // 首页自动跳转\n * useEffect(() => {\n * if (hasAuth()) {\n * router.push('/dashboard');\n * }\n * }, []);\n * ```\n */\n hasAuth: () => boolean;\n\n /**\n * Direct reference to the underlying `TokenAdapter` instance.\n *\n * Escape hatch for advanced scenarios where you need low-level\n * access to token storage (e.g. reading refresh token, manually\n * clearing tokens outside of the normal logout flow).\n *\n * Prefer `hasAuth()` and the higher-level hooks (`useUser`, `useLogin`)\n * for most use cases.\n */\n adapter: TokenAdapter;\n}\n\n/**\n * Hook to access the token adapter and its utility methods.\n *\n * Provides a convenient `hasAuth()` check that works synchronously,\n * useful for UI decisions that need to happen before SWR data is available.\n *\n * @example\n * ```tsx\n * function HomePage() {\n * const { hasAuth } = useAdapter();\n * const router = useRouter();\n *\n * useEffect(() => {\n * if (hasAuth()) {\n * router.push('/dashboard');\n * }\n * }, []);\n *\n * return <LoginPage />;\n * }\n * ```\n */\nexport function useAdapter(): UseAdapterReturn {\n const { config } = useAuthContext();\n const adapter = config.adapter;\n\n const hasAuth = useCallback((): boolean => {\n if (typeof adapter.hasAuth === 'function') {\n return adapter.hasAuth();\n }\n // Fallback: 检查 accessToken 是否存在\n return adapter.getAccessToken() !== null;\n }, [adapter]);\n\n return { hasAuth, adapter };\n}\n","import { isTokenExpired } from '@swr-login/core';\nimport { useMemo } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface SessionInfo {\n /** Current access token */\n accessToken: string | null;\n /** Current refresh token */\n refreshToken: string | null;\n /** Token expiration timestamp (ms since epoch) */\n expiresAt: number | null;\n /** Whether the access token has expired */\n isExpired: boolean;\n /** Whether tokens exist (regardless of expiry) */\n hasTokens: boolean;\n}\n\n/**\n * Hook to access raw session/token information.\n *\n * Useful for:\n * - Adding Authorization headers to custom requests\n * - Checking token expiry status\n * - Debugging session state\n *\n * @example\n * ```tsx\n * const { accessToken, isExpired } = useSession();\n *\n * const fetchData = () => fetch('/api/data', {\n * headers: { Authorization: `Bearer ${accessToken}` },\n * });\n * ```\n */\nexport function useSession(): SessionInfo {\n const { tokenManager } = useAuthContext();\n\n return useMemo(() => {\n const accessToken = tokenManager.getAccessToken();\n const refreshToken = tokenManager.getRefreshToken();\n const expiresAt = tokenManager.getAccessToken() ? tokenManager.getExpiresAt() : null;\n\n return {\n accessToken,\n refreshToken,\n expiresAt,\n isExpired: isTokenExpired(expiresAt),\n hasTokens: !!accessToken,\n };\n }, [tokenManager]);\n}\n","import { useCallback } from 'react';\nimport { useUser } from './useUser';\n\nexport interface UsePermissionReturn {\n /** Check if user has a specific permission */\n hasPermission: (permission: string) => boolean;\n /** Check if user has a specific role */\n hasRole: (role: string) => boolean;\n /** Check if user has all specified permissions */\n hasAllPermissions: (permissions: string[]) => boolean;\n /** Check if user has any of the specified permissions */\n hasAnyPermission: (permissions: string[]) => boolean;\n /** Check if user has all specified roles */\n hasAllRoles: (roles: string[]) => boolean;\n /** Check if user has any of the specified roles */\n hasAnyRole: (roles: string[]) => boolean;\n}\n\n/**\n * Hook for checking user permissions and roles.\n *\n * Reads from the `permissions` and `roles` arrays on the User object.\n *\n * @example\n * ```tsx\n * const { hasPermission, hasRole } = usePermission();\n *\n * if (hasPermission('admin:write')) {\n * // Show admin controls\n * }\n *\n * if (hasRole('editor')) {\n * // Show editor tools\n * }\n * ```\n */\nexport function usePermission(): UsePermissionReturn {\n const { user } = useUser();\n\n const hasPermission = useCallback(\n (permission: string): boolean => {\n return user?.permissions?.includes(permission) ?? false;\n },\n [user],\n );\n\n const hasRole = useCallback(\n (role: string): boolean => {\n return user?.roles?.includes(role) ?? false;\n },\n [user],\n );\n\n const hasAllPermissions = useCallback(\n (permissions: string[]): boolean => {\n if (!user?.permissions) return false;\n return permissions.every((p) => user.permissions?.includes(p));\n },\n [user],\n );\n\n const hasAnyPermission = useCallback(\n (permissions: string[]): boolean => {\n if (!user?.permissions) return false;\n return permissions.some((p) => user.permissions?.includes(p));\n },\n [user],\n );\n\n const hasAllRoles = useCallback(\n (roles: string[]): boolean => {\n if (!user?.roles) return false;\n return roles.every((r) => user.roles?.includes(r));\n },\n [user],\n );\n\n const hasAnyRole = useCallback(\n (roles: string[]): boolean => {\n if (!user?.roles) return false;\n return roles.some((r) => user.roles?.includes(r));\n },\n [user],\n );\n\n return {\n hasPermission,\n hasRole,\n hasAllPermissions,\n hasAnyPermission,\n hasAllRoles,\n hasAnyRole,\n };\n}\n","import type React from 'react';\nimport { usePermission } from '../hooks/usePermission';\nimport { useUser } from '../hooks/useUser';\n\nexport interface AuthGuardProps {\n children: React.ReactNode;\n /** Required permissions (checked against user.permissions) */\n permissions?: string[];\n /** Required roles (checked against user.roles) */\n roles?: string[];\n /** If true, ALL permissions/roles must match. If false, ANY match suffices. (default: false) */\n requireAll?: boolean;\n /** Component to render when user is not authenticated or lacks permissions */\n fallback?: React.ReactNode;\n /** Component to render while checking auth status */\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Declarative auth guard component.\n * Protects child content based on authentication state and permissions/roles.\n *\n * @example\n * ```tsx\n * <AuthGuard\n * permissions={['admin', 'editor']}\n * requireAll={false}\n * fallback={<Navigate to=\"/login\" />}\n * loadingComponent={<Skeleton />}\n * >\n * <AdminPanel />\n * </AuthGuard>\n * ```\n */\nexport function AuthGuard({\n children,\n permissions,\n roles,\n requireAll = false,\n fallback = null,\n loadingComponent = null,\n}: AuthGuardProps) {\n const { isLoading, isAuthenticated } = useUser();\n const { hasAllPermissions, hasAnyPermission, hasAllRoles, hasAnyRole } = usePermission();\n\n if (isLoading) {\n return <>{loadingComponent}</>;\n }\n\n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n const hasRequiredPermissions = requireAll\n ? hasAllPermissions(permissions)\n : hasAnyPermission(permissions);\n\n if (!hasRequiredPermissions) {\n return <>{fallback}</>;\n }\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n const hasRequiredRoles = requireAll ? hasAllRoles(roles) : hasAnyRole(roles);\n\n if (!hasRequiredRoles) {\n return <>{fallback}</>;\n }\n }\n\n return <>{children}</>;\n}\n"]}
1
+ {"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/hooks/useUser.ts","../src/hooks/useLogin.ts","../src/hooks/useMultiStepLogin.ts","../src/hooks/useAuthInjector.ts","../src/hooks/useUserChange.ts","../src/hooks/useLogout.ts","../src/hooks/useAdapter.ts","../src/hooks/useSession.ts","../src/hooks/usePermission.ts","../src/components/AuthGuard.tsx"],"names":["createContext","useContext","useRef","useMemo","AuthEventEmitter","AuthStateMachine","TokenManager","PluginManager","BroadcastSync","useEffect","useState","useCallback","useSWR","mutate","isMultiStepPlugin","swrGlobalMutate","isTokenExpired","jsx","Fragment"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n AuthEventEmitter,\n AuthStateMachine,\n BroadcastSync,\n PluginManager,\n SWRLoginConfig,\n TokenManager,\n UserChangeSource,\n} from '@swr-login/core';\nimport { createContext, useContext } from 'react';\n\n/**\n * Mutable hint object shared between the Provider and `useUser()` to describe\n * *why* the next user-change event is expected to happen.\n *\n * The Provider writes into this object as it observes `login` / `logout` /\n * `external` events; `useUser()` reads and clears it when the SWR cache value\n * actually transitions. Unset/expired hint → the change is a passive\n * `revalidate` or the very first `initial` load.\n *\n * Using a mutable ref-like object (instead of React state) avoids re-renders\n * and is safe because it is written synchronously from event callbacks and\n * read synchronously during `useUser()`'s `useEffect`.\n *\n * @internal\n */\nexport interface UserChangeHint {\n source: UserChangeSource | null;\n /** `Date.now()` when the hint was written. Stale hints (>1s) are ignored. */\n timestamp: number;\n}\n\n/** Internal context value passed through SWRLoginProvider */\nexport interface AuthContextValue {\n pluginManager: PluginManager;\n tokenManager: TokenManager;\n emitter: AuthEventEmitter;\n stateMachine: AuthStateMachine;\n broadcastSync: BroadcastSync | null;\n config: SWRLoginConfig;\n /** @internal shared hint for the next user-change source */\n userChangeHint: UserChangeHint;\n}\n\nexport const AuthContext = createContext<AuthContextValue | null>(null);\n\n/**\n * Internal hook to access auth context.\n * Must be used within SWRLoginProvider.\n * @throws Error if used outside of SWRLoginProvider\n */\nexport function useAuthContext(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) {\n throw new Error(\n '[swr-login] useAuthContext must be used within <SWRLoginProvider>. ' +\n 'Wrap your app with <SWRLoginProvider> to use swr-login hooks.',\n );\n }\n return ctx;\n}\n","import {\n AuthEventEmitter,\n AuthStateMachine,\n BroadcastSync,\n PluginManager,\n type SWRLoginConfig,\n TokenManager,\n} from '@swr-login/core';\nimport type React from 'react';\nimport { useEffect, useMemo, useRef } from 'react';\nimport { AuthContext, type AuthContextValue, type UserChangeHint } from './context';\n\nexport interface SWRLoginProviderProps {\n /** Authentication configuration */\n config: SWRLoginConfig;\n children: React.ReactNode;\n}\n\n/**\n * SWRLoginProvider initializes the auth system and provides context to all child hooks.\n *\n * @example\n * ```tsx\n * import { SWRLoginProvider } from '@swr-login/react';\n * import { JWTAdapter } from '@swr-login/adapter-jwt';\n * import { PasswordPlugin } from '@swr-login/plugin-password';\n *\n * function App() {\n * return (\n * <SWRLoginProvider\n * config={{\n * adapter: JWTAdapter(),\n * plugins: [PasswordPlugin({ loginUrl: '/api/login' })],\n * }}\n * >\n * <YourApp />\n * </SWRLoginProvider>\n * );\n * }\n * ```\n */\nexport function SWRLoginProvider({ config, children }: SWRLoginProviderProps) {\n const initializedRef = useRef(false);\n\n const contextValue = useMemo<AuthContextValue>(() => {\n const emitter = new AuthEventEmitter();\n const stateMachine = new AuthStateMachine(emitter);\n const tokenManager = new TokenManager(config.adapter, emitter, stateMachine);\n const pluginManager = new PluginManager(tokenManager, emitter);\n\n // Register plugins\n pluginManager.register(...config.plugins);\n\n // Set up broadcast sync if enabled\n const enableSync = config.security?.enableBroadcastSync !== false;\n const broadcastSync = enableSync && typeof window !== 'undefined' ? new BroadcastSync() : null;\n\n // Mutable hint object shared with useUser() to label the next user-change\n // event with its causal source (login/logout/external).\n const userChangeHint: UserChangeHint = { source: null, timestamp: 0 };\n const markHint = (source: UserChangeHint['source']) => {\n userChangeHint.source = source;\n userChangeHint.timestamp = Date.now();\n };\n\n // Label user-change source based on lifecycle events. The actual\n // 'user-change' event is emitted by useUser() once SWR produces a\n // different value; these hints only tell us *why* it's about to change.\n emitter.on('login', () => markHint('login'));\n emitter.on('logout', () => markHint('logout'));\n\n // Wire up lifecycle callbacks\n if (config.onLogin) {\n emitter.on('login', ({ user }) => config.onLogin?.(user));\n }\n if (config.onLogout) {\n emitter.on('logout', () => config.onLogout?.());\n }\n if (config.onError) {\n emitter.on('error', ({ error }) => config.onError?.(error));\n }\n\n return {\n pluginManager,\n tokenManager,\n emitter,\n stateMachine,\n broadcastSync,\n config,\n userChangeHint,\n };\n }, [config]);\n\n // Initialize plugins and broadcast sync on mount\n useEffect(() => {\n if (initializedRef.current) return;\n initializedRef.current = true;\n\n const {\n pluginManager,\n broadcastSync,\n tokenManager,\n stateMachine,\n emitter,\n config: cfg,\n } = contextValue;\n\n // Initialize all plugins\n pluginManager.initializeAll().catch((err) => {\n console.error('[swr-login] Plugin initialization error:', err);\n });\n\n // Check if user has existing token -> restore session\n const existingToken = tokenManager.getAccessToken();\n const expiresAt = tokenManager.getExpiresAt();\n\n if (existingToken && !tokenManager.isExpired()) {\n stateMachine.transition('authenticated');\n } else if (existingToken && expiresAt === null) {\n // expiresAt 未知(如外部登录只设置了 token 但未设置过期时间)\n // 乐观地认为已认证,让 fetchUser / SWR revalidate 来验证\n stateMachine.transition('authenticated');\n } else if (existingToken && tokenManager.isExpired()) {\n stateMachine.transition('unauthenticated');\n emitter.emit('token-expired', undefined);\n }\n\n // Listen for cross-tab events\n if (broadcastSync) {\n const { userChangeHint } = contextValue;\n const markExternal = () => {\n userChangeHint.source = 'external';\n userChangeHint.timestamp = Date.now();\n };\n\n const unsubscribe = broadcastSync.onMessage((message) => {\n switch (message.type) {\n case 'LOGOUT':\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n // Emit-after-mark would be overwritten by the synchronous\n // 'logout' handler (which sets hint='logout'); re-mark to\n // 'external' so useUser labels this cross-tab logout correctly.\n markExternal();\n break;\n case 'LOGIN':\n case 'TOKEN_REFRESH':\n // Cross-tab login / refresh → refresh SWR cache; label the\n // upcoming user-change as 'external'.\n markExternal();\n if (cfg.cacheAdapter) {\n cfg.cacheAdapter.revalidate();\n }\n break;\n }\n });\n\n return () => {\n unsubscribe();\n broadcastSync.destroy();\n };\n }\n }, [contextValue]);\n\n // Visibility change handler for security\n useEffect(() => {\n const { config: cfg, tokenManager, stateMachine, emitter } = contextValue;\n\n if (!cfg.security?.clearOnHidden || typeof document === 'undefined') return;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const handleVisibilityChange = () => {\n if (document.hidden) {\n const delay = cfg.security?.clearOnHiddenDelay ?? 300_000;\n timeoutId = setTimeout(() => {\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n }, delay);\n } else {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n if (timeoutId) clearTimeout(timeoutId);\n };\n }, [contextValue]);\n\n return <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>;\n}\n","import type { User, UserChangeEvent, UserChangeSource } from '@swr-login/core';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport useSWR from 'swr';\nimport { useAuthContext } from '../context';\n\nconst AUTH_KEY = '__swr_login_user__';\n\n/**\n * Time window (ms) during which a hint written by the Provider remains\n * valid. If SWR produces a change later than this after the hint was\n * written, we treat it as a passive `revalidate` instead.\n */\nconst USER_CHANGE_HINT_TTL_MS = 1000;\n\nexport interface UseUserReturn<T extends User = User> {\n /** Current authenticated user, or null if not logged in */\n user: T | null;\n /** Whether user data is being fetched */\n isLoading: boolean;\n /** Whether user is authenticated */\n isAuthenticated: boolean;\n /** Error from fetching user data */\n error: Error | undefined;\n /**\n * Sticky error from the last `fetchUser` failure.\n *\n * Unlike `error`, this value is **not** cleared when the auth state\n * transitions (e.g. from `authenticated` → `unauthenticated`).\n * It persists until `fetchUser` succeeds again or `clearError()` is called.\n *\n * Useful for distinguishing \"not logged in\" from \"account disabled\" in\n * AuthGuard or error-boundary components.\n */\n lastError: Error | undefined;\n /**\n * Manually reset `lastError` to `undefined`.\n */\n clearError: () => void;\n /**\n * Manually update cached user data.\n * Call with `null` to clear, or with a user object to update.\n */\n mutate: (data?: T | null) => Promise<void>;\n /**\n * Why the most recent `user` transition happened.\n *\n * - `null` — No user-change has been observed yet in this component\n * (e.g. before the first `fetchUser` resolves on mount).\n * - `'initial'` — First time the Provider observed a user value\n * (including `null` for unauthenticated cold start).\n * - `'login'` — Triggered by an explicit `login()` / multi-step finalize\n * / `injectAuth()` call.\n * - `'logout'` — Triggered by an explicit `logout()` / `injectLogout()`.\n * - `'revalidate'` — SWR background revalidation produced a different user.\n * - `'external'` — Cross-tab sync via BroadcastChannel / storage events.\n *\n * Use this to differentiate user-initiated transitions from passive ones,\n * e.g. to suppress a welcome toast on page refresh.\n */\n lastChangeSource: UserChangeSource | null;\n /**\n * Full event object for the most recent `user` transition, including\n * `previousUser` and `timestamp`. `null` before the first transition.\n *\n * See `lastChangeSource` for a quick discriminator.\n */\n lastChangeEvent: UserChangeEvent<T> | null;\n}\n\n/**\n * Hook to access current user data via SWR cache.\n *\n * Uses SWR's stale-while-revalidate strategy:\n * - Immediately returns cached user data\n * - Revalidates in the background using the configured fetchUser function\n * - Automatically syncs across all components using this hook\n *\n * @example\n * ```tsx\n * const { user, isLoading, isAuthenticated } = useUser<MyUser>();\n *\n * if (isLoading) return <Spinner />;\n * if (!isAuthenticated) return <LoginPage />;\n * return <Dashboard user={user} />;\n * ```\n *\n * @example Distinguish user-change sources\n * ```tsx\n * const { user, lastChangeSource } = useUser();\n *\n * useEffect(() => {\n * // Only auto-redirect when we detect an *existing* session on mount,\n * // not when the user just pressed the \"Login\" button (the login form\n * // is responsible for its own redirect there).\n * if (user && lastChangeSource === 'initial') {\n * showRedirectOverlay();\n * }\n * }, [user, lastChangeSource]);\n * ```\n */\nexport function useUser<T extends User = User>(): UseUserReturn<T> {\n const { tokenManager, stateMachine, config, emitter, userChangeHint } = useAuthContext();\n\n // ── lastError 状态管理 ──────────────────────────────────────\n const lastErrorRef = useRef<Error | undefined>(undefined);\n const retryCountRef = useRef(0);\n const [, setTick] = useState(0);\n const forceUpdate = useCallback(() => setTick((t) => t + 1), []);\n\n const clearError = useCallback(() => {\n lastErrorRef.current = undefined;\n forceUpdate();\n }, [forceUpdate]);\n\n // ── user-change 事件:previousUser 跟踪 + lastChangeSource 状态 ──\n // Use a sentinel (`undefined`) to mean \"never observed a value yet\",\n // separate from `null` which means \"observed and was unauthenticated\".\n const previousUserRef = useRef<T | null | undefined>(undefined);\n const [lastChangeEvent, setLastChangeEvent] = useState<UserChangeEvent<T> | null>(null);\n\n const fetcher = async (): Promise<T | null> => {\n const token = tokenManager.getAccessToken();\n if (!token) return null;\n\n if (tokenManager.isExpired()) {\n try {\n await tokenManager.refresh();\n } catch {\n // refresh 失败,但 token 可能仍然有效(如 cookie-based 场景)\n // 如果 token 已被清除则放弃,否则继续尝试 fetchUser 让服务端验证\n const remainingToken = tokenManager.getAccessToken();\n if (!remainingToken) return null;\n }\n }\n\n if (config.fetchUser) {\n const currentToken = tokenManager.getAccessToken();\n if (!currentToken) return null;\n return (await config.fetchUser(currentToken)) as T;\n }\n\n return null;\n };\n\n const {\n data,\n error,\n isLoading,\n mutate: swrMutate,\n } = useSWR<T | null>(AUTH_KEY, fetcher, {\n revalidateOnFocus: config.swrOptions?.revalidateOnFocus ?? true,\n revalidateOnReconnect: config.swrOptions?.revalidateOnReconnect ?? true,\n shouldRetryOnError: false,\n ...(config.swrOptions?.dedupingInterval !== undefined && {\n dedupingInterval: config.swrOptions.dedupingInterval,\n }),\n ...(config.swrOptions?.focusThrottleInterval !== undefined && {\n focusThrottleInterval: config.swrOptions.focusThrottleInterval,\n }),\n ...(config.swrOptions?.refreshInterval !== undefined && {\n refreshInterval: config.swrOptions.refreshInterval,\n }),\n });\n\n // ── user-change 派发(每当 SWR 的 data 稳定为 user/null 时触发一次)──\n useEffect(() => {\n // SWR 尚未完成首次解析:data === undefined → 跳过\n if (data === undefined) return;\n\n const prev = previousUserRef.current;\n // 值未变化(含对象引用 + null → null)→ 不派发\n if (Object.is(prev, data)) return;\n\n // 选择 source(优先级从高到低):\n // 1) Provider 近期标记了 hint(TTL 内)→ 使用 hint.source\n // hint 是由 emit('login') / emit('logout') / BroadcastChannel 等\n // 显式同步写入的\"权威信号\",必须优先于\"prev 是否存在\"启发式。\n // 否则在 SWR 首屏 fetch 未完成前用户就触发 login() 时,\n // 会把本应是 'login' 的事件错误地派发为 'initial'。\n // 2) 首次(prev === undefined)→ 'initial'\n // 3) 其余一律视为被动 revalidate\n let source: UserChangeSource;\n const now = Date.now();\n const hintFresh =\n userChangeHint.source !== null && now - userChangeHint.timestamp <= USER_CHANGE_HINT_TTL_MS;\n if (hintFresh) {\n source = userChangeHint.source as UserChangeSource;\n } else if (prev === undefined) {\n source = 'initial';\n } else {\n source = 'revalidate';\n }\n\n // NOTE: We intentionally do NOT clear the hint here. Multiple useUser()\n // consumers may each observe the same SWR data change within the same\n // tick; all of them should see the same source. The hint will naturally\n // expire via TTL before the next unrelated SWR revalidate arrives.\n\n const event: UserChangeEvent<T> = {\n source,\n user: data,\n previousUser: prev,\n timestamp: Date.now(),\n };\n\n previousUserRef.current = data;\n setLastChangeEvent(event);\n emitter.emit('user-change', event as UserChangeEvent);\n }, [data, emitter, userChangeHint]);\n\n // ── 同步 lastError + onFetchUserError 回调 ─────────────────\n useEffect(() => {\n if (error) {\n lastErrorRef.current = error;\n forceUpdate();\n\n // 调用 onFetchUserError 回调(如果已配置)\n if (config.onFetchUserError) {\n const action = config.onFetchUserError(error);\n\n if (action === 'retry' && retryCountRef.current < 1) {\n retryCountRef.current += 1;\n // 触发 SWR 重新请求\n swrMutate();\n } else if (action === 'logout') {\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n }\n // 'ignore' → 仅保留 lastError,不做额外操作\n }\n } else if (data !== undefined && !error) {\n // fetchUser 成功(data 可以是 null 或 user),重置 lastError 和重试计数\n if (lastErrorRef.current !== undefined) {\n lastErrorRef.current = undefined;\n forceUpdate();\n }\n retryCountRef.current = 0;\n }\n }, [error, data, forceUpdate, config, tokenManager, stateMachine, swrMutate]);\n\n const mutate = async (newData?: T | null): Promise<void> => {\n if (newData === undefined) {\n await swrMutate();\n } else {\n await swrMutate(newData, { revalidate: false });\n }\n };\n\n return {\n user: data ?? null,\n isLoading,\n isAuthenticated: !!data,\n error,\n lastError: lastErrorRef.current,\n clearError,\n mutate,\n lastChangeSource: lastChangeEvent?.source ?? null,\n lastChangeEvent,\n };\n}\n\n/** SWR cache key used for user data. Exported for advanced usage. */\nexport { AUTH_KEY };\n","import type { AuthResponse, LoginCallOptions } from '@swr-login/core';\nimport { isMultiStepPlugin } from '@swr-login/core';\nimport { useCallback, useState } from 'react';\nimport { mutate as swrGlobalMutate } from 'swr';\nimport { useAuthContext } from '../context';\nimport { AUTH_KEY } from './useUser';\n\nexport interface UseLoginOptions {\n /** Plugin name to use for login */\n pluginName?: string;\n}\n\nexport interface UseLoginReturn<TCredentials = unknown> {\n /**\n * Trigger login with specified credentials.\n * If pluginName was not provided in options, it must be passed as first argument.\n *\n * The `options` argument is forwarded to `PluginManager.login()`. Specifically,\n * `options.context` is exposed to the underlying plugin via\n * `PluginContext.loginContext` (opaque pass-through). Use it to disambiguate\n * concurrent login flows or pass per-call hints to plugin hooks\n * (e.g., `coding-auth-password`'s `onPreReset`) without resorting to\n * module-level mutable variables.\n *\n * @example\n * ```tsx\n * // With useLogin('password')\n * await login({ user, pass }, { context: { variant: 'teacher' } });\n *\n * // Without preset pluginName\n * await login('password', { user, pass }, { context: { variant: 'teacher' } });\n * ```\n */\n login: (\n credentialsOrPluginName: TCredentials | string,\n credentialsOrOptions?: TCredentials | LoginCallOptions,\n options?: LoginCallOptions,\n ) => Promise<AuthResponse>;\n /** Whether a login request is in progress */\n isLoading: boolean;\n /** Last login error, if any */\n error: Error | null;\n /** Reset error state */\n reset: () => void;\n}\n\n/**\n * Hook to trigger login flow via a registered plugin.\n *\n * @param pluginName - Optional default plugin name\n *\n * @example\n * ```tsx\n * // With default plugin\n * const { login, isLoading, error } = useLogin('password');\n * await login({ username: 'alice', password: 'secret' });\n *\n * // Without default plugin (specify at call time)\n * const { login } = useLogin();\n * await login('oauth-google', { redirect: false });\n * ```\n */\nexport function useLogin<TCredentials = unknown>(\n pluginName?: string,\n): UseLoginReturn<TCredentials> {\n const { pluginManager, tokenManager, stateMachine, config } = useAuthContext();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const login = useCallback(\n async (\n credentialsOrPluginName: TCredentials | string,\n credentialsOrOptions?: TCredentials | LoginCallOptions,\n maybeOptions?: LoginCallOptions,\n ): Promise<AuthResponse> => {\n let resolvedPlugin: string;\n let resolvedCredentials: TCredentials;\n let resolvedOptions: LoginCallOptions | undefined;\n\n if (typeof credentialsOrPluginName === 'string' && !pluginName) {\n // Form: login(pluginName, credentials?, options?)\n resolvedPlugin = credentialsOrPluginName;\n resolvedCredentials = (credentialsOrOptions ?? {}) as TCredentials;\n resolvedOptions = maybeOptions;\n } else if (pluginName) {\n // Form: login(credentials, options?)\n resolvedPlugin = pluginName;\n resolvedCredentials = credentialsOrPluginName as TCredentials;\n resolvedOptions = credentialsOrOptions as LoginCallOptions | undefined;\n } else {\n throw new Error(\n '[swr-login] Plugin name is required. Provide it to useLogin() or login().',\n );\n }\n\n setIsLoading(true);\n setError(null);\n stateMachine.transition('authenticating');\n\n // 检查是否为多步骤插件,给出友好错误提示\n const targetPlugin = pluginManager.getPlugin(resolvedPlugin);\n if (targetPlugin && isMultiStepPlugin(targetPlugin)) {\n const err = new Error(\n `[swr-login] Plugin \"${resolvedPlugin}\" is a multi-step plugin. Use useMultiStepLogin() instead of useLogin() for multi-step login flows.`,\n );\n setError(err);\n stateMachine.transition('error');\n setIsLoading(false);\n throw err;\n }\n\n try {\n const response = await pluginManager.login(\n resolvedPlugin,\n resolvedCredentials,\n resolvedOptions,\n );\n\n // ── afterAuth:在 plugin 成功后、fetchUser 之前执行自定义钩子 ──\n let shouldSkipFetchUser = false;\n if (config.afterAuth) {\n try {\n await config.afterAuth({\n pluginName: resolvedPlugin,\n authResponse: response,\n skipFetchUser: () => {\n shouldSkipFetchUser = true;\n },\n });\n } catch (afterAuthErr) {\n // afterAuth 抛错:回滚 token,login() reject\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n throw afterAuthErr;\n }\n }\n\n // ── validateUserOnLogin:在 plugin 成功后调用 fetchUser 验证用户状态 ──\n if (!shouldSkipFetchUser && config.fetchUser && config.validateUserOnLogin !== false) {\n try {\n const user = await config.fetchUser(response.accessToken);\n // 将 fetchUser 返回的用户写入 SWR 缓存,避免 useUser 重复请求\n await swrGlobalMutate(AUTH_KEY, user, { revalidate: false });\n } catch (fetchUserErr) {\n // fetchUser 失败:回滚 token,转为 unauthenticated\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n throw fetchUserErr;\n }\n }\n\n stateMachine.transition('authenticated');\n\n // Update cache adapter if available\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n\n return response;\n } catch (err) {\n const authError = err instanceof Error ? err : new Error('Login failed');\n setError(authError);\n stateMachine.transition('error');\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, tokenManager, stateMachine, config, pluginName],\n );\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return { login, isLoading, error, reset };\n}\n","import type { AuthResponse } from '@swr-login/core';\nimport { useCallback, useRef, useState } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface UseMultiStepLoginReturn<TCredentials = unknown> {\n /** 当前步骤索引(从 0 开始,-1 表示未开始) */\n currentStep: number;\n /** 当前步骤名称(未开始时为 null) */\n currentStepName: string | null;\n /** 总步骤数 */\n totalSteps: number;\n /** 启动流程(执行第一步) */\n start: (credentials: TCredentials) => Promise<void>;\n /** 继续下一步 */\n next: (input: unknown) => Promise<void>;\n /** 回到上一步(仅 UI 层回退,不重新执行步骤逻辑) */\n back: () => void;\n /** 取消整个流程,重置状态 */\n cancel: () => void;\n /** 当前步骤的输出数据(传给 UI 渲染) */\n stepData: unknown;\n /** 是否正在执行当前步骤 */\n isLoading: boolean;\n /** 当前步骤的错误 */\n error: Error | null;\n /** 重置错误状态 */\n reset: () => void;\n /** 流程是否已完成 */\n isComplete: boolean;\n /** 最终的 AuthResponse(流程完成后可用) */\n authResponse: AuthResponse | null;\n}\n\n/**\n * Hook 用于驱动多步骤登录插件的流程。\n *\n * 提供步骤状态管理、前进/后退/取消等操作,自动在最后一步完成后\n * 调用 finalizeAuth 并更新全局登录态。\n *\n * @param pluginName - 多步骤插件名称\n *\n * @example\n * ```tsx\n * function ClassCodeLoginFlow() {\n * const {\n * currentStepName, stepData, start, next, back, isLoading, error, isComplete,\n * } = useMultiStepLogin<{ classCode: string; loginCode: string }>('class-code');\n *\n * if (isComplete) {\n * return <Redirect to=\"/dashboard\" />;\n * }\n *\n * // 步骤 1:输入班级码\n * if (currentStepName === 'verify-code' || currentStep === -1) {\n * return <ClassCodeForm onSubmit={start} isLoading={isLoading} error={error} />;\n * }\n *\n * // 步骤 2:选择学生\n * if (currentStepName === 'select-student') {\n * return (\n * <StudentList\n * students={stepData.students}\n * onSelect={(userId) => next({ userId, classLoginToken: stepData.classLoginToken })}\n * onBack={back}\n * isLoading={isLoading}\n * />\n * );\n * }\n *\n * // 步骤 3:获取 Token(自动执行,显示 loading)\n * if (currentStepName === 'get-token') {\n * return <LoadingSpinner text=\"登录中...\" />;\n * }\n * }\n * ```\n */\nexport function useMultiStepLogin<TCredentials = unknown>(\n pluginName: string,\n): UseMultiStepLoginReturn<TCredentials> {\n const { pluginManager, stateMachine, config } = useAuthContext();\n\n const [currentStep, setCurrentStep] = useState(-1); // -1 表示未开始\n const [stepData, setStepData] = useState<unknown>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [isComplete, setIsComplete] = useState(false);\n const [authResponse, setAuthResponse] = useState<AuthResponse | null>(null);\n\n // 用 ref 保存中间步骤的累积数据,避免闭包问题\n const stepOutputsRef = useRef<unknown[]>([]);\n\n const steps = pluginManager.getSteps(pluginName);\n const totalSteps = steps?.length ?? 0;\n\n const currentStepName = currentStep >= 0 && steps ? (steps[currentStep]?.name ?? null) : null;\n\n const executeStepAt = useCallback(\n async (stepIndex: number, input: unknown) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const output = await pluginManager.executeStep(pluginName, stepIndex, input);\n stepOutputsRef.current[stepIndex] = output;\n setStepData(output);\n setCurrentStep(stepIndex);\n\n // 如果是最后一步,执行 finalizeAuth\n if (stepIndex === totalSteps - 1) {\n const response = await pluginManager.finalizeMultiStep(pluginName, output);\n stateMachine.transition('authenticated');\n\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n\n setAuthResponse(response);\n setIsComplete(true);\n }\n } catch (err) {\n const authError = err instanceof Error ? err : new Error('Step execution failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, pluginName, totalSteps, stateMachine, config],\n );\n\n const start = useCallback(\n async (credentials: TCredentials) => {\n // 重置状态\n stepOutputsRef.current = [];\n setCurrentStep(-1);\n setStepData(null);\n setIsComplete(false);\n setAuthResponse(null);\n setError(null);\n stateMachine.transition('authenticating');\n\n await executeStepAt(0, credentials);\n },\n [executeStepAt, stateMachine],\n );\n\n const next = useCallback(\n async (input: unknown) => {\n const nextIndex = currentStep + 1;\n if (nextIndex >= totalSteps) {\n throw new Error('[swr-login] No more steps to execute.');\n }\n await executeStepAt(nextIndex, input);\n },\n [currentStep, totalSteps, executeStepAt],\n );\n\n const back = useCallback(() => {\n if (currentStep > 0) {\n const prevStep = currentStep - 1;\n setCurrentStep(prevStep);\n setStepData(stepOutputsRef.current[prevStep] ?? null);\n setError(null);\n }\n }, [currentStep]);\n\n const cancel = useCallback(() => {\n setCurrentStep(-1);\n setStepData(null);\n setIsLoading(false);\n setError(null);\n setIsComplete(false);\n setAuthResponse(null);\n stepOutputsRef.current = [];\n stateMachine.transition('unauthenticated');\n }, [stateMachine]);\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return {\n currentStep,\n currentStepName,\n totalSteps,\n start,\n next,\n back,\n cancel,\n stepData,\n isLoading,\n error,\n reset,\n isComplete,\n authResponse,\n };\n}\n","import type { AuthInjector, AuthResponse } from '@swr-login/core';\nimport { useCallback } from 'react';\nimport { useAuthContext } from '../context';\n\n/**\n * Hook 用于从外部注入登录态到 swr-login 体系。\n *\n * 适用场景:\n * - 多步骤登录流程(如班级码登录、MFA)\n * - 第三方 SDK 登录完成后同步状态\n * - iframe / WebView 登录回调\n *\n * @example\n * ```tsx\n * function ExternalLoginCallback() {\n * const { injectAuth } = useAuthInjector();\n *\n * const handleLoginComplete = async (token: string, user: User) => {\n * await injectAuth({\n * user,\n * accessToken: token,\n * expiresAt: Date.now() + 86400000,\n * });\n * router.push('/dashboard');\n * };\n * }\n * ```\n */\nexport function useAuthInjector(): AuthInjector {\n const { tokenManager, emitter, stateMachine, config } = useAuthContext();\n\n const injectAuth = useCallback(\n async (response: AuthResponse): Promise<void> => {\n // 1. 存储 token\n tokenManager.setTokens({\n accessToken: response.accessToken,\n refreshToken: response.refreshToken,\n expiresAt: response.expiresAt,\n });\n\n // 2. 状态机转换为已认证。\n //\n // injectAuth 作为\"逃生舱\"应能从任何状态恢复到 authenticated,\n // 但状态机不允许 `error -> authenticated` / `unauthenticated -> authenticated` 直接转换。\n // 这两种状态都允许 `-> authenticating`,而 `authenticating -> authenticated` 合法,\n // 因此通过 `authenticating` 中转一次即可完成恢复,且每一步都符合状态机契约。\n const currentState = stateMachine.state;\n if (currentState === 'error' || currentState === 'unauthenticated') {\n stateMachine.transition('authenticating');\n }\n stateMachine.transition('authenticated');\n\n // 3. 发射登录事件(触发 onLogin 回调、跨标签页同步等)\n emitter.emit('login', { user: response.user });\n\n // 4. 更新缓存(使 useUser() 等 Hook 感知到用户信息)\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n },\n [tokenManager, emitter, stateMachine, config],\n );\n\n const injectLogout = useCallback(async (): Promise<void> => {\n // 1. 清除 token\n tokenManager.clearTokens();\n\n // 2. 状态机转换为未认证\n stateMachine.transition('unauthenticated');\n\n // 3. 发射登出事件\n emitter.emit('logout', undefined);\n\n // 4. 清除缓存\n if (config.cacheAdapter) {\n await config.cacheAdapter.clearUser();\n }\n }, [tokenManager, emitter, stateMachine, config]);\n\n return { injectAuth, injectLogout };\n}\n","import type { User, UserChangeEvent, UserChangeSource } from '@swr-login/core';\nimport { useEffect, useRef, useState } from 'react';\nimport { useAuthContext } from '../context';\n\n/**\n * Subscribe to `user-change` events as a **discrete event stream**.\n *\n * Unlike `useUser().user` (which reflects the *current* user value), this\n * hook returns the most recent transition event and re-renders the caller\n * only when a new transition occurs. It's intended for `useEffect`-driven\n * side effects that care about *when* and *why* the user changed rather\n * than the current user value itself.\n *\n * Returns `null` until the first transition is observed by the Provider.\n *\n * @typeParam T - Concrete user type (defaults to base `User`)\n *\n * @example\n * ```tsx\n * const change = useUserChange<MyUser>();\n *\n * useEffect(() => {\n * if (change?.source === 'login') {\n * toast.success(`Welcome back, ${change.user?.name}!`);\n * }\n * }, [change]);\n * ```\n *\n * @example Filter by source\n * ```tsx\n * const change = useUserChange();\n * useEffect(() => {\n * if (change?.source === 'external') {\n * // Another tab just logged in / out — refresh local UI\n * refreshSidebar();\n * }\n * }, [change]);\n * ```\n */\nexport function useUserChange<T extends User = User>(): UserChangeEvent<T> | null {\n const { emitter } = useAuthContext();\n const [event, setEvent] = useState<UserChangeEvent<T> | null>(null);\n\n useEffect(() => {\n const unsubscribe = emitter.on('user-change', (payload) => {\n setEvent(payload as UserChangeEvent<T>);\n });\n return unsubscribe;\n }, [emitter]);\n\n return event;\n}\n\n/**\n * Register a side-effect callback that fires on every `user-change` event,\n * **without** triggering a re-render of the calling component.\n *\n * Ideal for analytics / logging / imperative side effects that don't need\n * to participate in React's render cycle.\n *\n * The callback receives the full `UserChangeEvent` (including `previousUser`,\n * `timestamp`, and `source`). The listener is automatically unsubscribed on\n * unmount.\n *\n * Note: the callback reference is stored in a ref and always called with\n * the latest closure — you do NOT need to memoize it with `useCallback`.\n *\n * @example Analytics\n * ```tsx\n * useUserChangeEffect((e) => {\n * if (e.source === 'login') {\n * analytics.track('user_login', { userId: e.user?.id });\n * }\n * if (e.source === 'logout') {\n * analytics.track('user_logout', { userId: e.previousUser?.id });\n * }\n * });\n * ```\n *\n * @example Filter sources\n * ```tsx\n * useUserChangeEffect((e) => {\n * // Only react to passive revalidations that flipped the user to null\n * // (e.g. server returned 401 → session expired silently).\n * if (e.source === 'revalidate' && e.user === null && e.previousUser) {\n * showReLoginPrompt();\n * }\n * });\n * ```\n */\nexport function useUserChangeEffect<T extends User = User>(\n listener: (event: UserChangeEvent<T>) => void,\n): void {\n const { emitter } = useAuthContext();\n\n // Store latest callback in a ref so consumers don't need useCallback.\n const listenerRef = useRef(listener);\n listenerRef.current = listener;\n\n useEffect(() => {\n const unsubscribe = emitter.on('user-change', (payload) => {\n try {\n listenerRef.current(payload as UserChangeEvent<T>);\n } catch (err) {\n console.error('[swr-login] Error in useUserChangeEffect listener:', err);\n }\n });\n return unsubscribe;\n }, [emitter]);\n}\n\n/**\n * Convenience hook: fires the callback only when the change source matches\n * the given filter. Thin wrapper around {@link useUserChangeEffect}.\n *\n * @example\n * ```tsx\n * // Only run on explicit login (not on cold-start initial load)\n * useUserChangeOn('login', (e) => router.push('/dashboard'));\n *\n * // Subscribe to multiple sources at once\n * useUserChangeOn(['login', 'external'], (e) => refreshSidebar());\n * ```\n */\nexport function useUserChangeOn<T extends User = User>(\n source: UserChangeSource | UserChangeSource[],\n listener: (event: UserChangeEvent<T>) => void,\n): void {\n const sources = Array.isArray(source) ? source : [source];\n useUserChangeEffect<T>((event) => {\n if (sources.includes(event.source)) {\n listener(event);\n }\n });\n}\n","import { useCallback, useState } from 'react';\nimport { mutate } from 'swr';\nimport { useAuthContext } from '../context';\nimport { AUTH_KEY } from './useUser';\n\nexport interface UseLogoutOptions {\n /** Specific plugin to call logout on */\n pluginName?: string;\n /** Whether to broadcast logout to other tabs (default: true) */\n broadcast?: boolean;\n}\n\nexport interface UseLogoutReturn {\n /** Execute logout */\n logout: (options?: UseLogoutOptions) => Promise<void>;\n /** Whether logout is in progress */\n isLoading: boolean;\n}\n\n/**\n * Hook to perform secure logout with cross-tab sync.\n *\n * Logout flow:\n * 1. Call plugin logout (if applicable)\n * 2. Clear stored tokens\n * 3. Clear SWR cache (mutate user to null)\n * 4. Broadcast logout to other tabs\n * 5. Transition state to 'unauthenticated'\n *\n * @example\n * ```tsx\n * const { logout, isLoading } = useLogout();\n *\n * <button onClick={() => logout()} disabled={isLoading}>\n * Sign Out\n * </button>\n * ```\n */\nexport function useLogout(): UseLogoutReturn {\n const { pluginManager, stateMachine, broadcastSync } = useAuthContext();\n const [isLoading, setIsLoading] = useState(false);\n\n const logout = useCallback(\n async (options?: UseLogoutOptions) => {\n setIsLoading(true);\n\n try {\n // Call plugin logout\n await pluginManager.logout(options?.pluginName);\n\n // Clear SWR cache\n await mutate(AUTH_KEY, null, { revalidate: false });\n\n // Update state\n stateMachine.transition('unauthenticated');\n\n // Broadcast to other tabs\n const shouldBroadcast = options?.broadcast !== false;\n if (shouldBroadcast && broadcastSync) {\n broadcastSync.send('LOGOUT');\n }\n } catch (err) {\n console.error('[swr-login] Logout error:', err);\n // Even if logout API fails, clear local state\n await mutate(AUTH_KEY, null, { revalidate: false });\n stateMachine.transition('unauthenticated');\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, stateMachine, broadcastSync],\n );\n\n return { logout, isLoading };\n}\n","import type { TokenAdapter } from '@swr-login/core';\nimport { useCallback } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface UseAdapterReturn {\n /**\n * Synchronously check whether the adapter currently holds a valid auth credential.\n *\n * Uses `adapter.hasAuth()` if the adapter implements it,\n * otherwise falls back to `adapter.getAccessToken() !== null`.\n *\n * This is a **synchronous, non-reactive** check — it reads the adapter's\n * current state at call time. It does NOT trigger re-renders when the\n * underlying token changes. For reactive auth state, use `useUser()`.\n *\n * Typical use cases:\n * - Conditional rendering before SWR hydration completes\n * - Route guard pre-checks\n * - Analytics / logging\n *\n * @example\n * ```tsx\n * const { hasAuth } = useAdapter();\n *\n * // 首页自动跳转\n * useEffect(() => {\n * if (hasAuth()) {\n * router.push('/dashboard');\n * }\n * }, []);\n * ```\n */\n hasAuth: () => boolean;\n\n /**\n * Direct reference to the underlying `TokenAdapter` instance.\n *\n * Escape hatch for advanced scenarios where you need low-level\n * access to token storage (e.g. reading refresh token, manually\n * clearing tokens outside of the normal logout flow).\n *\n * Prefer `hasAuth()` and the higher-level hooks (`useUser`, `useLogin`)\n * for most use cases.\n */\n adapter: TokenAdapter;\n}\n\n/**\n * Hook to access the token adapter and its utility methods.\n *\n * Provides a convenient `hasAuth()` check that works synchronously,\n * useful for UI decisions that need to happen before SWR data is available.\n *\n * @example\n * ```tsx\n * function HomePage() {\n * const { hasAuth } = useAdapter();\n * const router = useRouter();\n *\n * useEffect(() => {\n * if (hasAuth()) {\n * router.push('/dashboard');\n * }\n * }, []);\n *\n * return <LoginPage />;\n * }\n * ```\n */\nexport function useAdapter(): UseAdapterReturn {\n const { config } = useAuthContext();\n const adapter = config.adapter;\n\n const hasAuth = useCallback((): boolean => {\n if (typeof adapter.hasAuth === 'function') {\n return adapter.hasAuth();\n }\n // Fallback: 检查 accessToken 是否存在\n return adapter.getAccessToken() !== null;\n }, [adapter]);\n\n return { hasAuth, adapter };\n}\n","import { isTokenExpired } from '@swr-login/core';\nimport { useMemo } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface SessionInfo {\n /** Current access token */\n accessToken: string | null;\n /** Current refresh token */\n refreshToken: string | null;\n /** Token expiration timestamp (ms since epoch) */\n expiresAt: number | null;\n /** Whether the access token has expired */\n isExpired: boolean;\n /** Whether tokens exist (regardless of expiry) */\n hasTokens: boolean;\n}\n\n/**\n * Hook to access raw session/token information.\n *\n * Useful for:\n * - Adding Authorization headers to custom requests\n * - Checking token expiry status\n * - Debugging session state\n *\n * @example\n * ```tsx\n * const { accessToken, isExpired } = useSession();\n *\n * const fetchData = () => fetch('/api/data', {\n * headers: { Authorization: `Bearer ${accessToken}` },\n * });\n * ```\n */\nexport function useSession(): SessionInfo {\n const { tokenManager } = useAuthContext();\n\n return useMemo(() => {\n const accessToken = tokenManager.getAccessToken();\n const refreshToken = tokenManager.getRefreshToken();\n const expiresAt = tokenManager.getAccessToken() ? tokenManager.getExpiresAt() : null;\n\n return {\n accessToken,\n refreshToken,\n expiresAt,\n isExpired: isTokenExpired(expiresAt),\n hasTokens: !!accessToken,\n };\n }, [tokenManager]);\n}\n","import { useCallback } from 'react';\nimport { useUser } from './useUser';\n\nexport interface UsePermissionReturn {\n /** Check if user has a specific permission */\n hasPermission: (permission: string) => boolean;\n /** Check if user has a specific role */\n hasRole: (role: string) => boolean;\n /** Check if user has all specified permissions */\n hasAllPermissions: (permissions: string[]) => boolean;\n /** Check if user has any of the specified permissions */\n hasAnyPermission: (permissions: string[]) => boolean;\n /** Check if user has all specified roles */\n hasAllRoles: (roles: string[]) => boolean;\n /** Check if user has any of the specified roles */\n hasAnyRole: (roles: string[]) => boolean;\n}\n\n/**\n * Hook for checking user permissions and roles.\n *\n * Reads from the `permissions` and `roles` arrays on the User object.\n *\n * @example\n * ```tsx\n * const { hasPermission, hasRole } = usePermission();\n *\n * if (hasPermission('admin:write')) {\n * // Show admin controls\n * }\n *\n * if (hasRole('editor')) {\n * // Show editor tools\n * }\n * ```\n */\nexport function usePermission(): UsePermissionReturn {\n const { user } = useUser();\n\n const hasPermission = useCallback(\n (permission: string): boolean => {\n return user?.permissions?.includes(permission) ?? false;\n },\n [user],\n );\n\n const hasRole = useCallback(\n (role: string): boolean => {\n return user?.roles?.includes(role) ?? false;\n },\n [user],\n );\n\n const hasAllPermissions = useCallback(\n (permissions: string[]): boolean => {\n if (!user?.permissions) return false;\n return permissions.every((p) => user.permissions?.includes(p));\n },\n [user],\n );\n\n const hasAnyPermission = useCallback(\n (permissions: string[]): boolean => {\n if (!user?.permissions) return false;\n return permissions.some((p) => user.permissions?.includes(p));\n },\n [user],\n );\n\n const hasAllRoles = useCallback(\n (roles: string[]): boolean => {\n if (!user?.roles) return false;\n return roles.every((r) => user.roles?.includes(r));\n },\n [user],\n );\n\n const hasAnyRole = useCallback(\n (roles: string[]): boolean => {\n if (!user?.roles) return false;\n return roles.some((r) => user.roles?.includes(r));\n },\n [user],\n );\n\n return {\n hasPermission,\n hasRole,\n hasAllPermissions,\n hasAnyPermission,\n hasAllRoles,\n hasAnyRole,\n };\n}\n","import type React from 'react';\nimport { usePermission } from '../hooks/usePermission';\nimport { useUser } from '../hooks/useUser';\n\nexport interface AuthGuardProps {\n children: React.ReactNode;\n /** Required permissions (checked against user.permissions) */\n permissions?: string[];\n /** Required roles (checked against user.roles) */\n roles?: string[];\n /** If true, ALL permissions/roles must match. If false, ANY match suffices. (default: false) */\n requireAll?: boolean;\n /** Component to render when user is not authenticated or lacks permissions */\n fallback?: React.ReactNode;\n /** Component to render while checking auth status */\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Declarative auth guard component.\n * Protects child content based on authentication state and permissions/roles.\n *\n * @example\n * ```tsx\n * <AuthGuard\n * permissions={['admin', 'editor']}\n * requireAll={false}\n * fallback={<Navigate to=\"/login\" />}\n * loadingComponent={<Skeleton />}\n * >\n * <AdminPanel />\n * </AuthGuard>\n * ```\n */\nexport function AuthGuard({\n children,\n permissions,\n roles,\n requireAll = false,\n fallback = null,\n loadingComponent = null,\n}: AuthGuardProps) {\n const { isLoading, isAuthenticated } = useUser();\n const { hasAllPermissions, hasAnyPermission, hasAllRoles, hasAnyRole } = usePermission();\n\n if (isLoading) {\n return <>{loadingComponent}</>;\n }\n\n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n const hasRequiredPermissions = requireAll\n ? hasAllPermissions(permissions)\n : hasAnyPermission(permissions);\n\n if (!hasRequiredPermissions) {\n return <>{fallback}</>;\n }\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n const hasRequiredRoles = requireAll ? hasAllRoles(roles) : hasAnyRole(roles);\n\n if (!hasRequiredRoles) {\n return <>{fallback}</>;\n }\n }\n\n return <>{children}</>;\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { SWRLoginConfig, AuthResponse, AuthInjector, User, UserChangeSource, UserChangeEvent, TokenAdapter, PluginManager, TokenManager, AuthEventEmitter, AuthStateMachine, BroadcastSync } from '@swr-login/core';
2
+ import { SWRLoginConfig, LoginCallOptions, AuthResponse, AuthInjector, User, UserChangeSource, UserChangeEvent, TokenAdapter, PluginManager, TokenManager, AuthEventEmitter, AuthStateMachine, BroadcastSync } from '@swr-login/core';
3
3
  import React from 'react';
4
4
 
5
5
  interface SWRLoginProviderProps {
@@ -40,8 +40,24 @@ interface UseLoginReturn<TCredentials = unknown> {
40
40
  /**
41
41
  * Trigger login with specified credentials.
42
42
  * If pluginName was not provided in options, it must be passed as first argument.
43
+ *
44
+ * The `options` argument is forwarded to `PluginManager.login()`. Specifically,
45
+ * `options.context` is exposed to the underlying plugin via
46
+ * `PluginContext.loginContext` (opaque pass-through). Use it to disambiguate
47
+ * concurrent login flows or pass per-call hints to plugin hooks
48
+ * (e.g., `coding-auth-password`'s `onPreReset`) without resorting to
49
+ * module-level mutable variables.
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * // With useLogin('password')
54
+ * await login({ user, pass }, { context: { variant: 'teacher' } });
55
+ *
56
+ * // Without preset pluginName
57
+ * await login('password', { user, pass }, { context: { variant: 'teacher' } });
58
+ * ```
43
59
  */
44
- login: (credentialsOrPluginName: TCredentials | string, credentials?: TCredentials) => Promise<AuthResponse>;
60
+ login: (credentialsOrPluginName: TCredentials | string, credentialsOrOptions?: TCredentials | LoginCallOptions, options?: LoginCallOptions) => Promise<AuthResponse>;
45
61
  /** Whether a login request is in progress */
46
62
  isLoading: boolean;
47
63
  /** Last login error, if any */
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { SWRLoginConfig, AuthResponse, AuthInjector, User, UserChangeSource, UserChangeEvent, TokenAdapter, PluginManager, TokenManager, AuthEventEmitter, AuthStateMachine, BroadcastSync } from '@swr-login/core';
2
+ import { SWRLoginConfig, LoginCallOptions, AuthResponse, AuthInjector, User, UserChangeSource, UserChangeEvent, TokenAdapter, PluginManager, TokenManager, AuthEventEmitter, AuthStateMachine, BroadcastSync } from '@swr-login/core';
3
3
  import React from 'react';
4
4
 
5
5
  interface SWRLoginProviderProps {
@@ -40,8 +40,24 @@ interface UseLoginReturn<TCredentials = unknown> {
40
40
  /**
41
41
  * Trigger login with specified credentials.
42
42
  * If pluginName was not provided in options, it must be passed as first argument.
43
+ *
44
+ * The `options` argument is forwarded to `PluginManager.login()`. Specifically,
45
+ * `options.context` is exposed to the underlying plugin via
46
+ * `PluginContext.loginContext` (opaque pass-through). Use it to disambiguate
47
+ * concurrent login flows or pass per-call hints to plugin hooks
48
+ * (e.g., `coding-auth-password`'s `onPreReset`) without resorting to
49
+ * module-level mutable variables.
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * // With useLogin('password')
54
+ * await login({ user, pass }, { context: { variant: 'teacher' } });
55
+ *
56
+ * // Without preset pluginName
57
+ * await login('password', { user, pass }, { context: { variant: 'teacher' } });
58
+ * ```
43
59
  */
44
- login: (credentialsOrPluginName: TCredentials | string, credentials?: TCredentials) => Promise<AuthResponse>;
60
+ login: (credentialsOrPluginName: TCredentials | string, credentialsOrOptions?: TCredentials | LoginCallOptions, options?: LoginCallOptions) => Promise<AuthResponse>;
45
61
  /** Whether a login request is in progress */
46
62
  isLoading: boolean;
47
63
  /** Last login error, if any */
package/dist/index.js CHANGED
@@ -186,12 +186,14 @@ function useUser() {
186
186
  const prev = previousUserRef.current;
187
187
  if (Object.is(prev, data)) return;
188
188
  let source;
189
- if (prev === void 0) {
189
+ const now = Date.now();
190
+ const hintFresh = userChangeHint.source !== null && now - userChangeHint.timestamp <= USER_CHANGE_HINT_TTL_MS;
191
+ if (hintFresh) {
192
+ source = userChangeHint.source;
193
+ } else if (prev === void 0) {
190
194
  source = "initial";
191
195
  } else {
192
- const now = Date.now();
193
- const hintFresh = userChangeHint.source !== null && now - userChangeHint.timestamp <= USER_CHANGE_HINT_TTL_MS;
194
- source = hintFresh ? userChangeHint.source : "revalidate";
196
+ source = "revalidate";
195
197
  }
196
198
  const event = {
197
199
  source,
@@ -251,15 +253,18 @@ function useLogin(pluginName) {
251
253
  const [isLoading, setIsLoading] = useState(false);
252
254
  const [error, setError] = useState(null);
253
255
  const login = useCallback(
254
- async (credentialsOrPluginName, maybeCredentials) => {
256
+ async (credentialsOrPluginName, credentialsOrOptions, maybeOptions) => {
255
257
  let resolvedPlugin;
256
258
  let resolvedCredentials;
259
+ let resolvedOptions;
257
260
  if (typeof credentialsOrPluginName === "string" && !pluginName) {
258
261
  resolvedPlugin = credentialsOrPluginName;
259
- resolvedCredentials = maybeCredentials ?? {};
262
+ resolvedCredentials = credentialsOrOptions ?? {};
263
+ resolvedOptions = maybeOptions;
260
264
  } else if (pluginName) {
261
265
  resolvedPlugin = pluginName;
262
266
  resolvedCredentials = credentialsOrPluginName;
267
+ resolvedOptions = credentialsOrOptions;
263
268
  } else {
264
269
  throw new Error(
265
270
  "[swr-login] Plugin name is required. Provide it to useLogin() or login()."
@@ -279,7 +284,11 @@ function useLogin(pluginName) {
279
284
  throw err;
280
285
  }
281
286
  try {
282
- const response = await pluginManager.login(resolvedPlugin, resolvedCredentials);
287
+ const response = await pluginManager.login(
288
+ resolvedPlugin,
289
+ resolvedCredentials,
290
+ resolvedOptions
291
+ );
283
292
  let shouldSkipFetchUser = false;
284
293
  if (config.afterAuth) {
285
294
  try {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/hooks/useUser.ts","../src/hooks/useLogin.ts","../src/hooks/useMultiStepLogin.ts","../src/hooks/useAuthInjector.ts","../src/hooks/useUserChange.ts","../src/hooks/useLogout.ts","../src/hooks/useAdapter.ts","../src/hooks/useSession.ts","../src/hooks/usePermission.ts","../src/components/AuthGuard.tsx"],"names":["useRef","useEffect","mutate","useState","useCallback","swrGlobalMutate","useMemo","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n AuthEventEmitter,\n AuthStateMachine,\n BroadcastSync,\n PluginManager,\n SWRLoginConfig,\n TokenManager,\n UserChangeSource,\n} from '@swr-login/core';\nimport { createContext, useContext } from 'react';\n\n/**\n * Mutable hint object shared between the Provider and `useUser()` to describe\n * *why* the next user-change event is expected to happen.\n *\n * The Provider writes into this object as it observes `login` / `logout` /\n * `external` events; `useUser()` reads and clears it when the SWR cache value\n * actually transitions. Unset/expired hint → the change is a passive\n * `revalidate` or the very first `initial` load.\n *\n * Using a mutable ref-like object (instead of React state) avoids re-renders\n * and is safe because it is written synchronously from event callbacks and\n * read synchronously during `useUser()`'s `useEffect`.\n *\n * @internal\n */\nexport interface UserChangeHint {\n source: UserChangeSource | null;\n /** `Date.now()` when the hint was written. Stale hints (>1s) are ignored. */\n timestamp: number;\n}\n\n/** Internal context value passed through SWRLoginProvider */\nexport interface AuthContextValue {\n pluginManager: PluginManager;\n tokenManager: TokenManager;\n emitter: AuthEventEmitter;\n stateMachine: AuthStateMachine;\n broadcastSync: BroadcastSync | null;\n config: SWRLoginConfig;\n /** @internal shared hint for the next user-change source */\n userChangeHint: UserChangeHint;\n}\n\nexport const AuthContext = createContext<AuthContextValue | null>(null);\n\n/**\n * Internal hook to access auth context.\n * Must be used within SWRLoginProvider.\n * @throws Error if used outside of SWRLoginProvider\n */\nexport function useAuthContext(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) {\n throw new Error(\n '[swr-login] useAuthContext must be used within <SWRLoginProvider>. ' +\n 'Wrap your app with <SWRLoginProvider> to use swr-login hooks.',\n );\n }\n return ctx;\n}\n","import {\n AuthEventEmitter,\n AuthStateMachine,\n BroadcastSync,\n PluginManager,\n type SWRLoginConfig,\n TokenManager,\n} from '@swr-login/core';\nimport type React from 'react';\nimport { useEffect, useMemo, useRef } from 'react';\nimport { AuthContext, type AuthContextValue, type UserChangeHint } from './context';\n\nexport interface SWRLoginProviderProps {\n /** Authentication configuration */\n config: SWRLoginConfig;\n children: React.ReactNode;\n}\n\n/**\n * SWRLoginProvider initializes the auth system and provides context to all child hooks.\n *\n * @example\n * ```tsx\n * import { SWRLoginProvider } from '@swr-login/react';\n * import { JWTAdapter } from '@swr-login/adapter-jwt';\n * import { PasswordPlugin } from '@swr-login/plugin-password';\n *\n * function App() {\n * return (\n * <SWRLoginProvider\n * config={{\n * adapter: JWTAdapter(),\n * plugins: [PasswordPlugin({ loginUrl: '/api/login' })],\n * }}\n * >\n * <YourApp />\n * </SWRLoginProvider>\n * );\n * }\n * ```\n */\nexport function SWRLoginProvider({ config, children }: SWRLoginProviderProps) {\n const initializedRef = useRef(false);\n\n const contextValue = useMemo<AuthContextValue>(() => {\n const emitter = new AuthEventEmitter();\n const stateMachine = new AuthStateMachine(emitter);\n const tokenManager = new TokenManager(config.adapter, emitter, stateMachine);\n const pluginManager = new PluginManager(tokenManager, emitter);\n\n // Register plugins\n pluginManager.register(...config.plugins);\n\n // Set up broadcast sync if enabled\n const enableSync = config.security?.enableBroadcastSync !== false;\n const broadcastSync = enableSync && typeof window !== 'undefined' ? new BroadcastSync() : null;\n\n // Mutable hint object shared with useUser() to label the next user-change\n // event with its causal source (login/logout/external).\n const userChangeHint: UserChangeHint = { source: null, timestamp: 0 };\n const markHint = (source: UserChangeHint['source']) => {\n userChangeHint.source = source;\n userChangeHint.timestamp = Date.now();\n };\n\n // Label user-change source based on lifecycle events. The actual\n // 'user-change' event is emitted by useUser() once SWR produces a\n // different value; these hints only tell us *why* it's about to change.\n emitter.on('login', () => markHint('login'));\n emitter.on('logout', () => markHint('logout'));\n\n // Wire up lifecycle callbacks\n if (config.onLogin) {\n emitter.on('login', ({ user }) => config.onLogin?.(user));\n }\n if (config.onLogout) {\n emitter.on('logout', () => config.onLogout?.());\n }\n if (config.onError) {\n emitter.on('error', ({ error }) => config.onError?.(error));\n }\n\n return {\n pluginManager,\n tokenManager,\n emitter,\n stateMachine,\n broadcastSync,\n config,\n userChangeHint,\n };\n }, [config]);\n\n // Initialize plugins and broadcast sync on mount\n useEffect(() => {\n if (initializedRef.current) return;\n initializedRef.current = true;\n\n const {\n pluginManager,\n broadcastSync,\n tokenManager,\n stateMachine,\n emitter,\n config: cfg,\n } = contextValue;\n\n // Initialize all plugins\n pluginManager.initializeAll().catch((err) => {\n console.error('[swr-login] Plugin initialization error:', err);\n });\n\n // Check if user has existing token -> restore session\n const existingToken = tokenManager.getAccessToken();\n const expiresAt = tokenManager.getExpiresAt();\n\n if (existingToken && !tokenManager.isExpired()) {\n stateMachine.transition('authenticated');\n } else if (existingToken && expiresAt === null) {\n // expiresAt 未知(如外部登录只设置了 token 但未设置过期时间)\n // 乐观地认为已认证,让 fetchUser / SWR revalidate 来验证\n stateMachine.transition('authenticated');\n } else if (existingToken && tokenManager.isExpired()) {\n stateMachine.transition('unauthenticated');\n emitter.emit('token-expired', undefined);\n }\n\n // Listen for cross-tab events\n if (broadcastSync) {\n const { userChangeHint } = contextValue;\n const markExternal = () => {\n userChangeHint.source = 'external';\n userChangeHint.timestamp = Date.now();\n };\n\n const unsubscribe = broadcastSync.onMessage((message) => {\n switch (message.type) {\n case 'LOGOUT':\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n // Emit-after-mark would be overwritten by the synchronous\n // 'logout' handler (which sets hint='logout'); re-mark to\n // 'external' so useUser labels this cross-tab logout correctly.\n markExternal();\n break;\n case 'LOGIN':\n case 'TOKEN_REFRESH':\n // Cross-tab login / refresh → refresh SWR cache; label the\n // upcoming user-change as 'external'.\n markExternal();\n if (cfg.cacheAdapter) {\n cfg.cacheAdapter.revalidate();\n }\n break;\n }\n });\n\n return () => {\n unsubscribe();\n broadcastSync.destroy();\n };\n }\n }, [contextValue]);\n\n // Visibility change handler for security\n useEffect(() => {\n const { config: cfg, tokenManager, stateMachine, emitter } = contextValue;\n\n if (!cfg.security?.clearOnHidden || typeof document === 'undefined') return;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const handleVisibilityChange = () => {\n if (document.hidden) {\n const delay = cfg.security?.clearOnHiddenDelay ?? 300_000;\n timeoutId = setTimeout(() => {\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n }, delay);\n } else {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n if (timeoutId) clearTimeout(timeoutId);\n };\n }, [contextValue]);\n\n return <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>;\n}\n","import type { User, UserChangeEvent, UserChangeSource } from '@swr-login/core';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport useSWR from 'swr';\nimport { useAuthContext } from '../context';\n\nconst AUTH_KEY = '__swr_login_user__';\n\n/**\n * Time window (ms) during which a hint written by the Provider remains\n * valid. If SWR produces a change later than this after the hint was\n * written, we treat it as a passive `revalidate` instead.\n */\nconst USER_CHANGE_HINT_TTL_MS = 1000;\n\nexport interface UseUserReturn<T extends User = User> {\n /** Current authenticated user, or null if not logged in */\n user: T | null;\n /** Whether user data is being fetched */\n isLoading: boolean;\n /** Whether user is authenticated */\n isAuthenticated: boolean;\n /** Error from fetching user data */\n error: Error | undefined;\n /**\n * Sticky error from the last `fetchUser` failure.\n *\n * Unlike `error`, this value is **not** cleared when the auth state\n * transitions (e.g. from `authenticated` → `unauthenticated`).\n * It persists until `fetchUser` succeeds again or `clearError()` is called.\n *\n * Useful for distinguishing \"not logged in\" from \"account disabled\" in\n * AuthGuard or error-boundary components.\n */\n lastError: Error | undefined;\n /**\n * Manually reset `lastError` to `undefined`.\n */\n clearError: () => void;\n /**\n * Manually update cached user data.\n * Call with `null` to clear, or with a user object to update.\n */\n mutate: (data?: T | null) => Promise<void>;\n /**\n * Why the most recent `user` transition happened.\n *\n * - `null` — No user-change has been observed yet in this component\n * (e.g. before the first `fetchUser` resolves on mount).\n * - `'initial'` — First time the Provider observed a user value\n * (including `null` for unauthenticated cold start).\n * - `'login'` — Triggered by an explicit `login()` / multi-step finalize\n * / `injectAuth()` call.\n * - `'logout'` — Triggered by an explicit `logout()` / `injectLogout()`.\n * - `'revalidate'` — SWR background revalidation produced a different user.\n * - `'external'` — Cross-tab sync via BroadcastChannel / storage events.\n *\n * Use this to differentiate user-initiated transitions from passive ones,\n * e.g. to suppress a welcome toast on page refresh.\n */\n lastChangeSource: UserChangeSource | null;\n /**\n * Full event object for the most recent `user` transition, including\n * `previousUser` and `timestamp`. `null` before the first transition.\n *\n * See `lastChangeSource` for a quick discriminator.\n */\n lastChangeEvent: UserChangeEvent<T> | null;\n}\n\n/**\n * Hook to access current user data via SWR cache.\n *\n * Uses SWR's stale-while-revalidate strategy:\n * - Immediately returns cached user data\n * - Revalidates in the background using the configured fetchUser function\n * - Automatically syncs across all components using this hook\n *\n * @example\n * ```tsx\n * const { user, isLoading, isAuthenticated } = useUser<MyUser>();\n *\n * if (isLoading) return <Spinner />;\n * if (!isAuthenticated) return <LoginPage />;\n * return <Dashboard user={user} />;\n * ```\n *\n * @example Distinguish user-change sources\n * ```tsx\n * const { user, lastChangeSource } = useUser();\n *\n * useEffect(() => {\n * // Only auto-redirect when we detect an *existing* session on mount,\n * // not when the user just pressed the \"Login\" button (the login form\n * // is responsible for its own redirect there).\n * if (user && lastChangeSource === 'initial') {\n * showRedirectOverlay();\n * }\n * }, [user, lastChangeSource]);\n * ```\n */\nexport function useUser<T extends User = User>(): UseUserReturn<T> {\n const { tokenManager, stateMachine, config, emitter, userChangeHint } = useAuthContext();\n\n // ── lastError 状态管理 ──────────────────────────────────────\n const lastErrorRef = useRef<Error | undefined>(undefined);\n const retryCountRef = useRef(0);\n const [, setTick] = useState(0);\n const forceUpdate = useCallback(() => setTick((t) => t + 1), []);\n\n const clearError = useCallback(() => {\n lastErrorRef.current = undefined;\n forceUpdate();\n }, [forceUpdate]);\n\n // ── user-change 事件:previousUser 跟踪 + lastChangeSource 状态 ──\n // Use a sentinel (`undefined`) to mean \"never observed a value yet\",\n // separate from `null` which means \"observed and was unauthenticated\".\n const previousUserRef = useRef<T | null | undefined>(undefined);\n const [lastChangeEvent, setLastChangeEvent] = useState<UserChangeEvent<T> | null>(null);\n\n const fetcher = async (): Promise<T | null> => {\n const token = tokenManager.getAccessToken();\n if (!token) return null;\n\n if (tokenManager.isExpired()) {\n try {\n await tokenManager.refresh();\n } catch {\n // refresh 失败,但 token 可能仍然有效(如 cookie-based 场景)\n // 如果 token 已被清除则放弃,否则继续尝试 fetchUser 让服务端验证\n const remainingToken = tokenManager.getAccessToken();\n if (!remainingToken) return null;\n }\n }\n\n if (config.fetchUser) {\n const currentToken = tokenManager.getAccessToken();\n if (!currentToken) return null;\n return (await config.fetchUser(currentToken)) as T;\n }\n\n return null;\n };\n\n const {\n data,\n error,\n isLoading,\n mutate: swrMutate,\n } = useSWR<T | null>(AUTH_KEY, fetcher, {\n revalidateOnFocus: config.swrOptions?.revalidateOnFocus ?? true,\n revalidateOnReconnect: config.swrOptions?.revalidateOnReconnect ?? true,\n shouldRetryOnError: false,\n ...(config.swrOptions?.dedupingInterval !== undefined && {\n dedupingInterval: config.swrOptions.dedupingInterval,\n }),\n ...(config.swrOptions?.focusThrottleInterval !== undefined && {\n focusThrottleInterval: config.swrOptions.focusThrottleInterval,\n }),\n ...(config.swrOptions?.refreshInterval !== undefined && {\n refreshInterval: config.swrOptions.refreshInterval,\n }),\n });\n\n // ── user-change 派发(每当 SWR 的 data 稳定为 user/null 时触发一次)──\n useEffect(() => {\n // SWR 尚未完成首次解析:data === undefined → 跳过\n if (data === undefined) return;\n\n const prev = previousUserRef.current;\n // 值未变化(含对象引用 + null → null)→ 不派发\n if (Object.is(prev, data)) return;\n\n // 选择 source:\n // 1) 首次(prev === undefined)→ 'initial'\n // 2) Provider 近期标记了 hint(TTL 内)→ 使用 hint.source\n // 3) 其余一律视为被动 revalidate\n let source: UserChangeSource;\n if (prev === undefined) {\n source = 'initial';\n } else {\n const now = Date.now();\n const hintFresh =\n userChangeHint.source !== null && now - userChangeHint.timestamp <= USER_CHANGE_HINT_TTL_MS;\n source = hintFresh ? (userChangeHint.source as UserChangeSource) : 'revalidate';\n }\n\n // NOTE: We intentionally do NOT clear the hint here. Multiple useUser()\n // consumers may each observe the same SWR data change within the same\n // tick; all of them should see the same source. The hint will naturally\n // expire via TTL before the next unrelated SWR revalidate arrives.\n\n const event: UserChangeEvent<T> = {\n source,\n user: data,\n previousUser: prev,\n timestamp: Date.now(),\n };\n\n previousUserRef.current = data;\n setLastChangeEvent(event);\n emitter.emit('user-change', event as UserChangeEvent);\n }, [data, emitter, userChangeHint]);\n\n // ── 同步 lastError + onFetchUserError 回调 ─────────────────\n useEffect(() => {\n if (error) {\n lastErrorRef.current = error;\n forceUpdate();\n\n // 调用 onFetchUserError 回调(如果已配置)\n if (config.onFetchUserError) {\n const action = config.onFetchUserError(error);\n\n if (action === 'retry' && retryCountRef.current < 1) {\n retryCountRef.current += 1;\n // 触发 SWR 重新请求\n swrMutate();\n } else if (action === 'logout') {\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n }\n // 'ignore' → 仅保留 lastError,不做额外操作\n }\n } else if (data !== undefined && !error) {\n // fetchUser 成功(data 可以是 null 或 user),重置 lastError 和重试计数\n if (lastErrorRef.current !== undefined) {\n lastErrorRef.current = undefined;\n forceUpdate();\n }\n retryCountRef.current = 0;\n }\n }, [error, data, forceUpdate, config, tokenManager, stateMachine, swrMutate]);\n\n const mutate = async (newData?: T | null): Promise<void> => {\n if (newData === undefined) {\n await swrMutate();\n } else {\n await swrMutate(newData, { revalidate: false });\n }\n };\n\n return {\n user: data ?? null,\n isLoading,\n isAuthenticated: !!data,\n error,\n lastError: lastErrorRef.current,\n clearError,\n mutate,\n lastChangeSource: lastChangeEvent?.source ?? null,\n lastChangeEvent,\n };\n}\n\n/** SWR cache key used for user data. Exported for advanced usage. */\nexport { AUTH_KEY };\n","import type { AuthResponse } from '@swr-login/core';\nimport { isMultiStepPlugin } from '@swr-login/core';\nimport { useCallback, useState } from 'react';\nimport { mutate as swrGlobalMutate } from 'swr';\nimport { useAuthContext } from '../context';\nimport { AUTH_KEY } from './useUser';\n\nexport interface UseLoginOptions {\n /** Plugin name to use for login */\n pluginName?: string;\n}\n\nexport interface UseLoginReturn<TCredentials = unknown> {\n /**\n * Trigger login with specified credentials.\n * If pluginName was not provided in options, it must be passed as first argument.\n */\n login: (\n credentialsOrPluginName: TCredentials | string,\n credentials?: TCredentials,\n ) => Promise<AuthResponse>;\n /** Whether a login request is in progress */\n isLoading: boolean;\n /** Last login error, if any */\n error: Error | null;\n /** Reset error state */\n reset: () => void;\n}\n\n/**\n * Hook to trigger login flow via a registered plugin.\n *\n * @param pluginName - Optional default plugin name\n *\n * @example\n * ```tsx\n * // With default plugin\n * const { login, isLoading, error } = useLogin('password');\n * await login({ username: 'alice', password: 'secret' });\n *\n * // Without default plugin (specify at call time)\n * const { login } = useLogin();\n * await login('oauth-google', { redirect: false });\n * ```\n */\nexport function useLogin<TCredentials = unknown>(\n pluginName?: string,\n): UseLoginReturn<TCredentials> {\n const { pluginManager, tokenManager, stateMachine, config } = useAuthContext();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const login = useCallback(\n async (\n credentialsOrPluginName: TCredentials | string,\n maybeCredentials?: TCredentials,\n ): Promise<AuthResponse> => {\n let resolvedPlugin: string;\n let resolvedCredentials: TCredentials;\n\n if (typeof credentialsOrPluginName === 'string' && !pluginName) {\n resolvedPlugin = credentialsOrPluginName;\n resolvedCredentials = (maybeCredentials ?? {}) as TCredentials;\n } else if (pluginName) {\n resolvedPlugin = pluginName;\n resolvedCredentials = credentialsOrPluginName as TCredentials;\n } else {\n throw new Error(\n '[swr-login] Plugin name is required. Provide it to useLogin() or login().',\n );\n }\n\n setIsLoading(true);\n setError(null);\n stateMachine.transition('authenticating');\n\n // 检查是否为多步骤插件,给出友好错误提示\n const targetPlugin = pluginManager.getPlugin(resolvedPlugin);\n if (targetPlugin && isMultiStepPlugin(targetPlugin)) {\n const err = new Error(\n `[swr-login] Plugin \"${resolvedPlugin}\" is a multi-step plugin. Use useMultiStepLogin() instead of useLogin() for multi-step login flows.`,\n );\n setError(err);\n stateMachine.transition('error');\n setIsLoading(false);\n throw err;\n }\n\n try {\n const response = await pluginManager.login(resolvedPlugin, resolvedCredentials);\n\n // ── afterAuth:在 plugin 成功后、fetchUser 之前执行自定义钩子 ──\n let shouldSkipFetchUser = false;\n if (config.afterAuth) {\n try {\n await config.afterAuth({\n pluginName: resolvedPlugin,\n authResponse: response,\n skipFetchUser: () => {\n shouldSkipFetchUser = true;\n },\n });\n } catch (afterAuthErr) {\n // afterAuth 抛错:回滚 token,login() reject\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n throw afterAuthErr;\n }\n }\n\n // ── validateUserOnLogin:在 plugin 成功后调用 fetchUser 验证用户状态 ──\n if (!shouldSkipFetchUser && config.fetchUser && config.validateUserOnLogin !== false) {\n try {\n const user = await config.fetchUser(response.accessToken);\n // 将 fetchUser 返回的用户写入 SWR 缓存,避免 useUser 重复请求\n await swrGlobalMutate(AUTH_KEY, user, { revalidate: false });\n } catch (fetchUserErr) {\n // fetchUser 失败:回滚 token,转为 unauthenticated\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n throw fetchUserErr;\n }\n }\n\n stateMachine.transition('authenticated');\n\n // Update cache adapter if available\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n\n return response;\n } catch (err) {\n const authError = err instanceof Error ? err : new Error('Login failed');\n setError(authError);\n stateMachine.transition('error');\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, tokenManager, stateMachine, config, pluginName],\n );\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return { login, isLoading, error, reset };\n}\n","import type { AuthResponse } from '@swr-login/core';\nimport { useCallback, useRef, useState } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface UseMultiStepLoginReturn<TCredentials = unknown> {\n /** 当前步骤索引(从 0 开始,-1 表示未开始) */\n currentStep: number;\n /** 当前步骤名称(未开始时为 null) */\n currentStepName: string | null;\n /** 总步骤数 */\n totalSteps: number;\n /** 启动流程(执行第一步) */\n start: (credentials: TCredentials) => Promise<void>;\n /** 继续下一步 */\n next: (input: unknown) => Promise<void>;\n /** 回到上一步(仅 UI 层回退,不重新执行步骤逻辑) */\n back: () => void;\n /** 取消整个流程,重置状态 */\n cancel: () => void;\n /** 当前步骤的输出数据(传给 UI 渲染) */\n stepData: unknown;\n /** 是否正在执行当前步骤 */\n isLoading: boolean;\n /** 当前步骤的错误 */\n error: Error | null;\n /** 重置错误状态 */\n reset: () => void;\n /** 流程是否已完成 */\n isComplete: boolean;\n /** 最终的 AuthResponse(流程完成后可用) */\n authResponse: AuthResponse | null;\n}\n\n/**\n * Hook 用于驱动多步骤登录插件的流程。\n *\n * 提供步骤状态管理、前进/后退/取消等操作,自动在最后一步完成后\n * 调用 finalizeAuth 并更新全局登录态。\n *\n * @param pluginName - 多步骤插件名称\n *\n * @example\n * ```tsx\n * function ClassCodeLoginFlow() {\n * const {\n * currentStepName, stepData, start, next, back, isLoading, error, isComplete,\n * } = useMultiStepLogin<{ classCode: string; loginCode: string }>('class-code');\n *\n * if (isComplete) {\n * return <Redirect to=\"/dashboard\" />;\n * }\n *\n * // 步骤 1:输入班级码\n * if (currentStepName === 'verify-code' || currentStep === -1) {\n * return <ClassCodeForm onSubmit={start} isLoading={isLoading} error={error} />;\n * }\n *\n * // 步骤 2:选择学生\n * if (currentStepName === 'select-student') {\n * return (\n * <StudentList\n * students={stepData.students}\n * onSelect={(userId) => next({ userId, classLoginToken: stepData.classLoginToken })}\n * onBack={back}\n * isLoading={isLoading}\n * />\n * );\n * }\n *\n * // 步骤 3:获取 Token(自动执行,显示 loading)\n * if (currentStepName === 'get-token') {\n * return <LoadingSpinner text=\"登录中...\" />;\n * }\n * }\n * ```\n */\nexport function useMultiStepLogin<TCredentials = unknown>(\n pluginName: string,\n): UseMultiStepLoginReturn<TCredentials> {\n const { pluginManager, stateMachine, config } = useAuthContext();\n\n const [currentStep, setCurrentStep] = useState(-1); // -1 表示未开始\n const [stepData, setStepData] = useState<unknown>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [isComplete, setIsComplete] = useState(false);\n const [authResponse, setAuthResponse] = useState<AuthResponse | null>(null);\n\n // 用 ref 保存中间步骤的累积数据,避免闭包问题\n const stepOutputsRef = useRef<unknown[]>([]);\n\n const steps = pluginManager.getSteps(pluginName);\n const totalSteps = steps?.length ?? 0;\n\n const currentStepName = currentStep >= 0 && steps ? (steps[currentStep]?.name ?? null) : null;\n\n const executeStepAt = useCallback(\n async (stepIndex: number, input: unknown) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const output = await pluginManager.executeStep(pluginName, stepIndex, input);\n stepOutputsRef.current[stepIndex] = output;\n setStepData(output);\n setCurrentStep(stepIndex);\n\n // 如果是最后一步,执行 finalizeAuth\n if (stepIndex === totalSteps - 1) {\n const response = await pluginManager.finalizeMultiStep(pluginName, output);\n stateMachine.transition('authenticated');\n\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n\n setAuthResponse(response);\n setIsComplete(true);\n }\n } catch (err) {\n const authError = err instanceof Error ? err : new Error('Step execution failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, pluginName, totalSteps, stateMachine, config],\n );\n\n const start = useCallback(\n async (credentials: TCredentials) => {\n // 重置状态\n stepOutputsRef.current = [];\n setCurrentStep(-1);\n setStepData(null);\n setIsComplete(false);\n setAuthResponse(null);\n setError(null);\n stateMachine.transition('authenticating');\n\n await executeStepAt(0, credentials);\n },\n [executeStepAt, stateMachine],\n );\n\n const next = useCallback(\n async (input: unknown) => {\n const nextIndex = currentStep + 1;\n if (nextIndex >= totalSteps) {\n throw new Error('[swr-login] No more steps to execute.');\n }\n await executeStepAt(nextIndex, input);\n },\n [currentStep, totalSteps, executeStepAt],\n );\n\n const back = useCallback(() => {\n if (currentStep > 0) {\n const prevStep = currentStep - 1;\n setCurrentStep(prevStep);\n setStepData(stepOutputsRef.current[prevStep] ?? null);\n setError(null);\n }\n }, [currentStep]);\n\n const cancel = useCallback(() => {\n setCurrentStep(-1);\n setStepData(null);\n setIsLoading(false);\n setError(null);\n setIsComplete(false);\n setAuthResponse(null);\n stepOutputsRef.current = [];\n stateMachine.transition('unauthenticated');\n }, [stateMachine]);\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return {\n currentStep,\n currentStepName,\n totalSteps,\n start,\n next,\n back,\n cancel,\n stepData,\n isLoading,\n error,\n reset,\n isComplete,\n authResponse,\n };\n}\n","import type { AuthInjector, AuthResponse } from '@swr-login/core';\nimport { useCallback } from 'react';\nimport { useAuthContext } from '../context';\n\n/**\n * Hook 用于从外部注入登录态到 swr-login 体系。\n *\n * 适用场景:\n * - 多步骤登录流程(如班级码登录、MFA)\n * - 第三方 SDK 登录完成后同步状态\n * - iframe / WebView 登录回调\n *\n * @example\n * ```tsx\n * function ExternalLoginCallback() {\n * const { injectAuth } = useAuthInjector();\n *\n * const handleLoginComplete = async (token: string, user: User) => {\n * await injectAuth({\n * user,\n * accessToken: token,\n * expiresAt: Date.now() + 86400000,\n * });\n * router.push('/dashboard');\n * };\n * }\n * ```\n */\nexport function useAuthInjector(): AuthInjector {\n const { tokenManager, emitter, stateMachine, config } = useAuthContext();\n\n const injectAuth = useCallback(\n async (response: AuthResponse): Promise<void> => {\n // 1. 存储 token\n tokenManager.setTokens({\n accessToken: response.accessToken,\n refreshToken: response.refreshToken,\n expiresAt: response.expiresAt,\n });\n\n // 2. 状态机转换为已认证。\n //\n // injectAuth 作为\"逃生舱\"应能从任何状态恢复到 authenticated,\n // 但状态机不允许 `error -> authenticated` / `unauthenticated -> authenticated` 直接转换。\n // 这两种状态都允许 `-> authenticating`,而 `authenticating -> authenticated` 合法,\n // 因此通过 `authenticating` 中转一次即可完成恢复,且每一步都符合状态机契约。\n const currentState = stateMachine.state;\n if (currentState === 'error' || currentState === 'unauthenticated') {\n stateMachine.transition('authenticating');\n }\n stateMachine.transition('authenticated');\n\n // 3. 发射登录事件(触发 onLogin 回调、跨标签页同步等)\n emitter.emit('login', { user: response.user });\n\n // 4. 更新缓存(使 useUser() 等 Hook 感知到用户信息)\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n },\n [tokenManager, emitter, stateMachine, config],\n );\n\n const injectLogout = useCallback(async (): Promise<void> => {\n // 1. 清除 token\n tokenManager.clearTokens();\n\n // 2. 状态机转换为未认证\n stateMachine.transition('unauthenticated');\n\n // 3. 发射登出事件\n emitter.emit('logout', undefined);\n\n // 4. 清除缓存\n if (config.cacheAdapter) {\n await config.cacheAdapter.clearUser();\n }\n }, [tokenManager, emitter, stateMachine, config]);\n\n return { injectAuth, injectLogout };\n}\n","import type { User, UserChangeEvent, UserChangeSource } from '@swr-login/core';\nimport { useEffect, useRef, useState } from 'react';\nimport { useAuthContext } from '../context';\n\n/**\n * Subscribe to `user-change` events as a **discrete event stream**.\n *\n * Unlike `useUser().user` (which reflects the *current* user value), this\n * hook returns the most recent transition event and re-renders the caller\n * only when a new transition occurs. It's intended for `useEffect`-driven\n * side effects that care about *when* and *why* the user changed rather\n * than the current user value itself.\n *\n * Returns `null` until the first transition is observed by the Provider.\n *\n * @typeParam T - Concrete user type (defaults to base `User`)\n *\n * @example\n * ```tsx\n * const change = useUserChange<MyUser>();\n *\n * useEffect(() => {\n * if (change?.source === 'login') {\n * toast.success(`Welcome back, ${change.user?.name}!`);\n * }\n * }, [change]);\n * ```\n *\n * @example Filter by source\n * ```tsx\n * const change = useUserChange();\n * useEffect(() => {\n * if (change?.source === 'external') {\n * // Another tab just logged in / out — refresh local UI\n * refreshSidebar();\n * }\n * }, [change]);\n * ```\n */\nexport function useUserChange<T extends User = User>(): UserChangeEvent<T> | null {\n const { emitter } = useAuthContext();\n const [event, setEvent] = useState<UserChangeEvent<T> | null>(null);\n\n useEffect(() => {\n const unsubscribe = emitter.on('user-change', (payload) => {\n setEvent(payload as UserChangeEvent<T>);\n });\n return unsubscribe;\n }, [emitter]);\n\n return event;\n}\n\n/**\n * Register a side-effect callback that fires on every `user-change` event,\n * **without** triggering a re-render of the calling component.\n *\n * Ideal for analytics / logging / imperative side effects that don't need\n * to participate in React's render cycle.\n *\n * The callback receives the full `UserChangeEvent` (including `previousUser`,\n * `timestamp`, and `source`). The listener is automatically unsubscribed on\n * unmount.\n *\n * Note: the callback reference is stored in a ref and always called with\n * the latest closure — you do NOT need to memoize it with `useCallback`.\n *\n * @example Analytics\n * ```tsx\n * useUserChangeEffect((e) => {\n * if (e.source === 'login') {\n * analytics.track('user_login', { userId: e.user?.id });\n * }\n * if (e.source === 'logout') {\n * analytics.track('user_logout', { userId: e.previousUser?.id });\n * }\n * });\n * ```\n *\n * @example Filter sources\n * ```tsx\n * useUserChangeEffect((e) => {\n * // Only react to passive revalidations that flipped the user to null\n * // (e.g. server returned 401 → session expired silently).\n * if (e.source === 'revalidate' && e.user === null && e.previousUser) {\n * showReLoginPrompt();\n * }\n * });\n * ```\n */\nexport function useUserChangeEffect<T extends User = User>(\n listener: (event: UserChangeEvent<T>) => void,\n): void {\n const { emitter } = useAuthContext();\n\n // Store latest callback in a ref so consumers don't need useCallback.\n const listenerRef = useRef(listener);\n listenerRef.current = listener;\n\n useEffect(() => {\n const unsubscribe = emitter.on('user-change', (payload) => {\n try {\n listenerRef.current(payload as UserChangeEvent<T>);\n } catch (err) {\n console.error('[swr-login] Error in useUserChangeEffect listener:', err);\n }\n });\n return unsubscribe;\n }, [emitter]);\n}\n\n/**\n * Convenience hook: fires the callback only when the change source matches\n * the given filter. Thin wrapper around {@link useUserChangeEffect}.\n *\n * @example\n * ```tsx\n * // Only run on explicit login (not on cold-start initial load)\n * useUserChangeOn('login', (e) => router.push('/dashboard'));\n *\n * // Subscribe to multiple sources at once\n * useUserChangeOn(['login', 'external'], (e) => refreshSidebar());\n * ```\n */\nexport function useUserChangeOn<T extends User = User>(\n source: UserChangeSource | UserChangeSource[],\n listener: (event: UserChangeEvent<T>) => void,\n): void {\n const sources = Array.isArray(source) ? source : [source];\n useUserChangeEffect<T>((event) => {\n if (sources.includes(event.source)) {\n listener(event);\n }\n });\n}\n","import { useCallback, useState } from 'react';\nimport { mutate } from 'swr';\nimport { useAuthContext } from '../context';\nimport { AUTH_KEY } from './useUser';\n\nexport interface UseLogoutOptions {\n /** Specific plugin to call logout on */\n pluginName?: string;\n /** Whether to broadcast logout to other tabs (default: true) */\n broadcast?: boolean;\n}\n\nexport interface UseLogoutReturn {\n /** Execute logout */\n logout: (options?: UseLogoutOptions) => Promise<void>;\n /** Whether logout is in progress */\n isLoading: boolean;\n}\n\n/**\n * Hook to perform secure logout with cross-tab sync.\n *\n * Logout flow:\n * 1. Call plugin logout (if applicable)\n * 2. Clear stored tokens\n * 3. Clear SWR cache (mutate user to null)\n * 4. Broadcast logout to other tabs\n * 5. Transition state to 'unauthenticated'\n *\n * @example\n * ```tsx\n * const { logout, isLoading } = useLogout();\n *\n * <button onClick={() => logout()} disabled={isLoading}>\n * Sign Out\n * </button>\n * ```\n */\nexport function useLogout(): UseLogoutReturn {\n const { pluginManager, stateMachine, broadcastSync } = useAuthContext();\n const [isLoading, setIsLoading] = useState(false);\n\n const logout = useCallback(\n async (options?: UseLogoutOptions) => {\n setIsLoading(true);\n\n try {\n // Call plugin logout\n await pluginManager.logout(options?.pluginName);\n\n // Clear SWR cache\n await mutate(AUTH_KEY, null, { revalidate: false });\n\n // Update state\n stateMachine.transition('unauthenticated');\n\n // Broadcast to other tabs\n const shouldBroadcast = options?.broadcast !== false;\n if (shouldBroadcast && broadcastSync) {\n broadcastSync.send('LOGOUT');\n }\n } catch (err) {\n console.error('[swr-login] Logout error:', err);\n // Even if logout API fails, clear local state\n await mutate(AUTH_KEY, null, { revalidate: false });\n stateMachine.transition('unauthenticated');\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, stateMachine, broadcastSync],\n );\n\n return { logout, isLoading };\n}\n","import type { TokenAdapter } from '@swr-login/core';\nimport { useCallback } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface UseAdapterReturn {\n /**\n * Synchronously check whether the adapter currently holds a valid auth credential.\n *\n * Uses `adapter.hasAuth()` if the adapter implements it,\n * otherwise falls back to `adapter.getAccessToken() !== null`.\n *\n * This is a **synchronous, non-reactive** check — it reads the adapter's\n * current state at call time. It does NOT trigger re-renders when the\n * underlying token changes. For reactive auth state, use `useUser()`.\n *\n * Typical use cases:\n * - Conditional rendering before SWR hydration completes\n * - Route guard pre-checks\n * - Analytics / logging\n *\n * @example\n * ```tsx\n * const { hasAuth } = useAdapter();\n *\n * // 首页自动跳转\n * useEffect(() => {\n * if (hasAuth()) {\n * router.push('/dashboard');\n * }\n * }, []);\n * ```\n */\n hasAuth: () => boolean;\n\n /**\n * Direct reference to the underlying `TokenAdapter` instance.\n *\n * Escape hatch for advanced scenarios where you need low-level\n * access to token storage (e.g. reading refresh token, manually\n * clearing tokens outside of the normal logout flow).\n *\n * Prefer `hasAuth()` and the higher-level hooks (`useUser`, `useLogin`)\n * for most use cases.\n */\n adapter: TokenAdapter;\n}\n\n/**\n * Hook to access the token adapter and its utility methods.\n *\n * Provides a convenient `hasAuth()` check that works synchronously,\n * useful for UI decisions that need to happen before SWR data is available.\n *\n * @example\n * ```tsx\n * function HomePage() {\n * const { hasAuth } = useAdapter();\n * const router = useRouter();\n *\n * useEffect(() => {\n * if (hasAuth()) {\n * router.push('/dashboard');\n * }\n * }, []);\n *\n * return <LoginPage />;\n * }\n * ```\n */\nexport function useAdapter(): UseAdapterReturn {\n const { config } = useAuthContext();\n const adapter = config.adapter;\n\n const hasAuth = useCallback((): boolean => {\n if (typeof adapter.hasAuth === 'function') {\n return adapter.hasAuth();\n }\n // Fallback: 检查 accessToken 是否存在\n return adapter.getAccessToken() !== null;\n }, [adapter]);\n\n return { hasAuth, adapter };\n}\n","import { isTokenExpired } from '@swr-login/core';\nimport { useMemo } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface SessionInfo {\n /** Current access token */\n accessToken: string | null;\n /** Current refresh token */\n refreshToken: string | null;\n /** Token expiration timestamp (ms since epoch) */\n expiresAt: number | null;\n /** Whether the access token has expired */\n isExpired: boolean;\n /** Whether tokens exist (regardless of expiry) */\n hasTokens: boolean;\n}\n\n/**\n * Hook to access raw session/token information.\n *\n * Useful for:\n * - Adding Authorization headers to custom requests\n * - Checking token expiry status\n * - Debugging session state\n *\n * @example\n * ```tsx\n * const { accessToken, isExpired } = useSession();\n *\n * const fetchData = () => fetch('/api/data', {\n * headers: { Authorization: `Bearer ${accessToken}` },\n * });\n * ```\n */\nexport function useSession(): SessionInfo {\n const { tokenManager } = useAuthContext();\n\n return useMemo(() => {\n const accessToken = tokenManager.getAccessToken();\n const refreshToken = tokenManager.getRefreshToken();\n const expiresAt = tokenManager.getAccessToken() ? tokenManager.getExpiresAt() : null;\n\n return {\n accessToken,\n refreshToken,\n expiresAt,\n isExpired: isTokenExpired(expiresAt),\n hasTokens: !!accessToken,\n };\n }, [tokenManager]);\n}\n","import { useCallback } from 'react';\nimport { useUser } from './useUser';\n\nexport interface UsePermissionReturn {\n /** Check if user has a specific permission */\n hasPermission: (permission: string) => boolean;\n /** Check if user has a specific role */\n hasRole: (role: string) => boolean;\n /** Check if user has all specified permissions */\n hasAllPermissions: (permissions: string[]) => boolean;\n /** Check if user has any of the specified permissions */\n hasAnyPermission: (permissions: string[]) => boolean;\n /** Check if user has all specified roles */\n hasAllRoles: (roles: string[]) => boolean;\n /** Check if user has any of the specified roles */\n hasAnyRole: (roles: string[]) => boolean;\n}\n\n/**\n * Hook for checking user permissions and roles.\n *\n * Reads from the `permissions` and `roles` arrays on the User object.\n *\n * @example\n * ```tsx\n * const { hasPermission, hasRole } = usePermission();\n *\n * if (hasPermission('admin:write')) {\n * // Show admin controls\n * }\n *\n * if (hasRole('editor')) {\n * // Show editor tools\n * }\n * ```\n */\nexport function usePermission(): UsePermissionReturn {\n const { user } = useUser();\n\n const hasPermission = useCallback(\n (permission: string): boolean => {\n return user?.permissions?.includes(permission) ?? false;\n },\n [user],\n );\n\n const hasRole = useCallback(\n (role: string): boolean => {\n return user?.roles?.includes(role) ?? false;\n },\n [user],\n );\n\n const hasAllPermissions = useCallback(\n (permissions: string[]): boolean => {\n if (!user?.permissions) return false;\n return permissions.every((p) => user.permissions?.includes(p));\n },\n [user],\n );\n\n const hasAnyPermission = useCallback(\n (permissions: string[]): boolean => {\n if (!user?.permissions) return false;\n return permissions.some((p) => user.permissions?.includes(p));\n },\n [user],\n );\n\n const hasAllRoles = useCallback(\n (roles: string[]): boolean => {\n if (!user?.roles) return false;\n return roles.every((r) => user.roles?.includes(r));\n },\n [user],\n );\n\n const hasAnyRole = useCallback(\n (roles: string[]): boolean => {\n if (!user?.roles) return false;\n return roles.some((r) => user.roles?.includes(r));\n },\n [user],\n );\n\n return {\n hasPermission,\n hasRole,\n hasAllPermissions,\n hasAnyPermission,\n hasAllRoles,\n hasAnyRole,\n };\n}\n","import type React from 'react';\nimport { usePermission } from '../hooks/usePermission';\nimport { useUser } from '../hooks/useUser';\n\nexport interface AuthGuardProps {\n children: React.ReactNode;\n /** Required permissions (checked against user.permissions) */\n permissions?: string[];\n /** Required roles (checked against user.roles) */\n roles?: string[];\n /** If true, ALL permissions/roles must match. If false, ANY match suffices. (default: false) */\n requireAll?: boolean;\n /** Component to render when user is not authenticated or lacks permissions */\n fallback?: React.ReactNode;\n /** Component to render while checking auth status */\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Declarative auth guard component.\n * Protects child content based on authentication state and permissions/roles.\n *\n * @example\n * ```tsx\n * <AuthGuard\n * permissions={['admin', 'editor']}\n * requireAll={false}\n * fallback={<Navigate to=\"/login\" />}\n * loadingComponent={<Skeleton />}\n * >\n * <AdminPanel />\n * </AuthGuard>\n * ```\n */\nexport function AuthGuard({\n children,\n permissions,\n roles,\n requireAll = false,\n fallback = null,\n loadingComponent = null,\n}: AuthGuardProps) {\n const { isLoading, isAuthenticated } = useUser();\n const { hasAllPermissions, hasAnyPermission, hasAllRoles, hasAnyRole } = usePermission();\n\n if (isLoading) {\n return <>{loadingComponent}</>;\n }\n\n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n const hasRequiredPermissions = requireAll\n ? hasAllPermissions(permissions)\n : hasAnyPermission(permissions);\n\n if (!hasRequiredPermissions) {\n return <>{fallback}</>;\n }\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n const hasRequiredRoles = requireAll ? hasAllRoles(roles) : hasAnyRole(roles);\n\n if (!hasRequiredRoles) {\n return <>{fallback}</>;\n }\n }\n\n return <>{children}</>;\n}\n"]}
1
+ {"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/hooks/useUser.ts","../src/hooks/useLogin.ts","../src/hooks/useMultiStepLogin.ts","../src/hooks/useAuthInjector.ts","../src/hooks/useUserChange.ts","../src/hooks/useLogout.ts","../src/hooks/useAdapter.ts","../src/hooks/useSession.ts","../src/hooks/usePermission.ts","../src/components/AuthGuard.tsx"],"names":["useRef","useEffect","mutate","useState","useCallback","swrGlobalMutate","useMemo","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n AuthEventEmitter,\n AuthStateMachine,\n BroadcastSync,\n PluginManager,\n SWRLoginConfig,\n TokenManager,\n UserChangeSource,\n} from '@swr-login/core';\nimport { createContext, useContext } from 'react';\n\n/**\n * Mutable hint object shared between the Provider and `useUser()` to describe\n * *why* the next user-change event is expected to happen.\n *\n * The Provider writes into this object as it observes `login` / `logout` /\n * `external` events; `useUser()` reads and clears it when the SWR cache value\n * actually transitions. Unset/expired hint → the change is a passive\n * `revalidate` or the very first `initial` load.\n *\n * Using a mutable ref-like object (instead of React state) avoids re-renders\n * and is safe because it is written synchronously from event callbacks and\n * read synchronously during `useUser()`'s `useEffect`.\n *\n * @internal\n */\nexport interface UserChangeHint {\n source: UserChangeSource | null;\n /** `Date.now()` when the hint was written. Stale hints (>1s) are ignored. */\n timestamp: number;\n}\n\n/** Internal context value passed through SWRLoginProvider */\nexport interface AuthContextValue {\n pluginManager: PluginManager;\n tokenManager: TokenManager;\n emitter: AuthEventEmitter;\n stateMachine: AuthStateMachine;\n broadcastSync: BroadcastSync | null;\n config: SWRLoginConfig;\n /** @internal shared hint for the next user-change source */\n userChangeHint: UserChangeHint;\n}\n\nexport const AuthContext = createContext<AuthContextValue | null>(null);\n\n/**\n * Internal hook to access auth context.\n * Must be used within SWRLoginProvider.\n * @throws Error if used outside of SWRLoginProvider\n */\nexport function useAuthContext(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) {\n throw new Error(\n '[swr-login] useAuthContext must be used within <SWRLoginProvider>. ' +\n 'Wrap your app with <SWRLoginProvider> to use swr-login hooks.',\n );\n }\n return ctx;\n}\n","import {\n AuthEventEmitter,\n AuthStateMachine,\n BroadcastSync,\n PluginManager,\n type SWRLoginConfig,\n TokenManager,\n} from '@swr-login/core';\nimport type React from 'react';\nimport { useEffect, useMemo, useRef } from 'react';\nimport { AuthContext, type AuthContextValue, type UserChangeHint } from './context';\n\nexport interface SWRLoginProviderProps {\n /** Authentication configuration */\n config: SWRLoginConfig;\n children: React.ReactNode;\n}\n\n/**\n * SWRLoginProvider initializes the auth system and provides context to all child hooks.\n *\n * @example\n * ```tsx\n * import { SWRLoginProvider } from '@swr-login/react';\n * import { JWTAdapter } from '@swr-login/adapter-jwt';\n * import { PasswordPlugin } from '@swr-login/plugin-password';\n *\n * function App() {\n * return (\n * <SWRLoginProvider\n * config={{\n * adapter: JWTAdapter(),\n * plugins: [PasswordPlugin({ loginUrl: '/api/login' })],\n * }}\n * >\n * <YourApp />\n * </SWRLoginProvider>\n * );\n * }\n * ```\n */\nexport function SWRLoginProvider({ config, children }: SWRLoginProviderProps) {\n const initializedRef = useRef(false);\n\n const contextValue = useMemo<AuthContextValue>(() => {\n const emitter = new AuthEventEmitter();\n const stateMachine = new AuthStateMachine(emitter);\n const tokenManager = new TokenManager(config.adapter, emitter, stateMachine);\n const pluginManager = new PluginManager(tokenManager, emitter);\n\n // Register plugins\n pluginManager.register(...config.plugins);\n\n // Set up broadcast sync if enabled\n const enableSync = config.security?.enableBroadcastSync !== false;\n const broadcastSync = enableSync && typeof window !== 'undefined' ? new BroadcastSync() : null;\n\n // Mutable hint object shared with useUser() to label the next user-change\n // event with its causal source (login/logout/external).\n const userChangeHint: UserChangeHint = { source: null, timestamp: 0 };\n const markHint = (source: UserChangeHint['source']) => {\n userChangeHint.source = source;\n userChangeHint.timestamp = Date.now();\n };\n\n // Label user-change source based on lifecycle events. The actual\n // 'user-change' event is emitted by useUser() once SWR produces a\n // different value; these hints only tell us *why* it's about to change.\n emitter.on('login', () => markHint('login'));\n emitter.on('logout', () => markHint('logout'));\n\n // Wire up lifecycle callbacks\n if (config.onLogin) {\n emitter.on('login', ({ user }) => config.onLogin?.(user));\n }\n if (config.onLogout) {\n emitter.on('logout', () => config.onLogout?.());\n }\n if (config.onError) {\n emitter.on('error', ({ error }) => config.onError?.(error));\n }\n\n return {\n pluginManager,\n tokenManager,\n emitter,\n stateMachine,\n broadcastSync,\n config,\n userChangeHint,\n };\n }, [config]);\n\n // Initialize plugins and broadcast sync on mount\n useEffect(() => {\n if (initializedRef.current) return;\n initializedRef.current = true;\n\n const {\n pluginManager,\n broadcastSync,\n tokenManager,\n stateMachine,\n emitter,\n config: cfg,\n } = contextValue;\n\n // Initialize all plugins\n pluginManager.initializeAll().catch((err) => {\n console.error('[swr-login] Plugin initialization error:', err);\n });\n\n // Check if user has existing token -> restore session\n const existingToken = tokenManager.getAccessToken();\n const expiresAt = tokenManager.getExpiresAt();\n\n if (existingToken && !tokenManager.isExpired()) {\n stateMachine.transition('authenticated');\n } else if (existingToken && expiresAt === null) {\n // expiresAt 未知(如外部登录只设置了 token 但未设置过期时间)\n // 乐观地认为已认证,让 fetchUser / SWR revalidate 来验证\n stateMachine.transition('authenticated');\n } else if (existingToken && tokenManager.isExpired()) {\n stateMachine.transition('unauthenticated');\n emitter.emit('token-expired', undefined);\n }\n\n // Listen for cross-tab events\n if (broadcastSync) {\n const { userChangeHint } = contextValue;\n const markExternal = () => {\n userChangeHint.source = 'external';\n userChangeHint.timestamp = Date.now();\n };\n\n const unsubscribe = broadcastSync.onMessage((message) => {\n switch (message.type) {\n case 'LOGOUT':\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n // Emit-after-mark would be overwritten by the synchronous\n // 'logout' handler (which sets hint='logout'); re-mark to\n // 'external' so useUser labels this cross-tab logout correctly.\n markExternal();\n break;\n case 'LOGIN':\n case 'TOKEN_REFRESH':\n // Cross-tab login / refresh → refresh SWR cache; label the\n // upcoming user-change as 'external'.\n markExternal();\n if (cfg.cacheAdapter) {\n cfg.cacheAdapter.revalidate();\n }\n break;\n }\n });\n\n return () => {\n unsubscribe();\n broadcastSync.destroy();\n };\n }\n }, [contextValue]);\n\n // Visibility change handler for security\n useEffect(() => {\n const { config: cfg, tokenManager, stateMachine, emitter } = contextValue;\n\n if (!cfg.security?.clearOnHidden || typeof document === 'undefined') return;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const handleVisibilityChange = () => {\n if (document.hidden) {\n const delay = cfg.security?.clearOnHiddenDelay ?? 300_000;\n timeoutId = setTimeout(() => {\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n }, delay);\n } else {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n if (timeoutId) clearTimeout(timeoutId);\n };\n }, [contextValue]);\n\n return <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>;\n}\n","import type { User, UserChangeEvent, UserChangeSource } from '@swr-login/core';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport useSWR from 'swr';\nimport { useAuthContext } from '../context';\n\nconst AUTH_KEY = '__swr_login_user__';\n\n/**\n * Time window (ms) during which a hint written by the Provider remains\n * valid. If SWR produces a change later than this after the hint was\n * written, we treat it as a passive `revalidate` instead.\n */\nconst USER_CHANGE_HINT_TTL_MS = 1000;\n\nexport interface UseUserReturn<T extends User = User> {\n /** Current authenticated user, or null if not logged in */\n user: T | null;\n /** Whether user data is being fetched */\n isLoading: boolean;\n /** Whether user is authenticated */\n isAuthenticated: boolean;\n /** Error from fetching user data */\n error: Error | undefined;\n /**\n * Sticky error from the last `fetchUser` failure.\n *\n * Unlike `error`, this value is **not** cleared when the auth state\n * transitions (e.g. from `authenticated` → `unauthenticated`).\n * It persists until `fetchUser` succeeds again or `clearError()` is called.\n *\n * Useful for distinguishing \"not logged in\" from \"account disabled\" in\n * AuthGuard or error-boundary components.\n */\n lastError: Error | undefined;\n /**\n * Manually reset `lastError` to `undefined`.\n */\n clearError: () => void;\n /**\n * Manually update cached user data.\n * Call with `null` to clear, or with a user object to update.\n */\n mutate: (data?: T | null) => Promise<void>;\n /**\n * Why the most recent `user` transition happened.\n *\n * - `null` — No user-change has been observed yet in this component\n * (e.g. before the first `fetchUser` resolves on mount).\n * - `'initial'` — First time the Provider observed a user value\n * (including `null` for unauthenticated cold start).\n * - `'login'` — Triggered by an explicit `login()` / multi-step finalize\n * / `injectAuth()` call.\n * - `'logout'` — Triggered by an explicit `logout()` / `injectLogout()`.\n * - `'revalidate'` — SWR background revalidation produced a different user.\n * - `'external'` — Cross-tab sync via BroadcastChannel / storage events.\n *\n * Use this to differentiate user-initiated transitions from passive ones,\n * e.g. to suppress a welcome toast on page refresh.\n */\n lastChangeSource: UserChangeSource | null;\n /**\n * Full event object for the most recent `user` transition, including\n * `previousUser` and `timestamp`. `null` before the first transition.\n *\n * See `lastChangeSource` for a quick discriminator.\n */\n lastChangeEvent: UserChangeEvent<T> | null;\n}\n\n/**\n * Hook to access current user data via SWR cache.\n *\n * Uses SWR's stale-while-revalidate strategy:\n * - Immediately returns cached user data\n * - Revalidates in the background using the configured fetchUser function\n * - Automatically syncs across all components using this hook\n *\n * @example\n * ```tsx\n * const { user, isLoading, isAuthenticated } = useUser<MyUser>();\n *\n * if (isLoading) return <Spinner />;\n * if (!isAuthenticated) return <LoginPage />;\n * return <Dashboard user={user} />;\n * ```\n *\n * @example Distinguish user-change sources\n * ```tsx\n * const { user, lastChangeSource } = useUser();\n *\n * useEffect(() => {\n * // Only auto-redirect when we detect an *existing* session on mount,\n * // not when the user just pressed the \"Login\" button (the login form\n * // is responsible for its own redirect there).\n * if (user && lastChangeSource === 'initial') {\n * showRedirectOverlay();\n * }\n * }, [user, lastChangeSource]);\n * ```\n */\nexport function useUser<T extends User = User>(): UseUserReturn<T> {\n const { tokenManager, stateMachine, config, emitter, userChangeHint } = useAuthContext();\n\n // ── lastError 状态管理 ──────────────────────────────────────\n const lastErrorRef = useRef<Error | undefined>(undefined);\n const retryCountRef = useRef(0);\n const [, setTick] = useState(0);\n const forceUpdate = useCallback(() => setTick((t) => t + 1), []);\n\n const clearError = useCallback(() => {\n lastErrorRef.current = undefined;\n forceUpdate();\n }, [forceUpdate]);\n\n // ── user-change 事件:previousUser 跟踪 + lastChangeSource 状态 ──\n // Use a sentinel (`undefined`) to mean \"never observed a value yet\",\n // separate from `null` which means \"observed and was unauthenticated\".\n const previousUserRef = useRef<T | null | undefined>(undefined);\n const [lastChangeEvent, setLastChangeEvent] = useState<UserChangeEvent<T> | null>(null);\n\n const fetcher = async (): Promise<T | null> => {\n const token = tokenManager.getAccessToken();\n if (!token) return null;\n\n if (tokenManager.isExpired()) {\n try {\n await tokenManager.refresh();\n } catch {\n // refresh 失败,但 token 可能仍然有效(如 cookie-based 场景)\n // 如果 token 已被清除则放弃,否则继续尝试 fetchUser 让服务端验证\n const remainingToken = tokenManager.getAccessToken();\n if (!remainingToken) return null;\n }\n }\n\n if (config.fetchUser) {\n const currentToken = tokenManager.getAccessToken();\n if (!currentToken) return null;\n return (await config.fetchUser(currentToken)) as T;\n }\n\n return null;\n };\n\n const {\n data,\n error,\n isLoading,\n mutate: swrMutate,\n } = useSWR<T | null>(AUTH_KEY, fetcher, {\n revalidateOnFocus: config.swrOptions?.revalidateOnFocus ?? true,\n revalidateOnReconnect: config.swrOptions?.revalidateOnReconnect ?? true,\n shouldRetryOnError: false,\n ...(config.swrOptions?.dedupingInterval !== undefined && {\n dedupingInterval: config.swrOptions.dedupingInterval,\n }),\n ...(config.swrOptions?.focusThrottleInterval !== undefined && {\n focusThrottleInterval: config.swrOptions.focusThrottleInterval,\n }),\n ...(config.swrOptions?.refreshInterval !== undefined && {\n refreshInterval: config.swrOptions.refreshInterval,\n }),\n });\n\n // ── user-change 派发(每当 SWR 的 data 稳定为 user/null 时触发一次)──\n useEffect(() => {\n // SWR 尚未完成首次解析:data === undefined → 跳过\n if (data === undefined) return;\n\n const prev = previousUserRef.current;\n // 值未变化(含对象引用 + null → null)→ 不派发\n if (Object.is(prev, data)) return;\n\n // 选择 source(优先级从高到低):\n // 1) Provider 近期标记了 hint(TTL 内)→ 使用 hint.source\n // hint 是由 emit('login') / emit('logout') / BroadcastChannel 等\n // 显式同步写入的\"权威信号\",必须优先于\"prev 是否存在\"启发式。\n // 否则在 SWR 首屏 fetch 未完成前用户就触发 login() 时,\n // 会把本应是 'login' 的事件错误地派发为 'initial'。\n // 2) 首次(prev === undefined)→ 'initial'\n // 3) 其余一律视为被动 revalidate\n let source: UserChangeSource;\n const now = Date.now();\n const hintFresh =\n userChangeHint.source !== null && now - userChangeHint.timestamp <= USER_CHANGE_HINT_TTL_MS;\n if (hintFresh) {\n source = userChangeHint.source as UserChangeSource;\n } else if (prev === undefined) {\n source = 'initial';\n } else {\n source = 'revalidate';\n }\n\n // NOTE: We intentionally do NOT clear the hint here. Multiple useUser()\n // consumers may each observe the same SWR data change within the same\n // tick; all of them should see the same source. The hint will naturally\n // expire via TTL before the next unrelated SWR revalidate arrives.\n\n const event: UserChangeEvent<T> = {\n source,\n user: data,\n previousUser: prev,\n timestamp: Date.now(),\n };\n\n previousUserRef.current = data;\n setLastChangeEvent(event);\n emitter.emit('user-change', event as UserChangeEvent);\n }, [data, emitter, userChangeHint]);\n\n // ── 同步 lastError + onFetchUserError 回调 ─────────────────\n useEffect(() => {\n if (error) {\n lastErrorRef.current = error;\n forceUpdate();\n\n // 调用 onFetchUserError 回调(如果已配置)\n if (config.onFetchUserError) {\n const action = config.onFetchUserError(error);\n\n if (action === 'retry' && retryCountRef.current < 1) {\n retryCountRef.current += 1;\n // 触发 SWR 重新请求\n swrMutate();\n } else if (action === 'logout') {\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n }\n // 'ignore' → 仅保留 lastError,不做额外操作\n }\n } else if (data !== undefined && !error) {\n // fetchUser 成功(data 可以是 null 或 user),重置 lastError 和重试计数\n if (lastErrorRef.current !== undefined) {\n lastErrorRef.current = undefined;\n forceUpdate();\n }\n retryCountRef.current = 0;\n }\n }, [error, data, forceUpdate, config, tokenManager, stateMachine, swrMutate]);\n\n const mutate = async (newData?: T | null): Promise<void> => {\n if (newData === undefined) {\n await swrMutate();\n } else {\n await swrMutate(newData, { revalidate: false });\n }\n };\n\n return {\n user: data ?? null,\n isLoading,\n isAuthenticated: !!data,\n error,\n lastError: lastErrorRef.current,\n clearError,\n mutate,\n lastChangeSource: lastChangeEvent?.source ?? null,\n lastChangeEvent,\n };\n}\n\n/** SWR cache key used for user data. Exported for advanced usage. */\nexport { AUTH_KEY };\n","import type { AuthResponse, LoginCallOptions } from '@swr-login/core';\nimport { isMultiStepPlugin } from '@swr-login/core';\nimport { useCallback, useState } from 'react';\nimport { mutate as swrGlobalMutate } from 'swr';\nimport { useAuthContext } from '../context';\nimport { AUTH_KEY } from './useUser';\n\nexport interface UseLoginOptions {\n /** Plugin name to use for login */\n pluginName?: string;\n}\n\nexport interface UseLoginReturn<TCredentials = unknown> {\n /**\n * Trigger login with specified credentials.\n * If pluginName was not provided in options, it must be passed as first argument.\n *\n * The `options` argument is forwarded to `PluginManager.login()`. Specifically,\n * `options.context` is exposed to the underlying plugin via\n * `PluginContext.loginContext` (opaque pass-through). Use it to disambiguate\n * concurrent login flows or pass per-call hints to plugin hooks\n * (e.g., `coding-auth-password`'s `onPreReset`) without resorting to\n * module-level mutable variables.\n *\n * @example\n * ```tsx\n * // With useLogin('password')\n * await login({ user, pass }, { context: { variant: 'teacher' } });\n *\n * // Without preset pluginName\n * await login('password', { user, pass }, { context: { variant: 'teacher' } });\n * ```\n */\n login: (\n credentialsOrPluginName: TCredentials | string,\n credentialsOrOptions?: TCredentials | LoginCallOptions,\n options?: LoginCallOptions,\n ) => Promise<AuthResponse>;\n /** Whether a login request is in progress */\n isLoading: boolean;\n /** Last login error, if any */\n error: Error | null;\n /** Reset error state */\n reset: () => void;\n}\n\n/**\n * Hook to trigger login flow via a registered plugin.\n *\n * @param pluginName - Optional default plugin name\n *\n * @example\n * ```tsx\n * // With default plugin\n * const { login, isLoading, error } = useLogin('password');\n * await login({ username: 'alice', password: 'secret' });\n *\n * // Without default plugin (specify at call time)\n * const { login } = useLogin();\n * await login('oauth-google', { redirect: false });\n * ```\n */\nexport function useLogin<TCredentials = unknown>(\n pluginName?: string,\n): UseLoginReturn<TCredentials> {\n const { pluginManager, tokenManager, stateMachine, config } = useAuthContext();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const login = useCallback(\n async (\n credentialsOrPluginName: TCredentials | string,\n credentialsOrOptions?: TCredentials | LoginCallOptions,\n maybeOptions?: LoginCallOptions,\n ): Promise<AuthResponse> => {\n let resolvedPlugin: string;\n let resolvedCredentials: TCredentials;\n let resolvedOptions: LoginCallOptions | undefined;\n\n if (typeof credentialsOrPluginName === 'string' && !pluginName) {\n // Form: login(pluginName, credentials?, options?)\n resolvedPlugin = credentialsOrPluginName;\n resolvedCredentials = (credentialsOrOptions ?? {}) as TCredentials;\n resolvedOptions = maybeOptions;\n } else if (pluginName) {\n // Form: login(credentials, options?)\n resolvedPlugin = pluginName;\n resolvedCredentials = credentialsOrPluginName as TCredentials;\n resolvedOptions = credentialsOrOptions as LoginCallOptions | undefined;\n } else {\n throw new Error(\n '[swr-login] Plugin name is required. Provide it to useLogin() or login().',\n );\n }\n\n setIsLoading(true);\n setError(null);\n stateMachine.transition('authenticating');\n\n // 检查是否为多步骤插件,给出友好错误提示\n const targetPlugin = pluginManager.getPlugin(resolvedPlugin);\n if (targetPlugin && isMultiStepPlugin(targetPlugin)) {\n const err = new Error(\n `[swr-login] Plugin \"${resolvedPlugin}\" is a multi-step plugin. Use useMultiStepLogin() instead of useLogin() for multi-step login flows.`,\n );\n setError(err);\n stateMachine.transition('error');\n setIsLoading(false);\n throw err;\n }\n\n try {\n const response = await pluginManager.login(\n resolvedPlugin,\n resolvedCredentials,\n resolvedOptions,\n );\n\n // ── afterAuth:在 plugin 成功后、fetchUser 之前执行自定义钩子 ──\n let shouldSkipFetchUser = false;\n if (config.afterAuth) {\n try {\n await config.afterAuth({\n pluginName: resolvedPlugin,\n authResponse: response,\n skipFetchUser: () => {\n shouldSkipFetchUser = true;\n },\n });\n } catch (afterAuthErr) {\n // afterAuth 抛错:回滚 token,login() reject\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n throw afterAuthErr;\n }\n }\n\n // ── validateUserOnLogin:在 plugin 成功后调用 fetchUser 验证用户状态 ──\n if (!shouldSkipFetchUser && config.fetchUser && config.validateUserOnLogin !== false) {\n try {\n const user = await config.fetchUser(response.accessToken);\n // 将 fetchUser 返回的用户写入 SWR 缓存,避免 useUser 重复请求\n await swrGlobalMutate(AUTH_KEY, user, { revalidate: false });\n } catch (fetchUserErr) {\n // fetchUser 失败:回滚 token,转为 unauthenticated\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n throw fetchUserErr;\n }\n }\n\n stateMachine.transition('authenticated');\n\n // Update cache adapter if available\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n\n return response;\n } catch (err) {\n const authError = err instanceof Error ? err : new Error('Login failed');\n setError(authError);\n stateMachine.transition('error');\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, tokenManager, stateMachine, config, pluginName],\n );\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return { login, isLoading, error, reset };\n}\n","import type { AuthResponse } from '@swr-login/core';\nimport { useCallback, useRef, useState } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface UseMultiStepLoginReturn<TCredentials = unknown> {\n /** 当前步骤索引(从 0 开始,-1 表示未开始) */\n currentStep: number;\n /** 当前步骤名称(未开始时为 null) */\n currentStepName: string | null;\n /** 总步骤数 */\n totalSteps: number;\n /** 启动流程(执行第一步) */\n start: (credentials: TCredentials) => Promise<void>;\n /** 继续下一步 */\n next: (input: unknown) => Promise<void>;\n /** 回到上一步(仅 UI 层回退,不重新执行步骤逻辑) */\n back: () => void;\n /** 取消整个流程,重置状态 */\n cancel: () => void;\n /** 当前步骤的输出数据(传给 UI 渲染) */\n stepData: unknown;\n /** 是否正在执行当前步骤 */\n isLoading: boolean;\n /** 当前步骤的错误 */\n error: Error | null;\n /** 重置错误状态 */\n reset: () => void;\n /** 流程是否已完成 */\n isComplete: boolean;\n /** 最终的 AuthResponse(流程完成后可用) */\n authResponse: AuthResponse | null;\n}\n\n/**\n * Hook 用于驱动多步骤登录插件的流程。\n *\n * 提供步骤状态管理、前进/后退/取消等操作,自动在最后一步完成后\n * 调用 finalizeAuth 并更新全局登录态。\n *\n * @param pluginName - 多步骤插件名称\n *\n * @example\n * ```tsx\n * function ClassCodeLoginFlow() {\n * const {\n * currentStepName, stepData, start, next, back, isLoading, error, isComplete,\n * } = useMultiStepLogin<{ classCode: string; loginCode: string }>('class-code');\n *\n * if (isComplete) {\n * return <Redirect to=\"/dashboard\" />;\n * }\n *\n * // 步骤 1:输入班级码\n * if (currentStepName === 'verify-code' || currentStep === -1) {\n * return <ClassCodeForm onSubmit={start} isLoading={isLoading} error={error} />;\n * }\n *\n * // 步骤 2:选择学生\n * if (currentStepName === 'select-student') {\n * return (\n * <StudentList\n * students={stepData.students}\n * onSelect={(userId) => next({ userId, classLoginToken: stepData.classLoginToken })}\n * onBack={back}\n * isLoading={isLoading}\n * />\n * );\n * }\n *\n * // 步骤 3:获取 Token(自动执行,显示 loading)\n * if (currentStepName === 'get-token') {\n * return <LoadingSpinner text=\"登录中...\" />;\n * }\n * }\n * ```\n */\nexport function useMultiStepLogin<TCredentials = unknown>(\n pluginName: string,\n): UseMultiStepLoginReturn<TCredentials> {\n const { pluginManager, stateMachine, config } = useAuthContext();\n\n const [currentStep, setCurrentStep] = useState(-1); // -1 表示未开始\n const [stepData, setStepData] = useState<unknown>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [isComplete, setIsComplete] = useState(false);\n const [authResponse, setAuthResponse] = useState<AuthResponse | null>(null);\n\n // 用 ref 保存中间步骤的累积数据,避免闭包问题\n const stepOutputsRef = useRef<unknown[]>([]);\n\n const steps = pluginManager.getSteps(pluginName);\n const totalSteps = steps?.length ?? 0;\n\n const currentStepName = currentStep >= 0 && steps ? (steps[currentStep]?.name ?? null) : null;\n\n const executeStepAt = useCallback(\n async (stepIndex: number, input: unknown) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const output = await pluginManager.executeStep(pluginName, stepIndex, input);\n stepOutputsRef.current[stepIndex] = output;\n setStepData(output);\n setCurrentStep(stepIndex);\n\n // 如果是最后一步,执行 finalizeAuth\n if (stepIndex === totalSteps - 1) {\n const response = await pluginManager.finalizeMultiStep(pluginName, output);\n stateMachine.transition('authenticated');\n\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n\n setAuthResponse(response);\n setIsComplete(true);\n }\n } catch (err) {\n const authError = err instanceof Error ? err : new Error('Step execution failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, pluginName, totalSteps, stateMachine, config],\n );\n\n const start = useCallback(\n async (credentials: TCredentials) => {\n // 重置状态\n stepOutputsRef.current = [];\n setCurrentStep(-1);\n setStepData(null);\n setIsComplete(false);\n setAuthResponse(null);\n setError(null);\n stateMachine.transition('authenticating');\n\n await executeStepAt(0, credentials);\n },\n [executeStepAt, stateMachine],\n );\n\n const next = useCallback(\n async (input: unknown) => {\n const nextIndex = currentStep + 1;\n if (nextIndex >= totalSteps) {\n throw new Error('[swr-login] No more steps to execute.');\n }\n await executeStepAt(nextIndex, input);\n },\n [currentStep, totalSteps, executeStepAt],\n );\n\n const back = useCallback(() => {\n if (currentStep > 0) {\n const prevStep = currentStep - 1;\n setCurrentStep(prevStep);\n setStepData(stepOutputsRef.current[prevStep] ?? null);\n setError(null);\n }\n }, [currentStep]);\n\n const cancel = useCallback(() => {\n setCurrentStep(-1);\n setStepData(null);\n setIsLoading(false);\n setError(null);\n setIsComplete(false);\n setAuthResponse(null);\n stepOutputsRef.current = [];\n stateMachine.transition('unauthenticated');\n }, [stateMachine]);\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return {\n currentStep,\n currentStepName,\n totalSteps,\n start,\n next,\n back,\n cancel,\n stepData,\n isLoading,\n error,\n reset,\n isComplete,\n authResponse,\n };\n}\n","import type { AuthInjector, AuthResponse } from '@swr-login/core';\nimport { useCallback } from 'react';\nimport { useAuthContext } from '../context';\n\n/**\n * Hook 用于从外部注入登录态到 swr-login 体系。\n *\n * 适用场景:\n * - 多步骤登录流程(如班级码登录、MFA)\n * - 第三方 SDK 登录完成后同步状态\n * - iframe / WebView 登录回调\n *\n * @example\n * ```tsx\n * function ExternalLoginCallback() {\n * const { injectAuth } = useAuthInjector();\n *\n * const handleLoginComplete = async (token: string, user: User) => {\n * await injectAuth({\n * user,\n * accessToken: token,\n * expiresAt: Date.now() + 86400000,\n * });\n * router.push('/dashboard');\n * };\n * }\n * ```\n */\nexport function useAuthInjector(): AuthInjector {\n const { tokenManager, emitter, stateMachine, config } = useAuthContext();\n\n const injectAuth = useCallback(\n async (response: AuthResponse): Promise<void> => {\n // 1. 存储 token\n tokenManager.setTokens({\n accessToken: response.accessToken,\n refreshToken: response.refreshToken,\n expiresAt: response.expiresAt,\n });\n\n // 2. 状态机转换为已认证。\n //\n // injectAuth 作为\"逃生舱\"应能从任何状态恢复到 authenticated,\n // 但状态机不允许 `error -> authenticated` / `unauthenticated -> authenticated` 直接转换。\n // 这两种状态都允许 `-> authenticating`,而 `authenticating -> authenticated` 合法,\n // 因此通过 `authenticating` 中转一次即可完成恢复,且每一步都符合状态机契约。\n const currentState = stateMachine.state;\n if (currentState === 'error' || currentState === 'unauthenticated') {\n stateMachine.transition('authenticating');\n }\n stateMachine.transition('authenticated');\n\n // 3. 发射登录事件(触发 onLogin 回调、跨标签页同步等)\n emitter.emit('login', { user: response.user });\n\n // 4. 更新缓存(使 useUser() 等 Hook 感知到用户信息)\n if (config.cacheAdapter) {\n await config.cacheAdapter.setUser(response.user);\n }\n },\n [tokenManager, emitter, stateMachine, config],\n );\n\n const injectLogout = useCallback(async (): Promise<void> => {\n // 1. 清除 token\n tokenManager.clearTokens();\n\n // 2. 状态机转换为未认证\n stateMachine.transition('unauthenticated');\n\n // 3. 发射登出事件\n emitter.emit('logout', undefined);\n\n // 4. 清除缓存\n if (config.cacheAdapter) {\n await config.cacheAdapter.clearUser();\n }\n }, [tokenManager, emitter, stateMachine, config]);\n\n return { injectAuth, injectLogout };\n}\n","import type { User, UserChangeEvent, UserChangeSource } from '@swr-login/core';\nimport { useEffect, useRef, useState } from 'react';\nimport { useAuthContext } from '../context';\n\n/**\n * Subscribe to `user-change` events as a **discrete event stream**.\n *\n * Unlike `useUser().user` (which reflects the *current* user value), this\n * hook returns the most recent transition event and re-renders the caller\n * only when a new transition occurs. It's intended for `useEffect`-driven\n * side effects that care about *when* and *why* the user changed rather\n * than the current user value itself.\n *\n * Returns `null` until the first transition is observed by the Provider.\n *\n * @typeParam T - Concrete user type (defaults to base `User`)\n *\n * @example\n * ```tsx\n * const change = useUserChange<MyUser>();\n *\n * useEffect(() => {\n * if (change?.source === 'login') {\n * toast.success(`Welcome back, ${change.user?.name}!`);\n * }\n * }, [change]);\n * ```\n *\n * @example Filter by source\n * ```tsx\n * const change = useUserChange();\n * useEffect(() => {\n * if (change?.source === 'external') {\n * // Another tab just logged in / out — refresh local UI\n * refreshSidebar();\n * }\n * }, [change]);\n * ```\n */\nexport function useUserChange<T extends User = User>(): UserChangeEvent<T> | null {\n const { emitter } = useAuthContext();\n const [event, setEvent] = useState<UserChangeEvent<T> | null>(null);\n\n useEffect(() => {\n const unsubscribe = emitter.on('user-change', (payload) => {\n setEvent(payload as UserChangeEvent<T>);\n });\n return unsubscribe;\n }, [emitter]);\n\n return event;\n}\n\n/**\n * Register a side-effect callback that fires on every `user-change` event,\n * **without** triggering a re-render of the calling component.\n *\n * Ideal for analytics / logging / imperative side effects that don't need\n * to participate in React's render cycle.\n *\n * The callback receives the full `UserChangeEvent` (including `previousUser`,\n * `timestamp`, and `source`). The listener is automatically unsubscribed on\n * unmount.\n *\n * Note: the callback reference is stored in a ref and always called with\n * the latest closure — you do NOT need to memoize it with `useCallback`.\n *\n * @example Analytics\n * ```tsx\n * useUserChangeEffect((e) => {\n * if (e.source === 'login') {\n * analytics.track('user_login', { userId: e.user?.id });\n * }\n * if (e.source === 'logout') {\n * analytics.track('user_logout', { userId: e.previousUser?.id });\n * }\n * });\n * ```\n *\n * @example Filter sources\n * ```tsx\n * useUserChangeEffect((e) => {\n * // Only react to passive revalidations that flipped the user to null\n * // (e.g. server returned 401 → session expired silently).\n * if (e.source === 'revalidate' && e.user === null && e.previousUser) {\n * showReLoginPrompt();\n * }\n * });\n * ```\n */\nexport function useUserChangeEffect<T extends User = User>(\n listener: (event: UserChangeEvent<T>) => void,\n): void {\n const { emitter } = useAuthContext();\n\n // Store latest callback in a ref so consumers don't need useCallback.\n const listenerRef = useRef(listener);\n listenerRef.current = listener;\n\n useEffect(() => {\n const unsubscribe = emitter.on('user-change', (payload) => {\n try {\n listenerRef.current(payload as UserChangeEvent<T>);\n } catch (err) {\n console.error('[swr-login] Error in useUserChangeEffect listener:', err);\n }\n });\n return unsubscribe;\n }, [emitter]);\n}\n\n/**\n * Convenience hook: fires the callback only when the change source matches\n * the given filter. Thin wrapper around {@link useUserChangeEffect}.\n *\n * @example\n * ```tsx\n * // Only run on explicit login (not on cold-start initial load)\n * useUserChangeOn('login', (e) => router.push('/dashboard'));\n *\n * // Subscribe to multiple sources at once\n * useUserChangeOn(['login', 'external'], (e) => refreshSidebar());\n * ```\n */\nexport function useUserChangeOn<T extends User = User>(\n source: UserChangeSource | UserChangeSource[],\n listener: (event: UserChangeEvent<T>) => void,\n): void {\n const sources = Array.isArray(source) ? source : [source];\n useUserChangeEffect<T>((event) => {\n if (sources.includes(event.source)) {\n listener(event);\n }\n });\n}\n","import { useCallback, useState } from 'react';\nimport { mutate } from 'swr';\nimport { useAuthContext } from '../context';\nimport { AUTH_KEY } from './useUser';\n\nexport interface UseLogoutOptions {\n /** Specific plugin to call logout on */\n pluginName?: string;\n /** Whether to broadcast logout to other tabs (default: true) */\n broadcast?: boolean;\n}\n\nexport interface UseLogoutReturn {\n /** Execute logout */\n logout: (options?: UseLogoutOptions) => Promise<void>;\n /** Whether logout is in progress */\n isLoading: boolean;\n}\n\n/**\n * Hook to perform secure logout with cross-tab sync.\n *\n * Logout flow:\n * 1. Call plugin logout (if applicable)\n * 2. Clear stored tokens\n * 3. Clear SWR cache (mutate user to null)\n * 4. Broadcast logout to other tabs\n * 5. Transition state to 'unauthenticated'\n *\n * @example\n * ```tsx\n * const { logout, isLoading } = useLogout();\n *\n * <button onClick={() => logout()} disabled={isLoading}>\n * Sign Out\n * </button>\n * ```\n */\nexport function useLogout(): UseLogoutReturn {\n const { pluginManager, stateMachine, broadcastSync } = useAuthContext();\n const [isLoading, setIsLoading] = useState(false);\n\n const logout = useCallback(\n async (options?: UseLogoutOptions) => {\n setIsLoading(true);\n\n try {\n // Call plugin logout\n await pluginManager.logout(options?.pluginName);\n\n // Clear SWR cache\n await mutate(AUTH_KEY, null, { revalidate: false });\n\n // Update state\n stateMachine.transition('unauthenticated');\n\n // Broadcast to other tabs\n const shouldBroadcast = options?.broadcast !== false;\n if (shouldBroadcast && broadcastSync) {\n broadcastSync.send('LOGOUT');\n }\n } catch (err) {\n console.error('[swr-login] Logout error:', err);\n // Even if logout API fails, clear local state\n await mutate(AUTH_KEY, null, { revalidate: false });\n stateMachine.transition('unauthenticated');\n } finally {\n setIsLoading(false);\n }\n },\n [pluginManager, stateMachine, broadcastSync],\n );\n\n return { logout, isLoading };\n}\n","import type { TokenAdapter } from '@swr-login/core';\nimport { useCallback } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface UseAdapterReturn {\n /**\n * Synchronously check whether the adapter currently holds a valid auth credential.\n *\n * Uses `adapter.hasAuth()` if the adapter implements it,\n * otherwise falls back to `adapter.getAccessToken() !== null`.\n *\n * This is a **synchronous, non-reactive** check — it reads the adapter's\n * current state at call time. It does NOT trigger re-renders when the\n * underlying token changes. For reactive auth state, use `useUser()`.\n *\n * Typical use cases:\n * - Conditional rendering before SWR hydration completes\n * - Route guard pre-checks\n * - Analytics / logging\n *\n * @example\n * ```tsx\n * const { hasAuth } = useAdapter();\n *\n * // 首页自动跳转\n * useEffect(() => {\n * if (hasAuth()) {\n * router.push('/dashboard');\n * }\n * }, []);\n * ```\n */\n hasAuth: () => boolean;\n\n /**\n * Direct reference to the underlying `TokenAdapter` instance.\n *\n * Escape hatch for advanced scenarios where you need low-level\n * access to token storage (e.g. reading refresh token, manually\n * clearing tokens outside of the normal logout flow).\n *\n * Prefer `hasAuth()` and the higher-level hooks (`useUser`, `useLogin`)\n * for most use cases.\n */\n adapter: TokenAdapter;\n}\n\n/**\n * Hook to access the token adapter and its utility methods.\n *\n * Provides a convenient `hasAuth()` check that works synchronously,\n * useful for UI decisions that need to happen before SWR data is available.\n *\n * @example\n * ```tsx\n * function HomePage() {\n * const { hasAuth } = useAdapter();\n * const router = useRouter();\n *\n * useEffect(() => {\n * if (hasAuth()) {\n * router.push('/dashboard');\n * }\n * }, []);\n *\n * return <LoginPage />;\n * }\n * ```\n */\nexport function useAdapter(): UseAdapterReturn {\n const { config } = useAuthContext();\n const adapter = config.adapter;\n\n const hasAuth = useCallback((): boolean => {\n if (typeof adapter.hasAuth === 'function') {\n return adapter.hasAuth();\n }\n // Fallback: 检查 accessToken 是否存在\n return adapter.getAccessToken() !== null;\n }, [adapter]);\n\n return { hasAuth, adapter };\n}\n","import { isTokenExpired } from '@swr-login/core';\nimport { useMemo } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface SessionInfo {\n /** Current access token */\n accessToken: string | null;\n /** Current refresh token */\n refreshToken: string | null;\n /** Token expiration timestamp (ms since epoch) */\n expiresAt: number | null;\n /** Whether the access token has expired */\n isExpired: boolean;\n /** Whether tokens exist (regardless of expiry) */\n hasTokens: boolean;\n}\n\n/**\n * Hook to access raw session/token information.\n *\n * Useful for:\n * - Adding Authorization headers to custom requests\n * - Checking token expiry status\n * - Debugging session state\n *\n * @example\n * ```tsx\n * const { accessToken, isExpired } = useSession();\n *\n * const fetchData = () => fetch('/api/data', {\n * headers: { Authorization: `Bearer ${accessToken}` },\n * });\n * ```\n */\nexport function useSession(): SessionInfo {\n const { tokenManager } = useAuthContext();\n\n return useMemo(() => {\n const accessToken = tokenManager.getAccessToken();\n const refreshToken = tokenManager.getRefreshToken();\n const expiresAt = tokenManager.getAccessToken() ? tokenManager.getExpiresAt() : null;\n\n return {\n accessToken,\n refreshToken,\n expiresAt,\n isExpired: isTokenExpired(expiresAt),\n hasTokens: !!accessToken,\n };\n }, [tokenManager]);\n}\n","import { useCallback } from 'react';\nimport { useUser } from './useUser';\n\nexport interface UsePermissionReturn {\n /** Check if user has a specific permission */\n hasPermission: (permission: string) => boolean;\n /** Check if user has a specific role */\n hasRole: (role: string) => boolean;\n /** Check if user has all specified permissions */\n hasAllPermissions: (permissions: string[]) => boolean;\n /** Check if user has any of the specified permissions */\n hasAnyPermission: (permissions: string[]) => boolean;\n /** Check if user has all specified roles */\n hasAllRoles: (roles: string[]) => boolean;\n /** Check if user has any of the specified roles */\n hasAnyRole: (roles: string[]) => boolean;\n}\n\n/**\n * Hook for checking user permissions and roles.\n *\n * Reads from the `permissions` and `roles` arrays on the User object.\n *\n * @example\n * ```tsx\n * const { hasPermission, hasRole } = usePermission();\n *\n * if (hasPermission('admin:write')) {\n * // Show admin controls\n * }\n *\n * if (hasRole('editor')) {\n * // Show editor tools\n * }\n * ```\n */\nexport function usePermission(): UsePermissionReturn {\n const { user } = useUser();\n\n const hasPermission = useCallback(\n (permission: string): boolean => {\n return user?.permissions?.includes(permission) ?? false;\n },\n [user],\n );\n\n const hasRole = useCallback(\n (role: string): boolean => {\n return user?.roles?.includes(role) ?? false;\n },\n [user],\n );\n\n const hasAllPermissions = useCallback(\n (permissions: string[]): boolean => {\n if (!user?.permissions) return false;\n return permissions.every((p) => user.permissions?.includes(p));\n },\n [user],\n );\n\n const hasAnyPermission = useCallback(\n (permissions: string[]): boolean => {\n if (!user?.permissions) return false;\n return permissions.some((p) => user.permissions?.includes(p));\n },\n [user],\n );\n\n const hasAllRoles = useCallback(\n (roles: string[]): boolean => {\n if (!user?.roles) return false;\n return roles.every((r) => user.roles?.includes(r));\n },\n [user],\n );\n\n const hasAnyRole = useCallback(\n (roles: string[]): boolean => {\n if (!user?.roles) return false;\n return roles.some((r) => user.roles?.includes(r));\n },\n [user],\n );\n\n return {\n hasPermission,\n hasRole,\n hasAllPermissions,\n hasAnyPermission,\n hasAllRoles,\n hasAnyRole,\n };\n}\n","import type React from 'react';\nimport { usePermission } from '../hooks/usePermission';\nimport { useUser } from '../hooks/useUser';\n\nexport interface AuthGuardProps {\n children: React.ReactNode;\n /** Required permissions (checked against user.permissions) */\n permissions?: string[];\n /** Required roles (checked against user.roles) */\n roles?: string[];\n /** If true, ALL permissions/roles must match. If false, ANY match suffices. (default: false) */\n requireAll?: boolean;\n /** Component to render when user is not authenticated or lacks permissions */\n fallback?: React.ReactNode;\n /** Component to render while checking auth status */\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Declarative auth guard component.\n * Protects child content based on authentication state and permissions/roles.\n *\n * @example\n * ```tsx\n * <AuthGuard\n * permissions={['admin', 'editor']}\n * requireAll={false}\n * fallback={<Navigate to=\"/login\" />}\n * loadingComponent={<Skeleton />}\n * >\n * <AdminPanel />\n * </AuthGuard>\n * ```\n */\nexport function AuthGuard({\n children,\n permissions,\n roles,\n requireAll = false,\n fallback = null,\n loadingComponent = null,\n}: AuthGuardProps) {\n const { isLoading, isAuthenticated } = useUser();\n const { hasAllPermissions, hasAnyPermission, hasAllRoles, hasAnyRole } = usePermission();\n\n if (isLoading) {\n return <>{loadingComponent}</>;\n }\n\n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n const hasRequiredPermissions = requireAll\n ? hasAllPermissions(permissions)\n : hasAnyPermission(permissions);\n\n if (!hasRequiredPermissions) {\n return <>{fallback}</>;\n }\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n const hasRequiredRoles = requireAll ? hasAllRoles(roles) : hasAnyRole(roles);\n\n if (!hasRequiredRoles) {\n return <>{fallback}</>;\n }\n }\n\n return <>{children}</>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swr-login/react",
3
- "version": "0.9.2",
3
+ "version": "0.10.0",
4
4
  "description": "React bindings for swr-login: Provider, Hooks, and AuthGuard component",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -27,7 +27,7 @@
27
27
  "swr": ">=2.0.0"
28
28
  },
29
29
  "dependencies": {
30
- "@swr-login/core": "0.9.0"
30
+ "@swr-login/core": "0.10.0"
31
31
  },
32
32
  "keywords": [
33
33
  "swr",