@swr-login/react 0.2.0 → 0.4.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
@@ -118,8 +118,87 @@ function SWRLoginProvider({ config, children }) {
118
118
  }, [contextValue]);
119
119
  return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value: contextValue, children });
120
120
  }
121
+ var AUTH_KEY = "__swr_login_user__";
122
+ function useUser() {
123
+ const { tokenManager, stateMachine, config } = useAuthContext();
124
+ const lastErrorRef = react.useRef(void 0);
125
+ const retryCountRef = react.useRef(0);
126
+ const [, setTick] = react.useState(0);
127
+ const forceUpdate = react.useCallback(() => setTick((t) => t + 1), []);
128
+ const clearError = react.useCallback(() => {
129
+ lastErrorRef.current = void 0;
130
+ forceUpdate();
131
+ }, [forceUpdate]);
132
+ const fetcher = async () => {
133
+ const token = tokenManager.getAccessToken();
134
+ if (!token) return null;
135
+ if (tokenManager.isExpired()) {
136
+ try {
137
+ await tokenManager.refresh();
138
+ } catch {
139
+ return null;
140
+ }
141
+ }
142
+ if (config.fetchUser) {
143
+ const currentToken = tokenManager.getAccessToken();
144
+ if (!currentToken) return null;
145
+ return await config.fetchUser(currentToken);
146
+ }
147
+ return null;
148
+ };
149
+ const {
150
+ data,
151
+ error,
152
+ isLoading,
153
+ mutate: swrMutate
154
+ } = useSWR__default.default(AUTH_KEY, fetcher, {
155
+ revalidateOnFocus: true,
156
+ revalidateOnReconnect: true,
157
+ shouldRetryOnError: false
158
+ });
159
+ react.useEffect(() => {
160
+ if (error) {
161
+ lastErrorRef.current = error;
162
+ forceUpdate();
163
+ if (config.onFetchUserError) {
164
+ const action = config.onFetchUserError(error);
165
+ if (action === "retry" && retryCountRef.current < 1) {
166
+ retryCountRef.current += 1;
167
+ swrMutate();
168
+ } else if (action === "logout") {
169
+ tokenManager.clearTokens();
170
+ stateMachine.transition("unauthenticated");
171
+ }
172
+ }
173
+ } else if (data !== void 0 && !error) {
174
+ if (lastErrorRef.current !== void 0) {
175
+ lastErrorRef.current = void 0;
176
+ forceUpdate();
177
+ }
178
+ retryCountRef.current = 0;
179
+ }
180
+ }, [error, data, forceUpdate, config, tokenManager, stateMachine, swrMutate]);
181
+ const mutate2 = async (newData) => {
182
+ if (newData === void 0) {
183
+ await swrMutate();
184
+ } else {
185
+ await swrMutate(newData, { revalidate: false });
186
+ }
187
+ };
188
+ return {
189
+ user: data ?? null,
190
+ isLoading,
191
+ isAuthenticated: !!data,
192
+ error,
193
+ lastError: lastErrorRef.current,
194
+ clearError,
195
+ mutate: mutate2
196
+ };
197
+ }
198
+
199
+ // src/hooks/useLogin.ts
121
200
  function useLogin(pluginName) {
122
- const { pluginManager, stateMachine, config } = useAuthContext();
201
+ const { pluginManager, tokenManager, stateMachine, config } = useAuthContext();
123
202
  const [isLoading, setIsLoading] = react.useState(false);
124
203
  const [error, setError] = react.useState(null);
125
204
  const login = react.useCallback(
@@ -140,8 +219,28 @@ function useLogin(pluginName) {
140
219
  setIsLoading(true);
141
220
  setError(null);
142
221
  stateMachine.transition("authenticating");
222
+ const targetPlugin = pluginManager.getPlugin(resolvedPlugin);
223
+ if (targetPlugin && core.isMultiStepPlugin(targetPlugin)) {
224
+ const err = new Error(
225
+ `[swr-login] Plugin "${resolvedPlugin}" is a multi-step plugin. Use useMultiStepLogin() instead of useLogin() for multi-step login flows.`
226
+ );
227
+ setError(err);
228
+ stateMachine.transition("error");
229
+ setIsLoading(false);
230
+ throw err;
231
+ }
143
232
  try {
144
233
  const response = await pluginManager.login(resolvedPlugin, resolvedCredentials);
234
+ if (config.fetchUser && config.validateUserOnLogin !== false) {
235
+ try {
236
+ const user = await config.fetchUser(response.accessToken);
237
+ await useSWR.mutate(AUTH_KEY, user, { revalidate: false });
238
+ } catch (fetchUserErr) {
239
+ tokenManager.clearTokens();
240
+ stateMachine.transition("unauthenticated");
241
+ throw fetchUserErr;
242
+ }
243
+ }
145
244
  stateMachine.transition("authenticated");
146
245
  if (config.cacheAdapter) {
147
246
  await config.cacheAdapter.setUser(response.user);
@@ -156,58 +255,140 @@ function useLogin(pluginName) {
156
255
  setIsLoading(false);
157
256
  }
158
257
  },
159
- [pluginManager, stateMachine, config, pluginName]
258
+ [pluginManager, tokenManager, stateMachine, config, pluginName]
160
259
  );
161
260
  const reset = react.useCallback(() => {
162
261
  setError(null);
163
262
  }, []);
164
263
  return { login, isLoading, error, reset };
165
264
  }
166
- var AUTH_KEY = "__swr_login_user__";
167
- function useUser() {
168
- const { tokenManager, config } = useAuthContext();
169
- const fetcher = async () => {
170
- const token = tokenManager.getAccessToken();
171
- if (!token) return null;
172
- if (tokenManager.isExpired()) {
265
+ function useMultiStepLogin(pluginName) {
266
+ const { pluginManager, stateMachine, config } = useAuthContext();
267
+ const [currentStep, setCurrentStep] = react.useState(-1);
268
+ const [stepData, setStepData] = react.useState(null);
269
+ const [isLoading, setIsLoading] = react.useState(false);
270
+ const [error, setError] = react.useState(null);
271
+ const [isComplete, setIsComplete] = react.useState(false);
272
+ const [authResponse, setAuthResponse] = react.useState(null);
273
+ const stepOutputsRef = react.useRef([]);
274
+ const steps = pluginManager.getSteps(pluginName);
275
+ const totalSteps = steps?.length ?? 0;
276
+ const currentStepName = currentStep >= 0 && steps ? steps[currentStep]?.name ?? null : null;
277
+ const executeStepAt = react.useCallback(
278
+ async (stepIndex, input) => {
279
+ setIsLoading(true);
280
+ setError(null);
173
281
  try {
174
- await tokenManager.refresh();
175
- } catch {
176
- return null;
282
+ const output = await pluginManager.executeStep(pluginName, stepIndex, input);
283
+ stepOutputsRef.current[stepIndex] = output;
284
+ setStepData(output);
285
+ setCurrentStep(stepIndex);
286
+ if (stepIndex === totalSteps - 1) {
287
+ const response = await pluginManager.finalizeMultiStep(pluginName, output);
288
+ stateMachine.transition("authenticated");
289
+ if (config.cacheAdapter) {
290
+ await config.cacheAdapter.setUser(response.user);
291
+ }
292
+ setAuthResponse(response);
293
+ setIsComplete(true);
294
+ }
295
+ } catch (err) {
296
+ const authError = err instanceof Error ? err : new Error("Step execution failed");
297
+ setError(authError);
298
+ throw authError;
299
+ } finally {
300
+ setIsLoading(false);
177
301
  }
302
+ },
303
+ [pluginManager, pluginName, totalSteps, stateMachine, config]
304
+ );
305
+ const start = react.useCallback(
306
+ async (credentials) => {
307
+ stepOutputsRef.current = [];
308
+ setCurrentStep(-1);
309
+ setStepData(null);
310
+ setIsComplete(false);
311
+ setAuthResponse(null);
312
+ setError(null);
313
+ stateMachine.transition("authenticating");
314
+ await executeStepAt(0, credentials);
315
+ },
316
+ [executeStepAt, stateMachine]
317
+ );
318
+ const next = react.useCallback(
319
+ async (input) => {
320
+ const nextIndex = currentStep + 1;
321
+ if (nextIndex >= totalSteps) {
322
+ throw new Error("[swr-login] No more steps to execute.");
323
+ }
324
+ await executeStepAt(nextIndex, input);
325
+ },
326
+ [currentStep, totalSteps, executeStepAt]
327
+ );
328
+ const back = react.useCallback(() => {
329
+ if (currentStep > 0) {
330
+ const prevStep = currentStep - 1;
331
+ setCurrentStep(prevStep);
332
+ setStepData(stepOutputsRef.current[prevStep] ?? null);
333
+ setError(null);
178
334
  }
179
- if (config.fetchUser) {
180
- const currentToken = tokenManager.getAccessToken();
181
- if (!currentToken) return null;
182
- return await config.fetchUser(currentToken);
183
- }
184
- return null;
185
- };
186
- const {
187
- data,
188
- error,
189
- isLoading,
190
- mutate: swrMutate
191
- } = useSWR__default.default(AUTH_KEY, fetcher, {
192
- revalidateOnFocus: true,
193
- revalidateOnReconnect: true,
194
- shouldRetryOnError: false
195
- });
196
- const mutate2 = async (newData) => {
197
- if (newData === void 0) {
198
- await swrMutate();
199
- } else {
200
- await swrMutate(newData, { revalidate: false });
201
- }
202
- };
335
+ }, [currentStep]);
336
+ const cancel = react.useCallback(() => {
337
+ setCurrentStep(-1);
338
+ setStepData(null);
339
+ setIsLoading(false);
340
+ setError(null);
341
+ setIsComplete(false);
342
+ setAuthResponse(null);
343
+ stepOutputsRef.current = [];
344
+ stateMachine.transition("unauthenticated");
345
+ }, [stateMachine]);
346
+ const reset = react.useCallback(() => {
347
+ setError(null);
348
+ }, []);
203
349
  return {
204
- user: data ?? null,
350
+ currentStep,
351
+ currentStepName,
352
+ totalSteps,
353
+ start,
354
+ next,
355
+ back,
356
+ cancel,
357
+ stepData,
205
358
  isLoading,
206
- isAuthenticated: !!data,
207
359
  error,
208
- mutate: mutate2
360
+ reset,
361
+ isComplete,
362
+ authResponse
209
363
  };
210
364
  }
365
+ function useAuthInjector() {
366
+ const { tokenManager, emitter, stateMachine, config } = useAuthContext();
367
+ const injectAuth = react.useCallback(
368
+ async (response) => {
369
+ tokenManager.setTokens({
370
+ accessToken: response.accessToken,
371
+ refreshToken: response.refreshToken,
372
+ expiresAt: response.expiresAt
373
+ });
374
+ stateMachine.transition("authenticated");
375
+ emitter.emit("login", { user: response.user });
376
+ if (config.cacheAdapter) {
377
+ await config.cacheAdapter.setUser(response.user);
378
+ }
379
+ },
380
+ [tokenManager, emitter, stateMachine, config]
381
+ );
382
+ const injectLogout = react.useCallback(async () => {
383
+ tokenManager.clearTokens();
384
+ stateMachine.transition("unauthenticated");
385
+ emitter.emit("logout", void 0);
386
+ if (config.cacheAdapter) {
387
+ await config.cacheAdapter.clearUser();
388
+ }
389
+ }, [tokenManager, emitter, stateMachine, config]);
390
+ return { injectAuth, injectLogout };
391
+ }
211
392
  function useLogout() {
212
393
  const { pluginManager, stateMachine, broadcastSync } = useAuthContext();
213
394
  const [isLoading, setIsLoading] = react.useState(false);
@@ -335,8 +516,10 @@ exports.AUTH_KEY = AUTH_KEY;
335
516
  exports.AuthGuard = AuthGuard;
336
517
  exports.SWRLoginProvider = SWRLoginProvider;
337
518
  exports.useAuthContext = useAuthContext;
519
+ exports.useAuthInjector = useAuthInjector;
338
520
  exports.useLogin = useLogin;
339
521
  exports.useLogout = useLogout;
522
+ exports.useMultiStepLogin = useMultiStepLogin;
340
523
  exports.usePermission = usePermission;
341
524
  exports.useSession = useSession;
342
525
  exports.useUser = useUser;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/hooks/useLogin.ts","../src/hooks/useUser.ts","../src/hooks/useLogout.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","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 { AuthResponse } from '@swr-login/core';\nimport { useCallback, useState } from 'react';\nimport { useAuthContext } from '../context';\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, 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 try {\n const response = await pluginManager.login(resolvedPlugin, resolvedCredentials);\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, stateMachine, config, pluginName],\n );\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return { login, isLoading, error, reset };\n}\n","import type { User } from '@swr-login/core';\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 * 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, config } = useAuthContext();\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 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 mutate,\n };\n}\n\n/** SWR cache key used for user data. Exported for advanced usage. */\nexport { AUTH_KEY };\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 { 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/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 // ── validateUserOnLogin:在 plugin 成功后调用 fetchUser 验证用户状态 ──\n if (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 { isTokenExpired } from '@swr-login/core';\nimport { useMemo } from 'react';\nimport { useAuthContext } from '../context';\n\nexport interface SessionInfo {\n /** Current access token */\n accessToken: string | null;\n /** Current refresh token */\n refreshToken: string | null;\n /** Token expiration timestamp (ms since epoch) */\n expiresAt: number | null;\n /** Whether the access token has expired */\n isExpired: boolean;\n /** Whether tokens exist (regardless of expiry) */\n hasTokens: boolean;\n}\n\n/**\n * Hook to access raw session/token information.\n *\n * Useful for:\n * - Adding Authorization headers to custom requests\n * - Checking token expiry status\n * - Debugging session state\n *\n * @example\n * ```tsx\n * const { accessToken, isExpired } = useSession();\n *\n * const fetchData = () => fetch('/api/data', {\n * headers: { Authorization: `Bearer ${accessToken}` },\n * });\n * ```\n */\nexport function useSession(): SessionInfo {\n const { tokenManager } = useAuthContext();\n\n return useMemo(() => {\n const accessToken = tokenManager.getAccessToken();\n const refreshToken = tokenManager.getRefreshToken();\n const expiresAt = tokenManager.getAccessToken() ? tokenManager.getExpiresAt() : null;\n\n return {\n accessToken,\n refreshToken,\n expiresAt,\n isExpired: isTokenExpired(expiresAt),\n hasTokens: !!accessToken,\n };\n }, [tokenManager]);\n}\n","import { useCallback } from 'react';\nimport { useUser } from './useUser';\n\nexport interface UsePermissionReturn {\n /** Check if user has a specific permission */\n hasPermission: (permission: string) => boolean;\n /** Check if user has a specific role */\n hasRole: (role: string) => boolean;\n /** Check if user has all specified permissions */\n hasAllPermissions: (permissions: string[]) => boolean;\n /** Check if user has any of the specified permissions */\n hasAnyPermission: (permissions: string[]) => boolean;\n /** Check if user has all specified roles */\n hasAllRoles: (roles: string[]) => boolean;\n /** Check if user has any of the specified roles */\n hasAnyRole: (roles: string[]) => boolean;\n}\n\n/**\n * Hook for checking user permissions and roles.\n *\n * Reads from the `permissions` and `roles` arrays on the User object.\n *\n * @example\n * ```tsx\n * const { hasPermission, hasRole } = usePermission();\n *\n * if (hasPermission('admin:write')) {\n * // Show admin controls\n * }\n *\n * if (hasRole('editor')) {\n * // Show editor tools\n * }\n * ```\n */\nexport function usePermission(): UsePermissionReturn {\n const { user } = useUser();\n\n const hasPermission = useCallback(\n (permission: string): boolean => {\n return user?.permissions?.includes(permission) ?? false;\n },\n [user],\n );\n\n const hasRole = useCallback(\n (role: string): boolean => {\n return user?.roles?.includes(role) ?? false;\n },\n [user],\n );\n\n const hasAllPermissions = useCallback(\n (permissions: string[]): boolean => {\n if (!user?.permissions) return false;\n return permissions.every((p) => user.permissions?.includes(p));\n },\n [user],\n );\n\n const hasAnyPermission = useCallback(\n (permissions: string[]): boolean => {\n if (!user?.permissions) return false;\n return permissions.some((p) => user.permissions?.includes(p));\n },\n [user],\n );\n\n const hasAllRoles = useCallback(\n (roles: string[]): boolean => {\n if (!user?.roles) return false;\n return roles.every((r) => user.roles?.includes(r));\n },\n [user],\n );\n\n const hasAnyRole = useCallback(\n (roles: string[]): boolean => {\n if (!user?.roles) return false;\n return roles.some((r) => user.roles?.includes(r));\n },\n [user],\n );\n\n return {\n hasPermission,\n hasRole,\n hasAllPermissions,\n hasAnyPermission,\n hasAllRoles,\n hasAnyRole,\n };\n}\n","import type React from 'react';\nimport { usePermission } from '../hooks/usePermission';\nimport { useUser } from '../hooks/useUser';\n\nexport interface AuthGuardProps {\n children: React.ReactNode;\n /** Required permissions (checked against user.permissions) */\n permissions?: string[];\n /** Required roles (checked against user.roles) */\n roles?: string[];\n /** If true, ALL permissions/roles must match. If false, ANY match suffices. (default: false) */\n requireAll?: boolean;\n /** Component to render when user is not authenticated or lacks permissions */\n fallback?: React.ReactNode;\n /** Component to render while checking auth status */\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Declarative auth guard component.\n * Protects child content based on authentication state and permissions/roles.\n *\n * @example\n * ```tsx\n * <AuthGuard\n * permissions={['admin', 'editor']}\n * requireAll={false}\n * fallback={<Navigate to=\"/login\" />}\n * loadingComponent={<Skeleton />}\n * >\n * <AdminPanel />\n * </AuthGuard>\n * ```\n */\nexport function AuthGuard({\n children,\n permissions,\n roles,\n requireAll = false,\n fallback = null,\n loadingComponent = null,\n}: AuthGuardProps) {\n const { isLoading, isAuthenticated } = useUser();\n const { hasAllPermissions, hasAnyPermission, hasAllRoles, hasAnyRole } = usePermission();\n\n if (isLoading) {\n return <>{loadingComponent}</>;\n }\n\n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n const hasRequiredPermissions = requireAll\n ? hasAllPermissions(permissions)\n : hasAnyPermission(permissions);\n\n if (!hasRequiredPermissions) {\n return <>{fallback}</>;\n }\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n const hasRequiredRoles = requireAll ? hasAllRoles(roles) : hasAnyRole(roles);\n\n if (!hasRequiredRoles) {\n return <>{fallback}</>;\n }\n }\n\n return <>{children}</>;\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { SWRLoginConfig, AuthResponse, User, PluginManager, TokenManager, AuthEventEmitter, AuthStateMachine, BroadcastSync } from '@swr-login/core';
2
+ import { SWRLoginConfig, AuthResponse, AuthInjector, User, PluginManager, TokenManager, AuthEventEmitter, AuthStateMachine, BroadcastSync } from '@swr-login/core';
3
3
  import React from 'react';
4
4
 
5
5
  interface SWRLoginProviderProps {
@@ -67,6 +67,105 @@ interface UseLoginReturn<TCredentials = unknown> {
67
67
  */
68
68
  declare function useLogin<TCredentials = unknown>(pluginName?: string): UseLoginReturn<TCredentials>;
69
69
 
70
+ interface UseMultiStepLoginReturn<TCredentials = unknown> {
71
+ /** 当前步骤索引(从 0 开始,-1 表示未开始) */
72
+ currentStep: number;
73
+ /** 当前步骤名称(未开始时为 null) */
74
+ currentStepName: string | null;
75
+ /** 总步骤数 */
76
+ totalSteps: number;
77
+ /** 启动流程(执行第一步) */
78
+ start: (credentials: TCredentials) => Promise<void>;
79
+ /** 继续下一步 */
80
+ next: (input: unknown) => Promise<void>;
81
+ /** 回到上一步(仅 UI 层回退,不重新执行步骤逻辑) */
82
+ back: () => void;
83
+ /** 取消整个流程,重置状态 */
84
+ cancel: () => void;
85
+ /** 当前步骤的输出数据(传给 UI 渲染) */
86
+ stepData: unknown;
87
+ /** 是否正在执行当前步骤 */
88
+ isLoading: boolean;
89
+ /** 当前步骤的错误 */
90
+ error: Error | null;
91
+ /** 重置错误状态 */
92
+ reset: () => void;
93
+ /** 流程是否已完成 */
94
+ isComplete: boolean;
95
+ /** 最终的 AuthResponse(流程完成后可用) */
96
+ authResponse: AuthResponse | null;
97
+ }
98
+ /**
99
+ * Hook 用于驱动多步骤登录插件的流程。
100
+ *
101
+ * 提供步骤状态管理、前进/后退/取消等操作,自动在最后一步完成后
102
+ * 调用 finalizeAuth 并更新全局登录态。
103
+ *
104
+ * @param pluginName - 多步骤插件名称
105
+ *
106
+ * @example
107
+ * ```tsx
108
+ * function ClassCodeLoginFlow() {
109
+ * const {
110
+ * currentStepName, stepData, start, next, back, isLoading, error, isComplete,
111
+ * } = useMultiStepLogin<{ classCode: string; loginCode: string }>('class-code');
112
+ *
113
+ * if (isComplete) {
114
+ * return <Redirect to="/dashboard" />;
115
+ * }
116
+ *
117
+ * // 步骤 1:输入班级码
118
+ * if (currentStepName === 'verify-code' || currentStep === -1) {
119
+ * return <ClassCodeForm onSubmit={start} isLoading={isLoading} error={error} />;
120
+ * }
121
+ *
122
+ * // 步骤 2:选择学生
123
+ * if (currentStepName === 'select-student') {
124
+ * return (
125
+ * <StudentList
126
+ * students={stepData.students}
127
+ * onSelect={(userId) => next({ userId, classLoginToken: stepData.classLoginToken })}
128
+ * onBack={back}
129
+ * isLoading={isLoading}
130
+ * />
131
+ * );
132
+ * }
133
+ *
134
+ * // 步骤 3:获取 Token(自动执行,显示 loading)
135
+ * if (currentStepName === 'get-token') {
136
+ * return <LoadingSpinner text="登录中..." />;
137
+ * }
138
+ * }
139
+ * ```
140
+ */
141
+ declare function useMultiStepLogin<TCredentials = unknown>(pluginName: string): UseMultiStepLoginReturn<TCredentials>;
142
+
143
+ /**
144
+ * Hook 用于从外部注入登录态到 swr-login 体系。
145
+ *
146
+ * 适用场景:
147
+ * - 多步骤登录流程(如班级码登录、MFA)
148
+ * - 第三方 SDK 登录完成后同步状态
149
+ * - iframe / WebView 登录回调
150
+ *
151
+ * @example
152
+ * ```tsx
153
+ * function ExternalLoginCallback() {
154
+ * const { injectAuth } = useAuthInjector();
155
+ *
156
+ * const handleLoginComplete = async (token: string, user: User) => {
157
+ * await injectAuth({
158
+ * user,
159
+ * accessToken: token,
160
+ * expiresAt: Date.now() + 86400000,
161
+ * });
162
+ * router.push('/dashboard');
163
+ * };
164
+ * }
165
+ * ```
166
+ */
167
+ declare function useAuthInjector(): AuthInjector;
168
+
70
169
  declare const AUTH_KEY = "__swr_login_user__";
71
170
  interface UseUserReturn<T extends User = User> {
72
171
  /** Current authenticated user, or null if not logged in */
@@ -77,6 +176,21 @@ interface UseUserReturn<T extends User = User> {
77
176
  isAuthenticated: boolean;
78
177
  /** Error from fetching user data */
79
178
  error: Error | undefined;
179
+ /**
180
+ * Sticky error from the last `fetchUser` failure.
181
+ *
182
+ * Unlike `error`, this value is **not** cleared when the auth state
183
+ * transitions (e.g. from `authenticated` → `unauthenticated`).
184
+ * It persists until `fetchUser` succeeds again or `clearError()` is called.
185
+ *
186
+ * Useful for distinguishing "not logged in" from "account disabled" in
187
+ * AuthGuard or error-boundary components.
188
+ */
189
+ lastError: Error | undefined;
190
+ /**
191
+ * Manually reset `lastError` to `undefined`.
192
+ */
193
+ clearError: () => void;
80
194
  /**
81
195
  * Manually update cached user data.
82
196
  * Call with `null` to clear, or with a user object to update.
@@ -247,4 +361,4 @@ interface AuthContextValue {
247
361
  */
248
362
  declare function useAuthContext(): AuthContextValue;
249
363
 
250
- export { AUTH_KEY, type AuthContextValue, AuthGuard, type AuthGuardProps, SWRLoginProvider, type SWRLoginProviderProps, type SessionInfo, type UseLoginOptions, type UseLoginReturn, type UseLogoutOptions, type UseLogoutReturn, type UsePermissionReturn, type UseUserReturn, useAuthContext, useLogin, useLogout, usePermission, useSession, useUser };
364
+ export { AUTH_KEY, type AuthContextValue, AuthGuard, type AuthGuardProps, SWRLoginProvider, type SWRLoginProviderProps, type SessionInfo, type UseLoginOptions, type UseLoginReturn, type UseLogoutOptions, type UseLogoutReturn, type UseMultiStepLoginReturn, type UsePermissionReturn, type UseUserReturn, useAuthContext, useAuthInjector, useLogin, useLogout, useMultiStepLogin, usePermission, useSession, useUser };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { SWRLoginConfig, AuthResponse, User, PluginManager, TokenManager, AuthEventEmitter, AuthStateMachine, BroadcastSync } from '@swr-login/core';
2
+ import { SWRLoginConfig, AuthResponse, AuthInjector, User, PluginManager, TokenManager, AuthEventEmitter, AuthStateMachine, BroadcastSync } from '@swr-login/core';
3
3
  import React from 'react';
4
4
 
5
5
  interface SWRLoginProviderProps {
@@ -67,6 +67,105 @@ interface UseLoginReturn<TCredentials = unknown> {
67
67
  */
68
68
  declare function useLogin<TCredentials = unknown>(pluginName?: string): UseLoginReturn<TCredentials>;
69
69
 
70
+ interface UseMultiStepLoginReturn<TCredentials = unknown> {
71
+ /** 当前步骤索引(从 0 开始,-1 表示未开始) */
72
+ currentStep: number;
73
+ /** 当前步骤名称(未开始时为 null) */
74
+ currentStepName: string | null;
75
+ /** 总步骤数 */
76
+ totalSteps: number;
77
+ /** 启动流程(执行第一步) */
78
+ start: (credentials: TCredentials) => Promise<void>;
79
+ /** 继续下一步 */
80
+ next: (input: unknown) => Promise<void>;
81
+ /** 回到上一步(仅 UI 层回退,不重新执行步骤逻辑) */
82
+ back: () => void;
83
+ /** 取消整个流程,重置状态 */
84
+ cancel: () => void;
85
+ /** 当前步骤的输出数据(传给 UI 渲染) */
86
+ stepData: unknown;
87
+ /** 是否正在执行当前步骤 */
88
+ isLoading: boolean;
89
+ /** 当前步骤的错误 */
90
+ error: Error | null;
91
+ /** 重置错误状态 */
92
+ reset: () => void;
93
+ /** 流程是否已完成 */
94
+ isComplete: boolean;
95
+ /** 最终的 AuthResponse(流程完成后可用) */
96
+ authResponse: AuthResponse | null;
97
+ }
98
+ /**
99
+ * Hook 用于驱动多步骤登录插件的流程。
100
+ *
101
+ * 提供步骤状态管理、前进/后退/取消等操作,自动在最后一步完成后
102
+ * 调用 finalizeAuth 并更新全局登录态。
103
+ *
104
+ * @param pluginName - 多步骤插件名称
105
+ *
106
+ * @example
107
+ * ```tsx
108
+ * function ClassCodeLoginFlow() {
109
+ * const {
110
+ * currentStepName, stepData, start, next, back, isLoading, error, isComplete,
111
+ * } = useMultiStepLogin<{ classCode: string; loginCode: string }>('class-code');
112
+ *
113
+ * if (isComplete) {
114
+ * return <Redirect to="/dashboard" />;
115
+ * }
116
+ *
117
+ * // 步骤 1:输入班级码
118
+ * if (currentStepName === 'verify-code' || currentStep === -1) {
119
+ * return <ClassCodeForm onSubmit={start} isLoading={isLoading} error={error} />;
120
+ * }
121
+ *
122
+ * // 步骤 2:选择学生
123
+ * if (currentStepName === 'select-student') {
124
+ * return (
125
+ * <StudentList
126
+ * students={stepData.students}
127
+ * onSelect={(userId) => next({ userId, classLoginToken: stepData.classLoginToken })}
128
+ * onBack={back}
129
+ * isLoading={isLoading}
130
+ * />
131
+ * );
132
+ * }
133
+ *
134
+ * // 步骤 3:获取 Token(自动执行,显示 loading)
135
+ * if (currentStepName === 'get-token') {
136
+ * return <LoadingSpinner text="登录中..." />;
137
+ * }
138
+ * }
139
+ * ```
140
+ */
141
+ declare function useMultiStepLogin<TCredentials = unknown>(pluginName: string): UseMultiStepLoginReturn<TCredentials>;
142
+
143
+ /**
144
+ * Hook 用于从外部注入登录态到 swr-login 体系。
145
+ *
146
+ * 适用场景:
147
+ * - 多步骤登录流程(如班级码登录、MFA)
148
+ * - 第三方 SDK 登录完成后同步状态
149
+ * - iframe / WebView 登录回调
150
+ *
151
+ * @example
152
+ * ```tsx
153
+ * function ExternalLoginCallback() {
154
+ * const { injectAuth } = useAuthInjector();
155
+ *
156
+ * const handleLoginComplete = async (token: string, user: User) => {
157
+ * await injectAuth({
158
+ * user,
159
+ * accessToken: token,
160
+ * expiresAt: Date.now() + 86400000,
161
+ * });
162
+ * router.push('/dashboard');
163
+ * };
164
+ * }
165
+ * ```
166
+ */
167
+ declare function useAuthInjector(): AuthInjector;
168
+
70
169
  declare const AUTH_KEY = "__swr_login_user__";
71
170
  interface UseUserReturn<T extends User = User> {
72
171
  /** Current authenticated user, or null if not logged in */
@@ -77,6 +176,21 @@ interface UseUserReturn<T extends User = User> {
77
176
  isAuthenticated: boolean;
78
177
  /** Error from fetching user data */
79
178
  error: Error | undefined;
179
+ /**
180
+ * Sticky error from the last `fetchUser` failure.
181
+ *
182
+ * Unlike `error`, this value is **not** cleared when the auth state
183
+ * transitions (e.g. from `authenticated` → `unauthenticated`).
184
+ * It persists until `fetchUser` succeeds again or `clearError()` is called.
185
+ *
186
+ * Useful for distinguishing "not logged in" from "account disabled" in
187
+ * AuthGuard or error-boundary components.
188
+ */
189
+ lastError: Error | undefined;
190
+ /**
191
+ * Manually reset `lastError` to `undefined`.
192
+ */
193
+ clearError: () => void;
80
194
  /**
81
195
  * Manually update cached user data.
82
196
  * Call with `null` to clear, or with a user object to update.
@@ -247,4 +361,4 @@ interface AuthContextValue {
247
361
  */
248
362
  declare function useAuthContext(): AuthContextValue;
249
363
 
250
- export { AUTH_KEY, type AuthContextValue, AuthGuard, type AuthGuardProps, SWRLoginProvider, type SWRLoginProviderProps, type SessionInfo, type UseLoginOptions, type UseLoginReturn, type UseLogoutOptions, type UseLogoutReturn, type UsePermissionReturn, type UseUserReturn, useAuthContext, useLogin, useLogout, usePermission, useSession, useUser };
364
+ export { AUTH_KEY, type AuthContextValue, AuthGuard, type AuthGuardProps, SWRLoginProvider, type SWRLoginProviderProps, type SessionInfo, type UseLoginOptions, type UseLoginReturn, type UseLogoutOptions, type UseLogoutReturn, type UseMultiStepLoginReturn, type UsePermissionReturn, type UseUserReturn, useAuthContext, useAuthInjector, useLogin, useLogout, useMultiStepLogin, usePermission, useSession, useUser };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AuthEventEmitter, AuthStateMachine, TokenManager, PluginManager, BroadcastSync, isTokenExpired } from '@swr-login/core';
1
+ import { AuthEventEmitter, AuthStateMachine, TokenManager, PluginManager, BroadcastSync, isMultiStepPlugin, isTokenExpired } from '@swr-login/core';
2
2
  import { createContext, useContext, useRef, useMemo, useEffect, useState, useCallback } from 'react';
3
3
  import { jsx, Fragment } from 'react/jsx-runtime';
4
4
  import useSWR, { mutate } from 'swr';
@@ -112,8 +112,87 @@ function SWRLoginProvider({ config, children }) {
112
112
  }, [contextValue]);
113
113
  return /* @__PURE__ */ jsx(AuthContext.Provider, { value: contextValue, children });
114
114
  }
115
+ var AUTH_KEY = "__swr_login_user__";
116
+ function useUser() {
117
+ const { tokenManager, stateMachine, config } = useAuthContext();
118
+ const lastErrorRef = useRef(void 0);
119
+ const retryCountRef = useRef(0);
120
+ const [, setTick] = useState(0);
121
+ const forceUpdate = useCallback(() => setTick((t) => t + 1), []);
122
+ const clearError = useCallback(() => {
123
+ lastErrorRef.current = void 0;
124
+ forceUpdate();
125
+ }, [forceUpdate]);
126
+ const fetcher = async () => {
127
+ const token = tokenManager.getAccessToken();
128
+ if (!token) return null;
129
+ if (tokenManager.isExpired()) {
130
+ try {
131
+ await tokenManager.refresh();
132
+ } catch {
133
+ return null;
134
+ }
135
+ }
136
+ if (config.fetchUser) {
137
+ const currentToken = tokenManager.getAccessToken();
138
+ if (!currentToken) return null;
139
+ return await config.fetchUser(currentToken);
140
+ }
141
+ return null;
142
+ };
143
+ const {
144
+ data,
145
+ error,
146
+ isLoading,
147
+ mutate: swrMutate
148
+ } = useSWR(AUTH_KEY, fetcher, {
149
+ revalidateOnFocus: true,
150
+ revalidateOnReconnect: true,
151
+ shouldRetryOnError: false
152
+ });
153
+ useEffect(() => {
154
+ if (error) {
155
+ lastErrorRef.current = error;
156
+ forceUpdate();
157
+ if (config.onFetchUserError) {
158
+ const action = config.onFetchUserError(error);
159
+ if (action === "retry" && retryCountRef.current < 1) {
160
+ retryCountRef.current += 1;
161
+ swrMutate();
162
+ } else if (action === "logout") {
163
+ tokenManager.clearTokens();
164
+ stateMachine.transition("unauthenticated");
165
+ }
166
+ }
167
+ } else if (data !== void 0 && !error) {
168
+ if (lastErrorRef.current !== void 0) {
169
+ lastErrorRef.current = void 0;
170
+ forceUpdate();
171
+ }
172
+ retryCountRef.current = 0;
173
+ }
174
+ }, [error, data, forceUpdate, config, tokenManager, stateMachine, swrMutate]);
175
+ const mutate2 = async (newData) => {
176
+ if (newData === void 0) {
177
+ await swrMutate();
178
+ } else {
179
+ await swrMutate(newData, { revalidate: false });
180
+ }
181
+ };
182
+ return {
183
+ user: data ?? null,
184
+ isLoading,
185
+ isAuthenticated: !!data,
186
+ error,
187
+ lastError: lastErrorRef.current,
188
+ clearError,
189
+ mutate: mutate2
190
+ };
191
+ }
192
+
193
+ // src/hooks/useLogin.ts
115
194
  function useLogin(pluginName) {
116
- const { pluginManager, stateMachine, config } = useAuthContext();
195
+ const { pluginManager, tokenManager, stateMachine, config } = useAuthContext();
117
196
  const [isLoading, setIsLoading] = useState(false);
118
197
  const [error, setError] = useState(null);
119
198
  const login = useCallback(
@@ -134,8 +213,28 @@ function useLogin(pluginName) {
134
213
  setIsLoading(true);
135
214
  setError(null);
136
215
  stateMachine.transition("authenticating");
216
+ const targetPlugin = pluginManager.getPlugin(resolvedPlugin);
217
+ if (targetPlugin && isMultiStepPlugin(targetPlugin)) {
218
+ const err = new Error(
219
+ `[swr-login] Plugin "${resolvedPlugin}" is a multi-step plugin. Use useMultiStepLogin() instead of useLogin() for multi-step login flows.`
220
+ );
221
+ setError(err);
222
+ stateMachine.transition("error");
223
+ setIsLoading(false);
224
+ throw err;
225
+ }
137
226
  try {
138
227
  const response = await pluginManager.login(resolvedPlugin, resolvedCredentials);
228
+ if (config.fetchUser && config.validateUserOnLogin !== false) {
229
+ try {
230
+ const user = await config.fetchUser(response.accessToken);
231
+ await mutate(AUTH_KEY, user, { revalidate: false });
232
+ } catch (fetchUserErr) {
233
+ tokenManager.clearTokens();
234
+ stateMachine.transition("unauthenticated");
235
+ throw fetchUserErr;
236
+ }
237
+ }
139
238
  stateMachine.transition("authenticated");
140
239
  if (config.cacheAdapter) {
141
240
  await config.cacheAdapter.setUser(response.user);
@@ -150,58 +249,140 @@ function useLogin(pluginName) {
150
249
  setIsLoading(false);
151
250
  }
152
251
  },
153
- [pluginManager, stateMachine, config, pluginName]
252
+ [pluginManager, tokenManager, stateMachine, config, pluginName]
154
253
  );
155
254
  const reset = useCallback(() => {
156
255
  setError(null);
157
256
  }, []);
158
257
  return { login, isLoading, error, reset };
159
258
  }
160
- var AUTH_KEY = "__swr_login_user__";
161
- function useUser() {
162
- const { tokenManager, config } = useAuthContext();
163
- const fetcher = async () => {
164
- const token = tokenManager.getAccessToken();
165
- if (!token) return null;
166
- if (tokenManager.isExpired()) {
259
+ function useMultiStepLogin(pluginName) {
260
+ const { pluginManager, stateMachine, config } = useAuthContext();
261
+ const [currentStep, setCurrentStep] = useState(-1);
262
+ const [stepData, setStepData] = useState(null);
263
+ const [isLoading, setIsLoading] = useState(false);
264
+ const [error, setError] = useState(null);
265
+ const [isComplete, setIsComplete] = useState(false);
266
+ const [authResponse, setAuthResponse] = useState(null);
267
+ const stepOutputsRef = useRef([]);
268
+ const steps = pluginManager.getSteps(pluginName);
269
+ const totalSteps = steps?.length ?? 0;
270
+ const currentStepName = currentStep >= 0 && steps ? steps[currentStep]?.name ?? null : null;
271
+ const executeStepAt = useCallback(
272
+ async (stepIndex, input) => {
273
+ setIsLoading(true);
274
+ setError(null);
167
275
  try {
168
- await tokenManager.refresh();
169
- } catch {
170
- return null;
276
+ const output = await pluginManager.executeStep(pluginName, stepIndex, input);
277
+ stepOutputsRef.current[stepIndex] = output;
278
+ setStepData(output);
279
+ setCurrentStep(stepIndex);
280
+ if (stepIndex === totalSteps - 1) {
281
+ const response = await pluginManager.finalizeMultiStep(pluginName, output);
282
+ stateMachine.transition("authenticated");
283
+ if (config.cacheAdapter) {
284
+ await config.cacheAdapter.setUser(response.user);
285
+ }
286
+ setAuthResponse(response);
287
+ setIsComplete(true);
288
+ }
289
+ } catch (err) {
290
+ const authError = err instanceof Error ? err : new Error("Step execution failed");
291
+ setError(authError);
292
+ throw authError;
293
+ } finally {
294
+ setIsLoading(false);
171
295
  }
296
+ },
297
+ [pluginManager, pluginName, totalSteps, stateMachine, config]
298
+ );
299
+ const start = useCallback(
300
+ async (credentials) => {
301
+ stepOutputsRef.current = [];
302
+ setCurrentStep(-1);
303
+ setStepData(null);
304
+ setIsComplete(false);
305
+ setAuthResponse(null);
306
+ setError(null);
307
+ stateMachine.transition("authenticating");
308
+ await executeStepAt(0, credentials);
309
+ },
310
+ [executeStepAt, stateMachine]
311
+ );
312
+ const next = useCallback(
313
+ async (input) => {
314
+ const nextIndex = currentStep + 1;
315
+ if (nextIndex >= totalSteps) {
316
+ throw new Error("[swr-login] No more steps to execute.");
317
+ }
318
+ await executeStepAt(nextIndex, input);
319
+ },
320
+ [currentStep, totalSteps, executeStepAt]
321
+ );
322
+ const back = useCallback(() => {
323
+ if (currentStep > 0) {
324
+ const prevStep = currentStep - 1;
325
+ setCurrentStep(prevStep);
326
+ setStepData(stepOutputsRef.current[prevStep] ?? null);
327
+ setError(null);
172
328
  }
173
- if (config.fetchUser) {
174
- const currentToken = tokenManager.getAccessToken();
175
- if (!currentToken) return null;
176
- return await config.fetchUser(currentToken);
177
- }
178
- return null;
179
- };
180
- const {
181
- data,
182
- error,
183
- isLoading,
184
- mutate: swrMutate
185
- } = useSWR(AUTH_KEY, fetcher, {
186
- revalidateOnFocus: true,
187
- revalidateOnReconnect: true,
188
- shouldRetryOnError: false
189
- });
190
- const mutate2 = async (newData) => {
191
- if (newData === void 0) {
192
- await swrMutate();
193
- } else {
194
- await swrMutate(newData, { revalidate: false });
195
- }
196
- };
329
+ }, [currentStep]);
330
+ const cancel = useCallback(() => {
331
+ setCurrentStep(-1);
332
+ setStepData(null);
333
+ setIsLoading(false);
334
+ setError(null);
335
+ setIsComplete(false);
336
+ setAuthResponse(null);
337
+ stepOutputsRef.current = [];
338
+ stateMachine.transition("unauthenticated");
339
+ }, [stateMachine]);
340
+ const reset = useCallback(() => {
341
+ setError(null);
342
+ }, []);
197
343
  return {
198
- user: data ?? null,
344
+ currentStep,
345
+ currentStepName,
346
+ totalSteps,
347
+ start,
348
+ next,
349
+ back,
350
+ cancel,
351
+ stepData,
199
352
  isLoading,
200
- isAuthenticated: !!data,
201
353
  error,
202
- mutate: mutate2
354
+ reset,
355
+ isComplete,
356
+ authResponse
203
357
  };
204
358
  }
359
+ function useAuthInjector() {
360
+ const { tokenManager, emitter, stateMachine, config } = useAuthContext();
361
+ const injectAuth = useCallback(
362
+ async (response) => {
363
+ tokenManager.setTokens({
364
+ accessToken: response.accessToken,
365
+ refreshToken: response.refreshToken,
366
+ expiresAt: response.expiresAt
367
+ });
368
+ stateMachine.transition("authenticated");
369
+ emitter.emit("login", { user: response.user });
370
+ if (config.cacheAdapter) {
371
+ await config.cacheAdapter.setUser(response.user);
372
+ }
373
+ },
374
+ [tokenManager, emitter, stateMachine, config]
375
+ );
376
+ const injectLogout = useCallback(async () => {
377
+ tokenManager.clearTokens();
378
+ stateMachine.transition("unauthenticated");
379
+ emitter.emit("logout", void 0);
380
+ if (config.cacheAdapter) {
381
+ await config.cacheAdapter.clearUser();
382
+ }
383
+ }, [tokenManager, emitter, stateMachine, config]);
384
+ return { injectAuth, injectLogout };
385
+ }
205
386
  function useLogout() {
206
387
  const { pluginManager, stateMachine, broadcastSync } = useAuthContext();
207
388
  const [isLoading, setIsLoading] = useState(false);
@@ -325,6 +506,6 @@ function AuthGuard({
325
506
  return /* @__PURE__ */ jsx(Fragment, { children });
326
507
  }
327
508
 
328
- export { AUTH_KEY, AuthGuard, SWRLoginProvider, useAuthContext, useLogin, useLogout, usePermission, useSession, useUser };
509
+ export { AUTH_KEY, AuthGuard, SWRLoginProvider, useAuthContext, useAuthInjector, useLogin, useLogout, useMultiStepLogin, usePermission, useSession, useUser };
329
510
  //# sourceMappingURL=index.js.map
330
511
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/hooks/useLogin.ts","../src/hooks/useUser.ts","../src/hooks/useLogout.ts","../src/hooks/useSession.ts","../src/hooks/usePermission.ts","../src/components/AuthGuard.tsx"],"names":["mutate","useState","useCallback","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 { AuthResponse } from '@swr-login/core';\nimport { useCallback, useState } from 'react';\nimport { useAuthContext } from '../context';\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, 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 try {\n const response = await pluginManager.login(resolvedPlugin, resolvedCredentials);\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, stateMachine, config, pluginName],\n );\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return { login, isLoading, error, reset };\n}\n","import type { User } from '@swr-login/core';\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 * 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, config } = useAuthContext();\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 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 mutate,\n };\n}\n\n/** SWR cache key used for user data. Exported for advanced usage. */\nexport { AUTH_KEY };\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 { 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/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 // ── validateUserOnLogin:在 plugin 成功后调用 fetchUser 验证用户状态 ──\n if (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 { 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.2.0",
3
+ "version": "0.4.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.2.0"
30
+ "@swr-login/core": "0.4.0"
31
31
  },
32
32
  "keywords": [
33
33
  "swr",