najm-auth 1.1.15 → 1.1.17

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.
@@ -26,6 +26,7 @@ interface AuthState {
26
26
  interface DecodedToken {
27
27
  userId: string;
28
28
  jti?: string;
29
+ sessionVersion?: number;
29
30
  roles?: string[];
30
31
  permissions?: string[];
31
32
  exp?: number;
@@ -79,8 +80,16 @@ interface AuthClientConfig {
79
80
  /**
80
81
  * Auth event types
81
82
  */
82
- type AuthEvent = 'login' | 'logout' | 'tokenRefresh' | 'sessionExpired' | 'stateChange' | 'userUpdated';
83
- type AuthEventHandler<T = unknown> = (data: T) => void;
83
+ interface AuthEventMap {
84
+ login: AuthUser;
85
+ logout: null;
86
+ tokenRefresh: null;
87
+ sessionExpired: null;
88
+ stateChange: AuthState;
89
+ userUpdated: AuthUser;
90
+ }
91
+ type AuthEvent = keyof AuthEventMap;
92
+ type AuthEventHandler<K extends AuthEvent = AuthEvent> = (data: AuthEventMap[K]) => void;
84
93
  /**
85
94
  * Tab sync message types
86
95
  */
@@ -142,4 +151,4 @@ declare class FetchClient {
142
151
  private parseBody;
143
152
  }
144
153
 
145
- export { AuthError as A, type DecodedToken as D, FetchClient as F, type RetryConfig as R, type SyncPayload as S, type TabSyncMessage as T, type AuthClientConfig as a, type AuthState as b, type AuthUser as c, type AuthEvent as d, type AuthEventHandler as e, type ServerResponse as f, type TokenPair as g, type RequestOptions as h };
154
+ export { AuthError as A, type DecodedToken as D, FetchClient as F, type RetryConfig as R, type SyncPayload as S, type TabSyncMessage as T, type AuthClientConfig as a, type AuthEventMap as b, type AuthState as c, type AuthUser as d, type AuthEvent as e, type AuthEventHandler as f, type ServerResponse as g, type TokenPair as h, type RequestOptions as i };
@@ -1,4 +1,4 @@
1
- import { F as FetchClient, a as AuthClientConfig, c as AuthUser, b as AuthState, d as AuthEvent, e as AuthEventHandler } from './FetchClient-DbrTaCVq.js';
1
+ import { F as FetchClient, a as AuthClientConfig, d as AuthUser, c as AuthState, e as AuthEvent, f as AuthEventHandler } from './FetchClient-aatsED1a.js';
2
2
 
3
3
  interface HydrateSession {
4
4
  user: AuthUser | null;
@@ -8,10 +8,14 @@ interface HydrateSession {
8
8
  }
9
9
  declare class NajmAuthClient {
10
10
  private config;
11
+ private static readonly MAX_REFRESH_FAILURES;
12
+ private static readonly CIRCUIT_RESET_MS;
11
13
  private state;
12
14
  private refreshTimer;
15
+ private refreshCircuitTimer;
13
16
  private refreshPromise;
14
17
  private fetchUserPromise;
18
+ private refreshFailures;
15
19
  private _hydrated;
16
20
  private listeners;
17
21
  private eventListeners;
@@ -32,6 +36,10 @@ declare class NajmAuthClient {
32
36
  forgotPassword(data: {
33
37
  email: string;
34
38
  }): Promise<void>;
39
+ changePassword(data: {
40
+ currentPassword: string;
41
+ newPassword: string;
42
+ }): Promise<void>;
35
43
  resetPassword(data: {
36
44
  token: string;
37
45
  newPassword: string;
@@ -51,15 +59,19 @@ declare class NajmAuthClient {
51
59
  */
52
60
  hydrate(session: HydrateSession | null): void;
53
61
  isHydrated(): boolean;
54
- on<T = unknown>(event: AuthEvent, handler: AuthEventHandler<T>): void;
55
- off(event: AuthEvent, handler: AuthEventHandler<any>): void;
62
+ on<K extends AuthEvent>(event: K, handler: AuthEventHandler<K>): void;
63
+ off<K extends AuthEvent>(event: K, handler: AuthEventHandler<K>): void;
56
64
  subscribe(listener: (state: AuthState) => void): () => void;
57
65
  destroy(): void;
66
+ private _refreshWithCircuit;
58
67
  private _doRefresh;
59
68
  private handleUnauthorized;
60
69
  private applyTokens;
61
70
  private scheduleRefresh;
62
71
  private clearRefreshTimer;
72
+ private clearRefreshCircuitTimer;
73
+ private resetRefreshFailures;
74
+ private registerRefreshFailure;
63
75
  private resetState;
64
76
  private getSyncPayload;
65
77
  private handleTabMessage;
@@ -1,6 +1,6 @@
1
- export { N as NajmAuthClient, c as createAuthClient } from '../NajmAuthClient-C6yy4mxL.js';
2
- import { D as DecodedToken, T as TabSyncMessage, S as SyncPayload } from '../FetchClient-DbrTaCVq.js';
3
- export { a as AuthClientConfig, A as AuthError, d as AuthEvent, e as AuthEventHandler, b as AuthState, c as AuthUser, F as FetchClient, h as RequestOptions, R as RetryConfig, f as ServerResponse, g as TokenPair } from '../FetchClient-DbrTaCVq.js';
1
+ export { H as HydrateSession, N as NajmAuthClient, c as createAuthClient } from '../NajmAuthClient-FCwA8LIg.js';
2
+ import { D as DecodedToken, T as TabSyncMessage, S as SyncPayload } from '../FetchClient-aatsED1a.js';
3
+ export { a as AuthClientConfig, A as AuthError, e as AuthEvent, f as AuthEventHandler, b as AuthEventMap, c as AuthState, d as AuthUser, F as FetchClient, i as RequestOptions, R as RetryConfig, g as ServerResponse, h as TokenPair } from '../FetchClient-aatsED1a.js';
4
4
 
5
5
  /**
6
6
  * Decode a JWT token payload without verification.
@@ -199,7 +199,7 @@ var INITIAL_STATE = {
199
199
  roles: [],
200
200
  permissions: []
201
201
  };
202
- var NajmAuthClient = class {
202
+ var NajmAuthClient = class _NajmAuthClient {
203
203
  constructor(config) {
204
204
  this.config = config;
205
205
  this.prefix = config.authPrefix ?? "/auth";
@@ -220,10 +220,14 @@ var NajmAuthClient = class {
220
220
  static {
221
221
  __name(this, "NajmAuthClient");
222
222
  }
223
+ static MAX_REFRESH_FAILURES = 3;
224
+ static CIRCUIT_RESET_MS = 6e4;
223
225
  state = { ...INITIAL_STATE };
224
226
  refreshTimer = null;
227
+ refreshCircuitTimer = null;
225
228
  refreshPromise = null;
226
229
  fetchUserPromise = null;
230
+ refreshFailures = 0;
227
231
  _hydrated = false;
228
232
  // Subscriptions (for React useSyncExternalStore)
229
233
  listeners = /* @__PURE__ */ new Set();
@@ -270,8 +274,11 @@ var NajmAuthClient = class {
270
274
  this.emit("logout", null);
271
275
  }
272
276
  async refresh() {
277
+ if (this.refreshFailures >= _NajmAuthClient.MAX_REFRESH_FAILURES) {
278
+ throw new Error("Session expired (circuit open)");
279
+ }
273
280
  if (!this.refreshPromise) {
274
- this.refreshPromise = this._doRefresh().finally(() => {
281
+ this.refreshPromise = this._refreshWithCircuit().finally(() => {
275
282
  this.refreshPromise = null;
276
283
  });
277
284
  }
@@ -290,8 +297,8 @@ var NajmAuthClient = class {
290
297
  const res = await this.api.get(`${this.prefix}/me`);
291
298
  this.state = { ...this.state, user: res.data };
292
299
  this.notify();
293
- this.emit("userUpdated", this.state.user);
294
- return this.state.user;
300
+ this.emit("userUpdated", res.data);
301
+ return res.data;
295
302
  } catch {
296
303
  return null;
297
304
  }
@@ -299,6 +306,12 @@ var NajmAuthClient = class {
299
306
  async forgotPassword(data) {
300
307
  await this.api.post(`${this.prefix}/forgot-password`, { body: data, skipAuth: true });
301
308
  }
309
+ async changePassword(data) {
310
+ await this.api.post(`${this.prefix}/change-password`, { body: data });
311
+ this.resetState();
312
+ this.tabSync?.broadcastLogout();
313
+ this.emit("logout", null);
314
+ }
302
315
  async resetPassword(data) {
303
316
  await this.api.post(`${this.prefix}/reset-password`, { body: data, skipAuth: true });
304
317
  }
@@ -343,6 +356,7 @@ var NajmAuthClient = class {
343
356
  hydrate(session) {
344
357
  if (this._hydrated) return;
345
358
  this._hydrated = true;
359
+ this.resetRefreshFailures();
346
360
  if (!session || !session.user) {
347
361
  this.state = { ...INITIAL_STATE };
348
362
  this.notify();
@@ -392,8 +406,10 @@ var NajmAuthClient = class {
392
406
  // =========================================================================
393
407
  destroy() {
394
408
  this.clearRefreshTimer();
409
+ this.clearRefreshCircuitTimer();
395
410
  this.tabSync?.destroy();
396
411
  this.tabSync = null;
412
+ this.refreshFailures = 0;
397
413
  this.state = { ...INITIAL_STATE };
398
414
  this.listeners.clear();
399
415
  this.eventListeners.clear();
@@ -401,24 +417,32 @@ var NajmAuthClient = class {
401
417
  // =========================================================================
402
418
  // Internals
403
419
  // =========================================================================
404
- async _doRefresh() {
420
+ async _refreshWithCircuit() {
405
421
  try {
406
- const res = await this.api.post(
407
- `${this.prefix}/refresh`,
408
- { skipAuth: true }
409
- );
410
- this.applyTokens(res.data);
411
- this.tabSync?.broadcastSync(this.getSyncPayload());
412
- this.emit("tokenRefresh", null);
422
+ await this._doRefresh();
423
+ this.resetRefreshFailures();
413
424
  } catch (err) {
414
- if (err instanceof AuthError && err.status === 401) {
425
+ const shouldOpenCircuit = this.registerRefreshFailure(err);
426
+ if (shouldOpenCircuit) {
415
427
  this.resetState();
416
428
  this.emit("sessionExpired", null);
417
- throw new Error("Session expired");
429
+ if (err instanceof AuthError && err.status === 401) {
430
+ throw new Error("Session expired");
431
+ }
432
+ throw new Error("Session expired (circuit open)");
418
433
  }
419
434
  throw err;
420
435
  }
421
436
  }
437
+ async _doRefresh() {
438
+ const res = await this.api.post(
439
+ `${this.prefix}/refresh`,
440
+ { skipAuth: true }
441
+ );
442
+ this.applyTokens(res.data);
443
+ this.tabSync?.broadcastSync(this.getSyncPayload());
444
+ this.emit("tokenRefresh", null);
445
+ }
422
446
  async handleUnauthorized() {
423
447
  try {
424
448
  await this.refresh();
@@ -428,6 +452,7 @@ var NajmAuthClient = class {
428
452
  }
429
453
  }
430
454
  applyTokens(tokens) {
455
+ this.resetRefreshFailures();
431
456
  const decoded = decodeToken(tokens.accessToken);
432
457
  this.state = {
433
458
  ...this.state,
@@ -454,6 +479,32 @@ var NajmAuthClient = class {
454
479
  this.refreshTimer = null;
455
480
  }
456
481
  }
482
+ clearRefreshCircuitTimer() {
483
+ if (this.refreshCircuitTimer) {
484
+ clearTimeout(this.refreshCircuitTimer);
485
+ this.refreshCircuitTimer = null;
486
+ }
487
+ }
488
+ resetRefreshFailures() {
489
+ this.refreshFailures = 0;
490
+ this.clearRefreshCircuitTimer();
491
+ }
492
+ registerRefreshFailure(err) {
493
+ if (err instanceof AuthError && err.status === 401) {
494
+ this.refreshFailures = _NajmAuthClient.MAX_REFRESH_FAILURES;
495
+ } else {
496
+ this.refreshFailures += 1;
497
+ }
498
+ if (this.refreshFailures >= _NajmAuthClient.MAX_REFRESH_FAILURES) {
499
+ this.clearRefreshCircuitTimer();
500
+ this.refreshCircuitTimer = setTimeout(() => {
501
+ this.refreshFailures = 0;
502
+ this.refreshCircuitTimer = null;
503
+ }, _NajmAuthClient.CIRCUIT_RESET_MS);
504
+ return true;
505
+ }
506
+ return false;
507
+ }
457
508
  resetState() {
458
509
  this.clearRefreshTimer();
459
510
  this.state = { ...INITIAL_STATE };
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, CSSProperties, ReactElement } from 'react';
4
- import { N as NajmAuthClient, H as HydrateSession } from '../../NajmAuthClient-C6yy4mxL.js';
5
- import { b as AuthState, c as AuthUser, A as AuthError } from '../../FetchClient-DbrTaCVq.js';
4
+ import { N as NajmAuthClient, H as HydrateSession } from '../../NajmAuthClient-FCwA8LIg.js';
5
+ import { c as AuthState, d as AuthUser, A as AuthError, e as AuthEvent, b as AuthEventMap } from '../../FetchClient-aatsED1a.js';
6
6
 
7
7
  interface AuthProviderProps {
8
8
  client: NajmAuthClient;
@@ -16,6 +16,8 @@ interface AuthProviderProps {
16
16
  }
17
17
  declare function AuthProvider({ client, children, initialSession }: AuthProviderProps): react_jsx_runtime.JSX.Element;
18
18
 
19
+ declare function useAuthClient(): NajmAuthClient;
20
+
19
21
  /**
20
22
  * Subscribe to the full auth state.
21
23
  * Re-renders when any part of the state changes.
@@ -50,6 +52,19 @@ interface UseSessionReturn extends AuthState {
50
52
  */
51
53
  declare function useSession(opts?: UseSessionOptions): UseSessionReturn;
52
54
 
55
+ interface UsePermissionsReturn {
56
+ can: (permission: string) => boolean;
57
+ hasRole: (role: string) => boolean;
58
+ hasAnyRole: (roles: string[]) => boolean;
59
+ permissions: string[];
60
+ roles: string[];
61
+ }
62
+ /**
63
+ * Access RBAC/PBAC checks as hooks.
64
+ * Permissions are decoded from JWT — no round-trip needed.
65
+ */
66
+ declare function usePermissions(): UsePermissionsReturn;
67
+
53
68
  interface UseLoginOptions {
54
69
  onSuccess?: (user: AuthUser) => void;
55
70
  onError?: (error: AuthError | Error) => void;
@@ -64,6 +79,17 @@ interface UseLoginReturn {
64
79
  }
65
80
  declare function useLogin(opts?: UseLoginOptions): UseLoginReturn;
66
81
 
82
+ interface UseRegisterOptions {
83
+ onSuccess?: (user: AuthUser) => void;
84
+ onError?: (error: AuthError | Error) => void;
85
+ }
86
+ interface UseRegisterReturn {
87
+ register: (data: Record<string, unknown>) => Promise<void>;
88
+ isLoading: boolean;
89
+ error: AuthError | Error | null;
90
+ }
91
+ declare function useRegister(opts?: UseRegisterOptions): UseRegisterReturn;
92
+
67
93
  interface UseLogoutOptions {
68
94
  onSuccess?: () => void;
69
95
  onError?: (error: Error) => void;
@@ -74,29 +100,89 @@ interface UseLogoutReturn {
74
100
  }
75
101
  declare function useLogout(opts?: UseLogoutOptions): UseLogoutReturn;
76
102
 
77
- interface UseRegisterOptions {
78
- onSuccess?: (user: AuthUser) => void;
103
+ interface UseForgotPasswordOptions {
104
+ onSuccess?: () => void;
79
105
  onError?: (error: AuthError | Error) => void;
80
106
  }
81
- interface UseRegisterReturn {
82
- register: (data: Record<string, unknown>) => Promise<void>;
107
+ interface UseForgotPasswordReturn {
108
+ forgotPassword: (data: {
109
+ email: string;
110
+ }) => Promise<void>;
83
111
  isLoading: boolean;
84
112
  error: AuthError | Error | null;
113
+ isSuccess: boolean;
114
+ reset: () => void;
85
115
  }
86
- declare function useRegister(opts?: UseRegisterOptions): UseRegisterReturn;
116
+ declare function useForgotPassword(opts?: UseForgotPasswordOptions): UseForgotPasswordReturn;
87
117
 
88
- interface UsePermissionsReturn {
89
- can: (permission: string) => boolean;
90
- hasRole: (role: string) => boolean;
91
- hasAnyRole: (roles: string[]) => boolean;
92
- permissions: string[];
93
- roles: string[];
118
+ interface UseResetPasswordOptions {
119
+ onSuccess?: () => void;
120
+ onError?: (error: AuthError | Error) => void;
121
+ }
122
+ interface UseResetPasswordReturn {
123
+ resetPassword: (data: {
124
+ token: string;
125
+ newPassword: string;
126
+ }) => Promise<void>;
127
+ isLoading: boolean;
128
+ error: AuthError | Error | null;
129
+ isSuccess: boolean;
130
+ reset: () => void;
131
+ }
132
+ declare function useResetPassword(opts?: UseResetPasswordOptions): UseResetPasswordReturn;
133
+
134
+ interface UseChangePasswordOptions {
135
+ onSuccess?: () => void;
136
+ onError?: (error: AuthError | Error) => void;
137
+ }
138
+ interface UseChangePasswordReturn {
139
+ changePassword: (data: {
140
+ currentPassword: string;
141
+ newPassword: string;
142
+ }) => Promise<void>;
143
+ isLoading: boolean;
144
+ error: AuthError | Error | null;
145
+ isSuccess: boolean;
146
+ reset: () => void;
94
147
  }
148
+ declare function useChangePassword(opts?: UseChangePasswordOptions): UseChangePasswordReturn;
149
+
95
150
  /**
96
- * Access RBAC/PBAC checks as hooks.
97
- * Permissions are decoded from JWT no round-trip needed.
151
+ * Subscribe to a specific auth event. Cleans up on unmount.
152
+ * Uses a stable ref for the handler to avoid re-subscribing on every render.
153
+ *
154
+ * @example
155
+ * ```tsx
156
+ * useAuthEvent('sessionExpired', () => {
157
+ * toast.error('Session expired, please log in again');
158
+ * router.push('/login');
159
+ * });
160
+ *
161
+ * useAuthEvent('login', (user) => {
162
+ * analytics.track('login', { userId: user.id });
163
+ * });
164
+ * ```
98
165
  */
99
- declare function usePermissions(): UsePermissionsReturn;
166
+ declare function useAuthEvent<K extends AuthEvent>(event: K, handler: (data: AuthEventMap[K]) => void): void;
167
+
168
+ interface AuthEventEntry {
169
+ event: AuthEvent;
170
+ data: unknown;
171
+ timestamp: number;
172
+ }
173
+ /**
174
+ * Subscribe to all auth events. Returns a log of recent events.
175
+ * Useful for logging, debug panels, or analytics.
176
+ *
177
+ * @example
178
+ * ```tsx
179
+ * const events = useAuthEvents({ maxEntries: 50 });
180
+ * // events = [{ event: 'login', data: {...}, timestamp: 1234 }, ...]
181
+ * ```
182
+ */
183
+ declare function useAuthEvents(opts?: {
184
+ maxEntries?: number;
185
+ }): AuthEventEntry[];
100
186
 
101
187
  interface SignedInProps {
102
188
  children: ReactNode;
@@ -175,6 +261,52 @@ interface CanProps {
175
261
  */
176
262
  declare function Can({ permission, role, children, fallback }: CanProps): react_jsx_runtime.JSX.Element;
177
263
 
264
+ interface RoleProps {
265
+ /** Required role name */
266
+ is?: string;
267
+ /** Allow any of these roles */
268
+ any?: string[];
269
+ children: ReactNode;
270
+ fallback?: ReactNode;
271
+ }
272
+ /**
273
+ * Role-based conditional rendering gate.
274
+ *
275
+ * @example
276
+ * ```tsx
277
+ * <Role is="admin">
278
+ * <AdminPanel />
279
+ * </Role>
280
+ *
281
+ * <Role any={['admin', 'editor']} fallback={<p>No access</p>}>
282
+ * <ContentEditor />
283
+ * </Role>
284
+ * ```
285
+ */
286
+ declare function Role(props: RoleProps): react_jsx_runtime.JSX.Element;
287
+
288
+ interface IfAuthProps {
289
+ /** Render when authenticated — receives user */
290
+ authenticated: (user: AuthUser) => ReactNode;
291
+ /** Render when not authenticated */
292
+ unauthenticated?: () => ReactNode;
293
+ /** Render during loading */
294
+ loading?: () => ReactNode;
295
+ }
296
+ /**
297
+ * Render-prop component for full auth state branching.
298
+ *
299
+ * @example
300
+ * ```tsx
301
+ * <IfAuth
302
+ * loading={() => <Skeleton />}
303
+ * authenticated={(user) => <p>Welcome, {user.name}</p>}
304
+ * unauthenticated={() => <Link href="/login">Sign in</Link>}
305
+ * />
306
+ * ```
307
+ */
308
+ declare function IfAuth({ authenticated, unauthenticated, loading }: IfAuthProps): react_jsx_runtime.JSX.Element;
309
+
178
310
  interface ProtectedProps {
179
311
  children: ReactNode;
180
312
  /** URL to redirect unauthenticated users to */
@@ -221,6 +353,32 @@ interface UserNameProps {
221
353
  */
222
354
  declare function UserName({ fallback }: UserNameProps): react_jsx_runtime.JSX.Element;
223
355
 
356
+ interface UserEmailProps {
357
+ fallback?: string;
358
+ }
359
+ /**
360
+ * Renders the user's email as plain text.
361
+ *
362
+ * @example
363
+ * ```tsx
364
+ * <p>Email: <UserEmail fallback="Not available" /></p>
365
+ * ```
366
+ */
367
+ declare function UserEmail({ fallback }: UserEmailProps): react_jsx_runtime.JSX.Element;
368
+
369
+ interface UserRoleProps {
370
+ fallback?: string;
371
+ }
372
+ /**
373
+ * Renders the user's primary role name.
374
+ *
375
+ * @example
376
+ * ```tsx
377
+ * <span>Role: <UserRole fallback="none" /></span>
378
+ * ```
379
+ */
380
+ declare function UserRole({ fallback }: UserRoleProps): react_jsx_runtime.JSX.Element;
381
+
224
382
  interface UserAvatarProps {
225
383
  /** Pixel size of the avatar (default: 32) */
226
384
  size?: number;
@@ -247,6 +405,24 @@ interface UserAvatarProps {
247
405
  */
248
406
  declare function UserAvatar({ size, className, style, alt }: UserAvatarProps): react_jsx_runtime.JSX.Element;
249
407
 
408
+ interface PermissionListProps {
409
+ /** Render function for each permission */
410
+ children: (permission: string, index: number) => ReactNode;
411
+ /** Rendered when the user has no permissions */
412
+ fallback?: ReactNode;
413
+ }
414
+ /**
415
+ * Iterates over the user's permissions with a render function.
416
+ *
417
+ * @example
418
+ * ```tsx
419
+ * <PermissionList fallback={<p>No permissions</p>}>
420
+ * {(perm) => <Badge key={perm}>{perm}</Badge>}
421
+ * </PermissionList>
422
+ * ```
423
+ */
424
+ declare function PermissionList({ children, fallback }: PermissionListProps): react_jsx_runtime.JSX.Element;
425
+
250
426
  interface SignOutButtonProps {
251
427
  /** A single clickable child element. Its `onClick` will be wired to logout. */
252
428
  children: ReactNode;
@@ -275,4 +451,45 @@ declare function SignOutButton({ children, onSuccess, onError }: SignOutButtonPr
275
451
  disabled?: boolean;
276
452
  }, string | react.JSXElementConstructor<any>>;
277
453
 
278
- export { AuthLoading, AuthProvider, Can, Protected, type SessionStatus, SignOutButton, SignedIn, SignedOut, UserAvatar, UserName, useAuth, useLogin, useLogout, usePermissions, useRegister, useSession, useUser };
454
+ interface LoginButtonProps {
455
+ /** Where to navigate on click (default: '/login') */
456
+ href?: string;
457
+ children: ReactNode;
458
+ }
459
+ /**
460
+ * Wires a child element's onClick to navigate to the login page.
461
+ * Headless — bring your own button.
462
+ *
463
+ * @example
464
+ * ```tsx
465
+ * <SignedOut>
466
+ * <LoginButton href="/login">
467
+ * <Button>Sign in</Button>
468
+ * </LoginButton>
469
+ * </SignedOut>
470
+ * ```
471
+ */
472
+ declare function LoginButton({ href, children }: LoginButtonProps): ReactElement<{
473
+ onClick?: (e: unknown) => void;
474
+ }, string | react.JSXElementConstructor<any>>;
475
+
476
+ interface RedirectToLoginProps {
477
+ /** Login URL (default: '/login') */
478
+ to?: string;
479
+ /** Preserve current URL as ?from= param (default: true) */
480
+ preserveFrom?: boolean;
481
+ }
482
+ /**
483
+ * Immediately redirects to the login page. Renders nothing.
484
+ * Use inside <SignedOut> or <Protected fallback={...}>.
485
+ *
486
+ * @example
487
+ * ```tsx
488
+ * <SignedOut>
489
+ * <RedirectToLogin to="/login" />
490
+ * </SignedOut>
491
+ * ```
492
+ */
493
+ declare function RedirectToLogin({ to, preserveFrom }: RedirectToLoginProps): any;
494
+
495
+ export { type AuthEventEntry, AuthLoading, AuthProvider, Can, IfAuth, LoginButton, PermissionList, Protected, RedirectToLogin, Role, type SessionStatus, SignOutButton, SignedIn, SignedOut, UserAvatar, UserEmail, UserName, UserRole, useAuth, useAuthClient, useAuthEvent, useAuthEvents, useChangePassword, useForgotPassword, useLogin, useLogout, usePermissions, useRegister, useResetPassword, useSession, useUser };