@swr-login/react 0.6.0 → 0.8.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
@@ -63,8 +63,11 @@ function SWRLoginProvider({ config, children }) {
63
63
  console.error("[swr-login] Plugin initialization error:", err);
64
64
  });
65
65
  const existingToken = tokenManager.getAccessToken();
66
+ const expiresAt = tokenManager.getExpiresAt();
66
67
  if (existingToken && !tokenManager.isExpired()) {
67
68
  stateMachine.transition("authenticated");
69
+ } else if (existingToken && expiresAt === null) {
70
+ stateMachine.transition("authenticated");
68
71
  } else if (existingToken && tokenManager.isExpired()) {
69
72
  stateMachine.transition("unauthenticated");
70
73
  emitter.emit("token-expired", void 0);
@@ -136,7 +139,8 @@ function useUser() {
136
139
  try {
137
140
  await tokenManager.refresh();
138
141
  } catch {
139
- return null;
142
+ const remainingToken = tokenManager.getAccessToken();
143
+ if (!remainingToken) return null;
140
144
  }
141
145
  }
142
146
  if (config.fetchUser) {
@@ -152,9 +156,18 @@ function useUser() {
152
156
  isLoading,
153
157
  mutate: swrMutate
154
158
  } = useSWR__default.default(AUTH_KEY, fetcher, {
155
- revalidateOnFocus: true,
156
- revalidateOnReconnect: true,
157
- shouldRetryOnError: false
159
+ revalidateOnFocus: config.swrOptions?.revalidateOnFocus ?? true,
160
+ revalidateOnReconnect: config.swrOptions?.revalidateOnReconnect ?? true,
161
+ shouldRetryOnError: false,
162
+ ...config.swrOptions?.dedupingInterval !== void 0 && {
163
+ dedupingInterval: config.swrOptions.dedupingInterval
164
+ },
165
+ ...config.swrOptions?.focusThrottleInterval !== void 0 && {
166
+ focusThrottleInterval: config.swrOptions.focusThrottleInterval
167
+ },
168
+ ...config.swrOptions?.refreshInterval !== void 0 && {
169
+ refreshInterval: config.swrOptions.refreshInterval
170
+ }
158
171
  });
159
172
  react.useEffect(() => {
160
173
  if (error) {
@@ -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/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} from '@swr-login/core';\nimport { createContext, useContext } from 'react';\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}\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 } 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 // 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 };\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 if (existingToken && !tokenManager.isExpired()) {\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 unsubscribe = broadcastSync.onMessage((message) => {\n switch (message.type) {\n case 'LOGOUT':\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n break;\n case 'LOGIN':\n case 'TOKEN_REFRESH':\n // Trigger revalidation from other tab\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 } 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\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\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 */\nexport function useUser<T extends User = User>(): UseUserReturn<T> {\n const { tokenManager, stateMachine, config } = 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 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 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: true,\n revalidateOnReconnect: true,\n shouldRetryOnError: false,\n });\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 };\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 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 { 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/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} from '@swr-login/core';\nimport { createContext, useContext } from 'react';\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}\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 } 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 // 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 };\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 unsubscribe = broadcastSync.onMessage((message) => {\n switch (message.type) {\n case 'LOGOUT':\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n break;\n case 'LOGIN':\n case 'TOKEN_REFRESH':\n // Trigger revalidation from other tab\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 } 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\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\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 */\nexport function useUser<T extends User = User>(): UseUserReturn<T> {\n const { tokenManager, stateMachine, config } = 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 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 // ── 同步 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 };\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 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 { 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.js CHANGED
@@ -57,8 +57,11 @@ function SWRLoginProvider({ config, children }) {
57
57
  console.error("[swr-login] Plugin initialization error:", err);
58
58
  });
59
59
  const existingToken = tokenManager.getAccessToken();
60
+ const expiresAt = tokenManager.getExpiresAt();
60
61
  if (existingToken && !tokenManager.isExpired()) {
61
62
  stateMachine.transition("authenticated");
63
+ } else if (existingToken && expiresAt === null) {
64
+ stateMachine.transition("authenticated");
62
65
  } else if (existingToken && tokenManager.isExpired()) {
63
66
  stateMachine.transition("unauthenticated");
64
67
  emitter.emit("token-expired", void 0);
@@ -130,7 +133,8 @@ function useUser() {
130
133
  try {
131
134
  await tokenManager.refresh();
132
135
  } catch {
133
- return null;
136
+ const remainingToken = tokenManager.getAccessToken();
137
+ if (!remainingToken) return null;
134
138
  }
135
139
  }
136
140
  if (config.fetchUser) {
@@ -146,9 +150,18 @@ function useUser() {
146
150
  isLoading,
147
151
  mutate: swrMutate
148
152
  } = useSWR(AUTH_KEY, fetcher, {
149
- revalidateOnFocus: true,
150
- revalidateOnReconnect: true,
151
- shouldRetryOnError: false
153
+ revalidateOnFocus: config.swrOptions?.revalidateOnFocus ?? true,
154
+ revalidateOnReconnect: config.swrOptions?.revalidateOnReconnect ?? true,
155
+ shouldRetryOnError: false,
156
+ ...config.swrOptions?.dedupingInterval !== void 0 && {
157
+ dedupingInterval: config.swrOptions.dedupingInterval
158
+ },
159
+ ...config.swrOptions?.focusThrottleInterval !== void 0 && {
160
+ focusThrottleInterval: config.swrOptions.focusThrottleInterval
161
+ },
162
+ ...config.swrOptions?.refreshInterval !== void 0 && {
163
+ refreshInterval: config.swrOptions.refreshInterval
164
+ }
152
165
  });
153
166
  useEffect(() => {
154
167
  if (error) {
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/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} from '@swr-login/core';\nimport { createContext, useContext } from 'react';\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}\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 } 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 // 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 };\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 if (existingToken && !tokenManager.isExpired()) {\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 unsubscribe = broadcastSync.onMessage((message) => {\n switch (message.type) {\n case 'LOGOUT':\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n break;\n case 'LOGIN':\n case 'TOKEN_REFRESH':\n // Trigger revalidation from other tab\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 } 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\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\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 */\nexport function useUser<T extends User = User>(): UseUserReturn<T> {\n const { tokenManager, stateMachine, config } = 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 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 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: true,\n revalidateOnReconnect: true,\n shouldRetryOnError: false,\n });\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 };\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 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 { 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/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} from '@swr-login/core';\nimport { createContext, useContext } from 'react';\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}\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 } 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 // 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 };\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 unsubscribe = broadcastSync.onMessage((message) => {\n switch (message.type) {\n case 'LOGOUT':\n tokenManager.clearTokens();\n stateMachine.transition('unauthenticated');\n emitter.emit('logout', undefined);\n break;\n case 'LOGIN':\n case 'TOKEN_REFRESH':\n // Trigger revalidation from other tab\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 } 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\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\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 */\nexport function useUser<T extends User = User>(): UseUserReturn<T> {\n const { tokenManager, stateMachine, config } = 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 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 // ── 同步 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 };\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 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 { 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.6.0",
3
+ "version": "0.8.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.6.0"
30
+ "@swr-login/core": "0.7.0"
31
31
  },
32
32
  "keywords": [
33
33
  "swr",