next-token-auth 1.0.18 → 1.0.19

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/README.md CHANGED
@@ -163,13 +163,18 @@ Your `AuthProvider` calls these automatically. You never call them directly.
163
163
  ```tsx
164
164
  // app/layout.tsx
165
165
  import { AuthProvider } from "next-token-auth/react";
166
+ import { getLayoutSession } from "next-token-auth/server";
166
167
  import { clientAuthConfig } from "@/lib/auth.client";
168
+ import { authConfig } from "@/lib/auth";
169
+
170
+ export default async function RootLayout({ children }: { children: React.ReactNode }) {
171
+ // Pre-fetch session server-side to avoid auth flash on page load
172
+ const session = await getLayoutSession(authConfig);
167
173
 
168
- export default function RootLayout({ children }: { children: React.ReactNode }) {
169
174
  return (
170
175
  <html lang="en">
171
176
  <body>
172
- <AuthProvider config={clientAuthConfig}>
177
+ <AuthProvider config={clientAuthConfig} initialSession={session}>
173
178
  {children}
174
179
  </AuthProvider>
175
180
  </body>
@@ -178,6 +183,8 @@ export default function RootLayout({ children }: { children: React.ReactNode })
178
183
  }
179
184
  ```
180
185
 
186
+ **Why `initialSession`?** Without it, there's a flash of the wrong auth state on every page load while the client fetches the session. Pre-fetching server-side eliminates the flash — the correct auth state is rendered from the first paint.
187
+
181
188
  ---
182
189
 
183
190
  ### Step 5: Build your login page
@@ -312,6 +319,39 @@ If `secret` is in the client config, it gets bundled into your JavaScript and ex
312
319
 
313
320
  ---
314
321
 
322
+ ## Avoiding the Auth Flash
323
+
324
+ Without server-side session pre-fetching, there's a visible flash on every page load:
325
+
326
+ 1. Server sends HTML with `isLoading = true`
327
+ 2. Browser renders a skeleton or empty state
328
+ 3. Client fetches `/api/auth/session`
329
+ 4. UI flips to the correct auth state (flash visible)
330
+
331
+ To fix this, use `getLayoutSession` in your root layout:
332
+
333
+ ```tsx
334
+ // app/layout.tsx
335
+ import { getLayoutSession } from "next-token-auth/server";
336
+ import { authConfig } from "@/lib/auth";
337
+
338
+ export default async function RootLayout({ children }) {
339
+ const session = await getLayoutSession(authConfig);
340
+
341
+ return (
342
+ <AuthProvider config={clientAuthConfig} initialSession={session}>
343
+ {children}
344
+ </AuthProvider>
345
+ );
346
+ }
347
+ ```
348
+
349
+ Now the correct auth state is rendered from the first paint — no flash, no skeleton, no layout shift.
350
+
351
+ `getLayoutSession` reads and decrypts the session cookie server-side using `next/headers`, which is available in layouts and server components (unlike `getServerSession`, which requires a `NextRequest` and only works in middleware and route handlers).
352
+
353
+ ---
354
+
315
355
  ## API Reference
316
356
 
317
357
  ### `useAuth()`
@@ -438,6 +478,37 @@ This keeps tokens secure — they never leave the server.
438
478
 
439
479
  ---
440
480
 
481
+ ### `getLayoutSession(config)`
482
+
483
+ Reads and validates the session in layouts and server components using `next/headers`.
484
+
485
+ ```ts
486
+ // app/layout.tsx
487
+ import { getLayoutSession } from "next-token-auth/server";
488
+ import { authConfig } from "@/lib/auth";
489
+
490
+ export default async function RootLayout({ children }) {
491
+ const session = await getLayoutSession(authConfig);
492
+
493
+ return (
494
+ <AuthProvider config={clientAuthConfig} initialSession={session}>
495
+ {children}
496
+ </AuthProvider>
497
+ );
498
+ }
499
+ ```
500
+
501
+ What it does:
502
+ 1. Reads the encrypted session cookie via `next/headers`
503
+ 2. Decrypts it using your `secret`
504
+ 3. Validates token expiry
505
+ 4. Optionally fetches the user profile from your backend
506
+ 5. Returns `{ user, isAuthenticated }` (tokens are omitted for client safety)
507
+
508
+ Use this instead of `getServerSession` when you need the session in a layout or server component where `NextRequest` is not available.
509
+
510
+ ---
511
+
441
512
  ### `getServerSession(req, config)`
442
513
 
443
514
  Reads and validates the session in server components and API routes.
package/dist/index.js CHANGED
@@ -515,14 +515,13 @@ var AuthClient = class {
515
515
  }
516
516
  };
517
517
  var AuthContext = react.createContext(null);
518
- function AuthProvider({ config, children }) {
519
- const [session, setSession] = react.useState({
520
- user: null,
521
- tokens: null,
522
- isAuthenticated: false
523
- });
524
- const [isLoading, setIsLoading] = react.useState(true);
518
+ function AuthProvider({ config, children, initialSession }) {
519
+ const [session, setSession] = react.useState(
520
+ initialSession ?? { user: null, tokens: null, isAuthenticated: false }
521
+ );
522
+ const [isLoading, setIsLoading] = react.useState(!initialSession);
525
523
  react.useEffect(() => {
524
+ if (initialSession) return;
526
525
  let cancelled = false;
527
526
  fetch("/api/auth/session").then((res) => res.json()).then((data) => {
528
527
  if (!cancelled) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/expiry.ts","../src/core/HttpClient.ts","../src/core/SessionManager.ts","../src/utils/crypto.ts","../src/core/TokenManager.ts","../src/core/AuthClient.ts","../src/react/AuthProvider.tsx","../src/react/hooks/useAuth.ts","../src/react/hooks/useSession.ts","../src/react/hooks/useRequireAuth.ts"],"names":["createContext","useState","useEffect","useCallback","jsx","useContext"],"mappings":"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","file":"index.js","sourcesContent":["import type { ExpiryInput, ExpiryStrategy, LoginResponse } from \"../types\";\n\nconst UNIT_MAP: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n};\n\n/**\n * Parses an expiry value into seconds.\n * Accepts:\n * - number → treated as seconds\n * - string → e.g. \"15m\", \"2h\", \"2d\", \"7d\", \"1w\"\n *\n * @throws if the format is unrecognised\n */\nexport function parseExpiry(input?: ExpiryInput): number {\n if (input === undefined || input === null) {\n throw new Error(\"parseExpiry: no expiry value provided\");\n }\n\n if (typeof input === \"number\") {\n if (input <= 0) throw new Error(\"parseExpiry: value must be positive\");\n return input;\n }\n\n const trimmed = input.trim();\n\n // Pure numeric string\n if (/^\\d+$/.test(trimmed)) {\n return parseInt(trimmed, 10);\n }\n\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)\\s*([smhdw])$/i);\n if (!match) {\n throw new Error(\n `parseExpiry: unrecognised format \"${input}\". ` +\n `Expected a number or a string like \"15m\", \"2h\", \"2d\", \"7d\", \"1w\".`\n );\n }\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n return Math.floor(value * UNIT_MAP[unit]);\n}\n\n/**\n * Safely parses an expiry value, returning a fallback on failure.\n */\nexport function safeParseExpiry(\n input?: ExpiryInput,\n fallbackSeconds = 900\n): number {\n try {\n return parseExpiry(input);\n } catch {\n return fallbackSeconds;\n }\n}\n\n/**\n * Resolves the access token expiry timestamp (ms) from a login response\n * using the configured strategy.\n */\nexport function resolveAccessTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number {\n const now = Date.now();\n\n const fromBackend =\n response.accessTokenExpiresIn ?? response.expiresIn ?? undefined;\n\n if (strategy === \"backend\") {\n if (fromBackend === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"backend\" but API returned no expiry'\n );\n }\n return now + parseExpiry(fromBackend) * 1000;\n }\n\n if (strategy === \"config\") {\n if (configExpiry === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"config\" but no expiry configured'\n );\n }\n return now + parseExpiry(configExpiry) * 1000;\n }\n\n // hybrid: backend first, fallback to config\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n // Last resort: 15 minutes\n return now + 900 * 1000;\n}\n\n/**\n * Resolves the refresh token expiry timestamp (ms).\n */\nexport function resolveRefreshTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number | undefined {\n const now = Date.now();\n const fromBackend = response.refreshTokenExpiresIn;\n\n if (strategy === \"backend\") {\n return fromBackend !== undefined\n ? now + parseExpiry(fromBackend) * 1000\n : undefined;\n }\n\n if (strategy === \"config\") {\n return configExpiry !== undefined\n ? now + parseExpiry(configExpiry) * 1000\n : undefined;\n }\n\n // hybrid\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n return undefined;\n}\n","import type { AuthConfig } from \"../types\";\nimport type { TokenManager } from \"./TokenManager\";\n\ntype RefreshFn = () => Promise<boolean>;\n\n/**\n * Authenticated HTTP client that:\n * - Injects Authorization: Bearer <accessToken>\n * - Auto-refreshes on 401 and retries the original request once\n */\nexport class HttpClient {\n private readonly config: AuthConfig;\n private readonly tokenManager: TokenManager;\n private refreshFn: RefreshFn | null = null;\n private refreshPromise: Promise<boolean> | null = null;\n\n constructor(config: AuthConfig, tokenManager: TokenManager) {\n this.config = config;\n this.tokenManager = tokenManager;\n }\n\n /** Register the refresh callback (set by AuthClient to avoid circular deps) */\n setRefreshFn(fn: RefreshFn): void {\n this.refreshFn = fn;\n }\n\n /**\n * Authenticated fetch wrapper.\n * Automatically injects the Bearer token and handles 401 → refresh → retry.\n */\n async fetch(input: RequestInfo | URL, init: RequestInit = {}): Promise<Response> {\n const tokens = this.tokenManager.getTokens();\n\n const headers = new Headers(init.headers);\n if (tokens?.accessToken) {\n headers.set(\"Authorization\", `Bearer ${tokens.accessToken}`);\n }\n\n const response = await this.doFetch(input, { ...init, headers });\n\n if (response.status === 401 && this.refreshFn) {\n const refreshed = await this.deduplicatedRefresh();\n if (refreshed) {\n // Retry with new token\n const newTokens = this.tokenManager.getTokens();\n if (newTokens?.accessToken) {\n headers.set(\"Authorization\", `Bearer ${newTokens.accessToken}`);\n }\n return this.doFetch(input, { ...init, headers });\n }\n }\n\n return response;\n }\n\n /**\n * Raw fetch using the configured fetchFn or global fetch.\n */\n async doFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const fetchFn = this.config.fetchFn ?? fetch;\n return fetchFn(input as RequestInfo, init);\n }\n\n /**\n * Builds a full URL from a path relative to baseUrl.\n */\n url(path: string): string {\n return `${this.config.baseUrl.replace(/\\/$/, \"\")}/${path.replace(/^\\//, \"\")}`;\n }\n\n // ─── Private ────────────────────────────────────────────────────────────────\n\n /**\n * Ensures only one refresh request is in-flight at a time.\n */\n private async deduplicatedRefresh(): Promise<boolean> {\n if (this.refreshPromise) return this.refreshPromise;\n\n this.refreshPromise = this.refreshFn!().finally(() => {\n this.refreshPromise = null;\n });\n\n return this.refreshPromise;\n }\n}\n","import type { AuthConfig, AuthSession, AuthTokens } from \"../types\";\nimport type { HttpClient } from \"./HttpClient\";\nimport type { TokenManager } from \"./TokenManager\";\n\n/**\n * Derives and caches the current AuthSession from stored tokens.\n * Fetches the user profile from the /me endpoint when available.\n */\nexport class SessionManager<User = unknown> {\n private session: AuthSession<User> = {\n user: null,\n tokens: null,\n isAuthenticated: false,\n };\n\n private readonly config: AuthConfig<User>;\n private readonly tokenManager: TokenManager;\n private readonly httpClient: HttpClient;\n\n constructor(\n config: AuthConfig<User>,\n tokenManager: TokenManager,\n httpClient: HttpClient\n ) {\n this.config = config;\n this.tokenManager = tokenManager;\n this.httpClient = httpClient;\n }\n\n getSession(): AuthSession<User> {\n return this.session;\n }\n\n setSession(session: AuthSession<User>): void {\n this.session = session;\n }\n\n /**\n * Builds a session from stored tokens, optionally fetching the user profile.\n */\n async loadSession(): Promise<AuthSession<User>> {\n const tokens = this.tokenManager.getTokens();\n\n if (!tokens) {\n this.session = { user: null, tokens: null, isAuthenticated: false };\n return this.session;\n }\n\n // If access token is expired and refresh is also expired, clear everything\n if (\n this.tokenManager.isAccessExpired(tokens) &&\n this.tokenManager.isRefreshExpired(tokens)\n ) {\n this.tokenManager.clearTokens();\n this.session = { user: null, tokens: null, isAuthenticated: false };\n return this.session;\n }\n\n const user = await this.fetchUser(tokens);\n this.session = {\n user,\n tokens,\n isAuthenticated: true,\n };\n\n return this.session;\n }\n\n /**\n * Updates the session after a successful token refresh.\n */\n async refreshSession(tokens: AuthTokens): Promise<void> {\n const user = this.session.user ?? (await this.fetchUser(tokens));\n this.session = { user, tokens, isAuthenticated: true };\n }\n\n clearSession(): void {\n this.session = { user: null, tokens: null, isAuthenticated: false };\n }\n\n // ─── Private ────────────────────────────────────────────────────────────────\n\n private async fetchUser(tokens: AuthTokens): Promise<User | null> {\n const meEndpoint = this.config.endpoints.me;\n if (!meEndpoint) return null;\n\n try {\n const res = await this.httpClient.fetch(this.httpClient.url(meEndpoint));\n if (!res.ok) return null;\n return (await res.json()) as User;\n } catch {\n return null;\n }\n }\n}\n","/**\n * Lightweight symmetric encryption using AES-GCM via the Web Crypto API.\n * Works in both browser and Node.js (>=18) / Edge runtimes.\n */\n\nconst ALGO = \"AES-GCM\";\nconst IV_LENGTH = 12; // bytes\n\nfunction getTextEncoder() {\n return new TextEncoder();\n}\n\nfunction getTextDecoder() {\n return new TextDecoder();\n}\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n if (!secret) {\n throw new Error(\n \"[next-token-auth] `secret` is undefined. \" +\n \"If using cookie storage, ensure AUTH_SECRET is set in your environment.\"\n );\n }\n const raw = getTextEncoder().encode(secret.padEnd(32, \"0\").slice(0, 32));\n return crypto.subtle.importKey(\"raw\", raw, { name: ALGO }, false, [\n \"encrypt\",\n \"decrypt\",\n ]);\n}\n\n/**\n * Encrypts a plaintext string using AES-GCM.\n * Returns a base64url-encoded string: `<iv>.<ciphertext>`\n */\nexport async function encrypt(data: string, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n // Ensure we have a plain ArrayBuffer for SubtleCrypto\n const iv = ivArray.buffer.slice(0, IV_LENGTH) as ArrayBuffer;\n const encoded = getTextEncoder().encode(data);\n\n const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);\n\n const ivB64 = bufferToBase64(new Uint8Array(iv));\n const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));\n return `${ivB64}.${cipherB64}`;\n}\n\n/**\n * Decrypts a string produced by `encrypt`.\n */\nexport async function decrypt(data: string, secret: string): Promise<string> {\n const [ivB64, cipherB64] = data.split(\".\");\n if (!ivB64 || !cipherB64) {\n throw new Error(\"decrypt: invalid ciphertext format\");\n }\n\n const key = await deriveKey(secret);\n const ivBytes = base64ToBuffer(ivB64);\n const iv = ivBytes.buffer.slice(\n ivBytes.byteOffset,\n ivBytes.byteOffset + ivBytes.byteLength\n ) as ArrayBuffer;\n\n const cipherBytes = base64ToBuffer(cipherB64);\n const cipherBuffer = cipherBytes.buffer.slice(\n cipherBytes.byteOffset,\n cipherBytes.byteOffset + cipherBytes.byteLength\n ) as ArrayBuffer;\n\n const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);\n\n return getTextDecoder().decode(plainBuffer);\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction bufferToBase64(buffer: Uint8Array): string {\n return btoa(String.fromCharCode(...buffer))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction base64ToBuffer(b64: string): Uint8Array {\n const padded = b64.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded);\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n","import type { AuthConfig, AuthTokens } from \"../types\";\nimport { encrypt, decrypt } from \"../utils/crypto\";\n\n/**\n * Manages storage, retrieval, and expiry checks for auth tokens.\n * Supports \"cookie\" and \"memory\" storage strategies.\n */\nexport class TokenManager {\n private memoryStore: AuthTokens | null = null;\n private readonly config: AuthConfig;\n\n constructor(config: AuthConfig) {\n this.config = config;\n }\n\n // ─── Public API ─────────────────────────────────────────────────────────────\n\n getTokens(): AuthTokens | null {\n if (this.config.token.storage === \"memory\") {\n return this.memoryStore;\n }\n // Return the in-memory cache (populated by setTokens or initFromCookie)\n return this.decryptedCache;\n }\n\n /**\n * Must be called once on startup (before getTokens) when storage is \"cookie\".\n * Reads and decrypts the cookie, populating the in-memory cache.\n */\n async initFromCookie(): Promise<void> {\n if (typeof document === \"undefined\") return;\n if (this.config.token.storage !== \"cookie\") return;\n\n const raw = getCookieValue(this.cookieName());\n if (!raw) return;\n\n try {\n const json = await decrypt(decodeURIComponent(raw), this.config.secret);\n this.decryptedCache = JSON.parse(json) as AuthTokens;\n } catch {\n this.decryptedCache = null;\n }\n }\n\n async setTokens(tokens: AuthTokens): Promise<void> {\n if (this.config.token.storage === \"memory\") {\n this.memoryStore = tokens;\n return;\n }\n await this.writeToCookie(tokens);\n }\n\n clearTokens(): void {\n this.memoryStore = null;\n this.decryptedCache = null;\n if (this.config.token.storage === \"cookie\") {\n this.deleteCookie();\n }\n }\n\n isAccessExpired(tokens: AuthTokens): boolean {\n const threshold = (this.config.refreshThreshold ?? 60) * 1000;\n return Date.now() >= tokens.accessTokenExpiresAt - threshold;\n }\n\n isRefreshExpired(tokens: AuthTokens): boolean {\n if (!tokens.refreshTokenExpiresAt) return false;\n return Date.now() >= tokens.refreshTokenExpiresAt;\n }\n\n // ─── Cookie helpers (client-side only) ──────────────────────────────────────\n\n private cookieName(): string {\n return this.config.token.cookieName ?? \"next-token-auth.session\";\n }\n\n /** In-memory cache of the last successfully decrypted cookie value. */\n private decryptedCache: AuthTokens | null = null;\n\n private async writeToCookie(tokens: AuthTokens): Promise<void> {\n if (typeof document === \"undefined\") return;\n\n // Encrypt before writing — must match what the server reads via decrypt()\n const value = await encrypt(JSON.stringify(tokens), this.config.secret);\n const secure = this.config.token.secure !== false ? \"; Secure\" : \"\";\n const sameSite = this.config.token.sameSite ?? \"lax\";\n const maxAge = tokens.refreshTokenExpiresAt\n ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n document.cookie = [\n `${this.cookieName()}=${encodeURIComponent(value)}`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \");\n\n // Keep the in-memory cache in sync\n this.decryptedCache = tokens;\n }\n\n private deleteCookie(): void {\n if (typeof document === \"undefined\") return;\n document.cookie = `${this.cookieName()}=; Max-Age=0; Path=/`;\n }\n\n // ─── Server-side helpers ─────────────────────────────────────────────────────\n\n /**\n * Encrypts tokens — used internally by writeToCookie and available for\n * advanced server-side use cases.\n */\n async encryptTokens(tokens: AuthTokens): Promise<string> {\n return encrypt(JSON.stringify(tokens), this.config.secret);\n }\n\n /**\n * Decrypts tokens from a server-side cookie value.\n */\n async decryptTokens(ciphertext: string): Promise<AuthTokens | null> {\n try {\n const json = await decrypt(ciphertext, this.config.secret);\n return JSON.parse(json) as AuthTokens;\n } catch {\n return null;\n }\n }\n}\n\n// ─── Utility ──────────────────────────────────────────────────────────────────\n\nfunction getCookieValue(name: string): string | null {\n if (typeof document === \"undefined\") return null;\n const match = document.cookie.match(\n new RegExp(`(?:^|;\\\\s*)${escapeRegex(name)}=([^;]*)`)\n );\n return match ? match[1] : null;\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import type {\n AuthConfig,\n AuthSession,\n AuthTokens,\n LoginInput,\n LoginResponse,\n} from \"../types\";\nimport { resolveAccessTokenExpiry, resolveRefreshTokenExpiry } from \"../utils/expiry\";\nimport { HttpClient } from \"./HttpClient\";\nimport { SessionManager } from \"./SessionManager\";\nimport { TokenManager } from \"./TokenManager\";\n\n/**\n * Central orchestrator for authentication operations.\n * Coordinates TokenManager, SessionManager, and HttpClient.\n */\nexport class AuthClient<User = unknown> {\n readonly tokenManager: TokenManager;\n readonly sessionManager: SessionManager<User>;\n readonly httpClient: HttpClient;\n\n private readonly config: AuthConfig<User>;\n private sessionListeners: Array<(session: AuthSession<User>) => void> = [];\n\n constructor(config: AuthConfig<User>) {\n this.config = config;\n this.tokenManager = new TokenManager(config as AuthConfig<unknown>);\n this.httpClient = new HttpClient(config as AuthConfig<unknown>, this.tokenManager);\n this.sessionManager = new SessionManager(\n config,\n this.tokenManager,\n this.httpClient\n );\n\n // Wire up the refresh callback\n this.httpClient.setRefreshFn(() => this.refresh());\n }\n\n // ─── Auth Operations ────────────────────────────────────────────────────────\n\n /**\n * Authenticates the user and stores tokens.\n */\n async login(input: LoginInput): Promise<AuthSession<User>> {\n const res = await this.httpClient.doFetch(\n this.httpClient.url(this.config.endpoints.login),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(input),\n }\n );\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(`Login failed (${res.status}): ${error}`);\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const tokens = this.buildTokens(data);\n\n await this.tokenManager.setTokens(tokens);\n await this.sessionManager.loadSession();\n\n const session = this.sessionManager.getSession();\n this.config.onLogin?.(session);\n this.notifyListeners(session);\n\n return session;\n }\n\n /**\n * Logs out the user, clears tokens, and optionally calls the backend.\n */\n async logout(): Promise<void> {\n const logoutEndpoint = this.config.endpoints.logout;\n\n if (logoutEndpoint) {\n try {\n await this.httpClient.fetch(this.httpClient.url(logoutEndpoint), {\n method: \"POST\",\n });\n } catch {\n // Best-effort logout\n }\n }\n\n this.tokenManager.clearTokens();\n this.sessionManager.clearSession();\n this.config.onLogout?.();\n this.notifyListeners(this.sessionManager.getSession());\n }\n\n /**\n * Refreshes the access token using the stored refresh token.\n * Returns true on success, false on failure.\n */\n async refresh(): Promise<boolean> {\n const tokens = this.tokenManager.getTokens();\n\n if (!tokens) return false;\n if (this.tokenManager.isRefreshExpired(tokens)) {\n await this.logout();\n return false;\n }\n\n try {\n const res = await this.httpClient.doFetch(\n this.httpClient.url(this.config.endpoints.refresh),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n }\n );\n\n if (!res.ok) {\n await this.logout();\n return false;\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const newTokens = this.buildTokens(data);\n\n await this.tokenManager.setTokens(newTokens);\n await this.sessionManager.refreshSession(newTokens);\n this.notifyListeners(this.sessionManager.getSession());\n\n return true;\n } catch (err) {\n this.config.onRefreshError?.(err);\n return false;\n }\n }\n\n /**\n * Loads the session from stored tokens (call on app mount).\n */\n async initialize(): Promise<AuthSession<User>> {\n // Decrypt the cookie into the in-memory cache before any session reads\n await this.tokenManager.initFromCookie();\n\n const session = await this.sessionManager.loadSession();\n\n // Proactively refresh if access token is near expiry\n if (\n session.isAuthenticated &&\n session.tokens &&\n this.tokenManager.isAccessExpired(session.tokens) &&\n !this.tokenManager.isRefreshExpired(session.tokens)\n ) {\n await this.refresh();\n }\n\n return this.sessionManager.getSession();\n }\n\n getSession(): AuthSession<User> {\n return this.sessionManager.getSession();\n }\n\n /**\n * Returns the authenticated fetch wrapper.\n */\n get fetch(): HttpClient[\"fetch\"] {\n return this.httpClient.fetch.bind(this.httpClient);\n }\n\n // ─── Subscription ────────────────────────────────────────────────────────────\n\n subscribe(listener: (session: AuthSession<User>) => void): () => void {\n this.sessionListeners.push(listener);\n return () => {\n this.sessionListeners = this.sessionListeners.filter((l) => l !== listener);\n };\n }\n\n // ─── Private ────────────────────────────────────────────────────────────────\n\n private buildTokens(data: LoginResponse<User>): AuthTokens {\n const strategy = this.config.expiry?.strategy ?? \"hybrid\";\n const configAccess = this.config.expiry?.accessTokenExpiresIn;\n const configRefresh = this.config.expiry?.refreshTokenExpiresIn;\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(data, configAccess, strategy),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(data, configRefresh, strategy),\n };\n }\n\n private notifyListeners(session: AuthSession<User>): void {\n for (const listener of this.sessionListeners) {\n listener(session);\n }\n }\n}\n","\"use client\";\n\nimport React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport type { AuthSession, ClientAuthConfig, LoginInput } from \"../types\";\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\ninterface AuthContextValue<User = unknown> {\n session: AuthSession<User>;\n isLoading: boolean;\n login: (input: LoginInput) => Promise<void>;\n logout: () => Promise<void>;\n refresh: () => Promise<void>;\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\n// ─── Provider ─────────────────────────────────────────────────────────────────\n\ninterface AuthProviderProps {\n config: ClientAuthConfig;\n children: React.ReactNode;\n}\n\nexport function AuthProvider({ config, children }: AuthProviderProps) {\n const [session, setSession] = useState<AuthSession>({\n user: null,\n tokens: null,\n isAuthenticated: false,\n });\n const [isLoading, setIsLoading] = useState(true);\n\n // Initialize session on mount\n useEffect(() => {\n let cancelled = false;\n\n fetch(\"/api/auth/session\")\n .then((res) => res.json())\n .then((data) => {\n if (!cancelled) {\n setSession({\n user: data.user ?? null,\n tokens: null, // tokens are HttpOnly, never exposed to client\n isAuthenticated: data.isAuthenticated ?? false,\n });\n setIsLoading(false);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setSession({ user: null, tokens: null, isAuthenticated: false });\n setIsLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, []);\n\n // Auto-refresh timer\n useEffect(() => {\n if (!config.autoRefresh) return;\n\n const interval = setInterval(async () => {\n if (session.isAuthenticated) {\n try {\n await fetch(\"/api/auth/refresh\", { method: \"POST\" });\n const updated = await fetch(\"/api/auth/session\").then((r) => r.json());\n setSession({\n user: updated.user ?? null,\n tokens: null,\n isAuthenticated: updated.isAuthenticated ?? false,\n });\n } catch {\n // Refresh failed — session will be cleared on next page load\n }\n }\n }, (config.refreshThreshold ?? 60) * 1000);\n\n return () => clearInterval(interval);\n }, [config.autoRefresh, config.refreshThreshold, session.isAuthenticated]);\n\n const login = useCallback(\n async (input: LoginInput) => {\n setIsLoading(true);\n try {\n const res = await fetch(\"/api/auth/login\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(input),\n });\n\n if (!res.ok) {\n const { error } = await res.json();\n throw new Error(error ?? \"Login failed\");\n }\n\n const { user } = await res.json();\n const newSession = { user, tokens: null, isAuthenticated: true };\n setSession(newSession);\n config.onLogin?.(newSession);\n } finally {\n setIsLoading(false);\n }\n },\n [config]\n );\n\n const logout = useCallback(async () => {\n await fetch(\"/api/auth/logout\", { method: \"POST\" });\n setSession({ user: null, tokens: null, isAuthenticated: false });\n config.onLogout?.();\n }, [config]);\n\n const refresh = useCallback(async () => {\n await fetch(\"/api/auth/refresh\", { method: \"POST\" });\n const updated = await fetch(\"/api/auth/session\").then((r) => r.json());\n setSession({\n user: updated.user ?? null,\n tokens: null,\n isAuthenticated: updated.isAuthenticated ?? false,\n });\n }, []);\n\n const value: AuthContextValue = {\n session,\n isLoading,\n login,\n logout,\n refresh,\n };\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n// ─── Internal hook ────────────────────────────────────────────────────────────\n\nexport function useAuthContext<User = unknown>(): AuthContextValue<User> {\n const ctx = useContext(AuthContext);\n if (!ctx) {\n throw new Error(\"useAuthContext must be used within <AuthProvider>\");\n }\n return ctx as AuthContextValue<User>;\n}\n","\"use client\";\n\nimport type { AuthSession, LoginInput } from \"../../types\";\nimport { useAuthContext } from \"../AuthProvider\";\n\nexport interface UseAuthReturn<User = unknown> {\n session: AuthSession<User>;\n login: (input: LoginInput) => Promise<void>;\n logout: () => Promise<void>;\n refresh: () => Promise<void>;\n isLoading: boolean;\n}\n\n/**\n * Primary hook for authentication operations.\n *\n * @example\n * const { session, login, logout, isLoading } = useAuth();\n */\nexport function useAuth<User = unknown>(): UseAuthReturn<User> {\n const { session, login, logout, refresh, isLoading } =\n useAuthContext<User>();\n\n return { session, login, logout, refresh, isLoading };\n}\n","\"use client\";\n\nimport type { AuthSession } from \"../../types\";\nimport { useAuthContext } from \"../AuthProvider\";\n\n/**\n * Returns the current auth session without exposing login/logout actions.\n *\n * @example\n * const { user, isAuthenticated } = useSession();\n */\nexport function useSession<User = unknown>(): AuthSession<User> {\n return useAuthContext<User>().session;\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { useAuthContext } from \"../AuthProvider\";\n\nexport interface UseRequireAuthOptions {\n /** Path to redirect unauthenticated users to. @default \"/login\" */\n redirectTo?: string;\n /** Called when the user is not authenticated (use for custom redirect logic) */\n onUnauthenticated?: () => void;\n}\n\n/**\n * Redirects unauthenticated users to the login page.\n * Works with both Next.js App Router and Pages Router.\n *\n * @example\n * // App Router (client component)\n * useRequireAuth({ redirectTo: \"/login\" });\n *\n * @example\n * // Custom handler\n * useRequireAuth({ onUnauthenticated: () => router.push(\"/login\") });\n */\nexport function useRequireAuth(options: UseRequireAuthOptions = {}): void {\n const { redirectTo = \"/login\", onUnauthenticated } = options;\n const { session, isLoading } = useAuthContext();\n\n useEffect(() => {\n if (isLoading) return;\n if (session.isAuthenticated) return;\n\n if (onUnauthenticated) {\n onUnauthenticated();\n return;\n }\n\n // Works in both App Router and Pages Router environments\n if (typeof window !== \"undefined\") {\n window.location.href = redirectTo;\n }\n }, [session.isAuthenticated, isLoading, redirectTo, onUnauthenticated]);\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/expiry.ts","../src/core/HttpClient.ts","../src/core/SessionManager.ts","../src/utils/crypto.ts","../src/core/TokenManager.ts","../src/core/AuthClient.ts","../src/react/AuthProvider.tsx","../src/react/hooks/useAuth.ts","../src/react/hooks/useSession.ts","../src/react/hooks/useRequireAuth.ts"],"names":["createContext","useState","useEffect","useCallback","jsx","useContext"],"mappings":";;;;;;AAEA,IAAM,QAAA,GAAmC;AAAA,EACvC,CAAA,EAAG,CAAA;AAAA,EACH,CAAA,EAAG,EAAA;AAAA,EACH,CAAA,EAAG,IAAA;AAAA,EACH,CAAA,EAAG,KAAA;AAAA,EACH,CAAA,EAAG;AACL,CAAA;AAUO,SAAS,YAAY,KAAA,EAA6B;AACvD,EAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AACzC,IAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,EACzD;AAEA,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,IAAI,KAAA,IAAS,CAAA,EAAG,MAAM,IAAI,MAAM,qCAAqC,CAAA;AACrE,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAG3B,EAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAA,EAAG;AACzB,IAAA,OAAO,QAAA,CAAS,SAAS,EAAE,CAAA;AAAA,EAC7B;AAEA,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,gCAAgC,CAAA;AAC5D,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,qCAAqC,KAAK,CAAA,oEAAA;AAAA,KAE5C;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,KAAA,CAAM,CAAC,CAAC,CAAA;AACjC,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,CAAC,CAAA,CAAE,WAAA,EAAY;AAClC,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,QAAA,CAAS,IAAI,CAAC,CAAA;AAC1C;AAKO,SAAS,eAAA,CACd,KAAA,EACA,eAAA,GAAkB,GAAA,EACV;AACR,EAAA,IAAI;AACF,IAAA,OAAO,YAAY,KAAK,CAAA;AAAA,EAC1B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,eAAA;AAAA,EACT;AACF;AAMO,SAAS,wBAAA,CACd,QAAA,EACA,YAAA,EACA,QAAA,GAA2B,QAAA,EACnB;AACR,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAErB,EAAA,MAAM,WAAA,GACJ,QAAA,CAAS,oBAAA,IAAwB,QAAA,CAAS,SAAA,IAAa,MAAA;AAEzD,EAAA,IAAI,aAAa,SAAA,EAAW;AAC1B,IAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,GAAA,GAAM,WAAA,CAAY,WAAW,CAAA,GAAI,GAAA;AAAA,EAC1C;AAEA,EAAA,IAAI,aAAa,QAAA,EAAU;AACzB,IAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,GAAA,GAAM,WAAA,CAAY,YAAY,CAAA,GAAI,GAAA;AAAA,EAC3C;AAGA,EAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,IAAA,OAAO,GAAA,GAAM,eAAA,CAAgB,WAAW,CAAA,GAAI,GAAA;AAAA,EAC9C;AACA,EAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,IAAA,OAAO,GAAA,GAAM,eAAA,CAAgB,YAAY,CAAA,GAAI,GAAA;AAAA,EAC/C;AAGA,EAAA,OAAO,MAAM,GAAA,GAAM,GAAA;AACrB;AAKO,SAAS,yBAAA,CACd,QAAA,EACA,YAAA,EACA,QAAA,GAA2B,QAAA,EACP;AACpB,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,MAAM,cAAc,QAAA,CAAS,qBAAA;AAE7B,EAAA,IAAI,aAAa,SAAA,EAAW;AAC1B,IAAA,OAAO,gBAAgB,MAAA,GACnB,GAAA,GAAM,WAAA,CAAY,WAAW,IAAI,GAAA,GACjC,MAAA;AAAA,EACN;AAEA,EAAA,IAAI,aAAa,QAAA,EAAU;AACzB,IAAA,OAAO,iBAAiB,MAAA,GACpB,GAAA,GAAM,WAAA,CAAY,YAAY,IAAI,GAAA,GAClC,MAAA;AAAA,EACN;AAGA,EAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,IAAA,OAAO,GAAA,GAAM,eAAA,CAAgB,WAAW,CAAA,GAAI,GAAA;AAAA,EAC9C;AACA,EAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,IAAA,OAAO,GAAA,GAAM,eAAA,CAAgB,YAAY,CAAA,GAAI,GAAA;AAAA,EAC/C;AAEA,EAAA,OAAO,MAAA;AACT;;;AChIO,IAAM,aAAN,MAAiB;AAAA,EAMtB,WAAA,CAAY,QAAoB,YAAA,EAA4B;AAH5D,IAAA,IAAA,CAAQ,SAAA,GAA8B,IAAA;AACtC,IAAA,IAAA,CAAQ,cAAA,GAA0C,IAAA;AAGhD,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AAAA,EACtB;AAAA;AAAA,EAGA,aAAa,EAAA,EAAqB;AAChC,IAAA,IAAA,CAAK,SAAA,GAAY,EAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAA,CAAM,KAAA,EAA0B,IAAA,GAAoB,EAAC,EAAsB;AAC/E,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,YAAA,CAAa,SAAA,EAAU;AAE3C,IAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAA;AACxC,IAAA,IAAI,QAAQ,WAAA,EAAa;AACvB,MAAA,OAAA,CAAQ,GAAA,CAAI,eAAA,EAAiB,CAAA,OAAA,EAAU,MAAA,CAAO,WAAW,CAAA,CAAE,CAAA;AAAA,IAC7D;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,OAAA,CAAQ,OAAO,EAAE,GAAG,IAAA,EAAM,OAAA,EAAS,CAAA;AAE/D,IAAA,IAAI,QAAA,CAAS,MAAA,KAAW,GAAA,IAAO,IAAA,CAAK,SAAA,EAAW;AAC7C,MAAA,MAAM,SAAA,GAAY,MAAM,IAAA,CAAK,mBAAA,EAAoB;AACjD,MAAA,IAAI,SAAA,EAAW;AAEb,QAAA,MAAM,SAAA,GAAY,IAAA,CAAK,YAAA,CAAa,SAAA,EAAU;AAC9C,QAAA,IAAI,WAAW,WAAA,EAAa;AAC1B,UAAA,OAAA,CAAQ,GAAA,CAAI,eAAA,EAAiB,CAAA,OAAA,EAAU,SAAA,CAAU,WAAW,CAAA,CAAE,CAAA;AAAA,QAChE;AACA,QAAA,OAAO,KAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,GAAG,IAAA,EAAM,SAAS,CAAA;AAAA,MACjD;AAAA,IACF;AAEA,IAAA,OAAO,QAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAA,CAAQ,KAAA,EAA0B,IAAA,EAAuC;AAC7E,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,MAAA,CAAO,OAAA,IAAW,KAAA;AACvC,IAAA,OAAO,OAAA,CAAQ,OAAsB,IAAI,CAAA;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,IAAA,EAAsB;AACxB,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,CAAA,EAAI,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,CAAA;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,mBAAA,GAAwC;AACpD,IAAA,IAAI,IAAA,CAAK,cAAA,EAAgB,OAAO,IAAA,CAAK,cAAA;AAErC,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA,CAAK,SAAA,EAAW,CAAE,QAAQ,MAAM;AACpD,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AAAA,IACxB,CAAC,CAAA;AAED,IAAA,OAAO,IAAA,CAAK,cAAA;AAAA,EACd;AACF;;;AC5EO,IAAM,iBAAN,MAAqC;AAAA,EAW1C,WAAA,CACE,MAAA,EACA,YAAA,EACA,UAAA,EACA;AAdF,IAAA,IAAA,CAAQ,OAAA,GAA6B;AAAA,MACnC,IAAA,EAAM,IAAA;AAAA,MACN,MAAA,EAAQ,IAAA;AAAA,MACR,eAAA,EAAiB;AAAA,KACnB;AAWE,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AACpB,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAAA,EACpB;AAAA,EAEA,UAAA,GAAgC;AAC9B,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd;AAAA,EAEA,WAAW,OAAA,EAAkC;AAC3C,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAA,GAA0C;AAC9C,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,YAAA,CAAa,SAAA,EAAU;AAE3C,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,IAAA,CAAK,UAAU,EAAE,IAAA,EAAM,MAAM,MAAA,EAAQ,IAAA,EAAM,iBAAiB,KAAA,EAAM;AAClE,MAAA,OAAO,IAAA,CAAK,OAAA;AAAA,IACd;AAGA,IAAA,IACE,IAAA,CAAK,aAAa,eAAA,CAAgB,MAAM,KACxC,IAAA,CAAK,YAAA,CAAa,gBAAA,CAAiB,MAAM,CAAA,EACzC;AACA,MAAA,IAAA,CAAK,aAAa,WAAA,EAAY;AAC9B,MAAA,IAAA,CAAK,UAAU,EAAE,IAAA,EAAM,MAAM,MAAA,EAAQ,IAAA,EAAM,iBAAiB,KAAA,EAAM;AAClE,MAAA,OAAO,IAAA,CAAK,OAAA;AAAA,IACd;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,SAAA,CAAU,MAAM,CAAA;AACxC,IAAA,IAAA,CAAK,OAAA,GAAU;AAAA,MACb,IAAA;AAAA,MACA,MAAA;AAAA,MACA,eAAA,EAAiB;AAAA,KACnB;AAEA,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,MAAA,EAAmC;AACtD,IAAA,MAAM,OAAO,IAAA,CAAK,OAAA,CAAQ,QAAS,MAAM,IAAA,CAAK,UAAU,MAAM,CAAA;AAC9D,IAAA,IAAA,CAAK,OAAA,GAAU,EAAE,IAAA,EAAM,MAAA,EAAQ,iBAAiB,IAAA,EAAK;AAAA,EACvD;AAAA,EAEA,YAAA,GAAqB;AACnB,IAAA,IAAA,CAAK,UAAU,EAAE,IAAA,EAAM,MAAM,MAAA,EAAQ,IAAA,EAAM,iBAAiB,KAAA,EAAM;AAAA,EACpE;AAAA;AAAA,EAIA,MAAc,UAAU,MAAA,EAA0C;AAChE,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,EAAA;AACzC,IAAA,IAAI,CAAC,YAAY,OAAO,IAAA;AAExB,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,UAAA,CAAW,MAAM,IAAA,CAAK,UAAA,CAAW,GAAA,CAAI,UAAU,CAAC,CAAA;AACvE,MAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,OAAO,IAAA;AACpB,MAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AAAA,IACzB,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACF;;;ACzFA,IAAM,IAAA,GAAO,SAAA;AACb,IAAM,SAAA,GAAY,EAAA;AAElB,SAAS,cAAA,GAAiB;AACxB,EAAA,OAAO,IAAI,WAAA,EAAY;AACzB;AAEA,SAAS,cAAA,GAAiB;AACxB,EAAA,OAAO,IAAI,WAAA,EAAY;AACzB;AAEA,eAAe,UAAU,MAAA,EAAoC;AAC3D,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,MAAM,GAAA,GAAM,cAAA,EAAe,CAAE,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,EAAA,EAAI,GAAG,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA;AACvE,EAAA,OAAO,MAAA,CAAO,OAAO,SAAA,CAAU,KAAA,EAAO,KAAK,EAAE,IAAA,EAAM,IAAA,EAAK,EAAG,KAAA,EAAO;AAAA,IAChE,SAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;AAMA,eAAsB,OAAA,CAAQ,MAAc,MAAA,EAAiC;AAC3E,EAAA,MAAM,GAAA,GAAM,MAAM,SAAA,CAAU,MAAM,CAAA;AAClC,EAAA,MAAM,UAAU,MAAA,CAAO,eAAA,CAAgB,IAAI,UAAA,CAAW,SAAS,CAAC,CAAA;AAEhE,EAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,GAAG,SAAS,CAAA;AAC5C,EAAA,MAAM,OAAA,GAAU,cAAA,EAAe,CAAE,MAAA,CAAO,IAAI,CAAA;AAE5C,EAAA,MAAM,YAAA,GAAe,MAAM,MAAA,CAAO,MAAA,CAAO,OAAA,CAAQ,EAAE,IAAA,EAAM,IAAA,EAAM,EAAA,EAAG,EAAG,GAAA,EAAK,OAAO,CAAA;AAEjF,EAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,IAAI,UAAA,CAAW,EAAE,CAAC,CAAA;AAC/C,EAAA,MAAM,SAAA,GAAY,cAAA,CAAe,IAAI,UAAA,CAAW,YAAY,CAAC,CAAA;AAC7D,EAAA,OAAO,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAC9B;AAKA,eAAsB,OAAA,CAAQ,MAAc,MAAA,EAAiC;AAC3E,EAAA,MAAM,CAAC,KAAA,EAAO,SAAS,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACzC,EAAA,IAAI,CAAC,KAAA,IAAS,CAAC,SAAA,EAAW;AACxB,IAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AAAA,EACtD;AAEA,EAAA,MAAM,GAAA,GAAM,MAAM,SAAA,CAAU,MAAM,CAAA;AAClC,EAAA,MAAM,OAAA,GAAU,eAAe,KAAK,CAAA;AACpC,EAAA,MAAM,EAAA,GAAK,QAAQ,MAAA,CAAO,KAAA;AAAA,IACxB,OAAA,CAAQ,UAAA;AAAA,IACR,OAAA,CAAQ,aAAa,OAAA,CAAQ;AAAA,GAC/B;AAEA,EAAA,MAAM,WAAA,GAAc,eAAe,SAAS,CAAA;AAC5C,EAAA,MAAM,YAAA,GAAe,YAAY,MAAA,CAAO,KAAA;AAAA,IACtC,WAAA,CAAY,UAAA;AAAA,IACZ,WAAA,CAAY,aAAa,WAAA,CAAY;AAAA,GACvC;AAEA,EAAA,MAAM,WAAA,GAAc,MAAM,MAAA,CAAO,MAAA,CAAO,OAAA,CAAQ,EAAE,IAAA,EAAM,IAAA,EAAM,EAAA,EAAG,EAAG,GAAA,EAAK,YAAY,CAAA;AAErF,EAAA,OAAO,cAAA,EAAe,CAAE,MAAA,CAAO,WAAW,CAAA;AAC5C;AAIA,SAAS,eAAe,MAAA,EAA4B;AAClD,EAAA,OAAO,KAAK,MAAA,CAAO,YAAA,CAAa,GAAG,MAAM,CAAC,CAAA,CACvC,OAAA,CAAQ,KAAA,EAAO,GAAG,EAClB,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAClB,OAAA,CAAQ,OAAO,EAAE,CAAA;AACtB;AAEA,SAAS,eAAe,GAAA,EAAyB;AAC/C,EAAA,MAAM,MAAA,GAAS,IAAI,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,MAAM,GAAG,CAAA;AACvD,EAAA,MAAM,MAAA,GAAS,KAAK,MAAM,CAAA;AAC1B,EAAA,OAAO,UAAA,CAAW,KAAK,MAAA,EAAQ,CAAC,MAAM,CAAA,CAAE,UAAA,CAAW,CAAC,CAAC,CAAA;AACvD;;;ACjFO,IAAM,eAAN,MAAmB;AAAA,EAIxB,YAAY,MAAA,EAAoB;AAHhC,IAAA,IAAA,CAAQ,WAAA,GAAiC,IAAA;AAqEzC;AAAA,IAAA,IAAA,CAAQ,cAAA,GAAoC,IAAA;AAjE1C,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA;AAAA,EAIA,SAAA,GAA+B;AAC7B,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,OAAA,KAAY,QAAA,EAAU;AAC1C,MAAA,OAAO,IAAA,CAAK,WAAA;AAAA,IACd;AAEA,IAAA,OAAO,IAAA,CAAK,cAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAA,GAAgC;AACpC,IAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACrC,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,OAAA,KAAY,QAAA,EAAU;AAE5C,IAAA,MAAM,GAAA,GAAM,cAAA,CAAe,IAAA,CAAK,UAAA,EAAY,CAAA;AAC5C,IAAA,IAAI,CAAC,GAAA,EAAK;AAEV,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,kBAAA,CAAmB,GAAG,CAAA,EAAG,IAAA,CAAK,OAAO,MAAM,CAAA;AACtE,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAAA,IACvC,CAAA,CAAA,MAAQ;AACN,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,MAAM,UAAU,MAAA,EAAmC;AACjD,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,OAAA,KAAY,QAAA,EAAU;AAC1C,MAAA,IAAA,CAAK,WAAA,GAAc,MAAA;AACnB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAA,CAAK,cAAc,MAAM,CAAA;AAAA,EACjC;AAAA,EAEA,WAAA,GAAoB;AAClB,IAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AACtB,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,OAAA,KAAY,QAAA,EAAU;AAC1C,MAAA,IAAA,CAAK,YAAA,EAAa;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,gBAAgB,MAAA,EAA6B;AAC3C,IAAA,MAAM,SAAA,GAAA,CAAa,IAAA,CAAK,MAAA,CAAO,gBAAA,IAAoB,EAAA,IAAM,GAAA;AACzD,IAAA,OAAO,IAAA,CAAK,GAAA,EAAI,IAAK,MAAA,CAAO,oBAAA,GAAuB,SAAA;AAAA,EACrD;AAAA,EAEA,iBAAiB,MAAA,EAA6B;AAC5C,IAAA,IAAI,CAAC,MAAA,CAAO,qBAAA,EAAuB,OAAO,KAAA;AAC1C,IAAA,OAAO,IAAA,CAAK,GAAA,EAAI,IAAK,MAAA,CAAO,qBAAA;AAAA,EAC9B;AAAA;AAAA,EAIQ,UAAA,GAAqB;AAC3B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,UAAA,IAAc,yBAAA;AAAA,EACzC;AAAA,EAKA,MAAc,cAAc,MAAA,EAAmC;AAC7D,IAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AAGrC,IAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,IAAA,CAAK,UAAU,MAAM,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA;AACtE,IAAA,MAAM,SAAS,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,MAAA,KAAW,QAAQ,UAAA,GAAa,EAAA;AACjE,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,QAAA,IAAY,KAAA;AAC/C,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,qBAAA,GAClB,IAAA,CAAK,KAAA,CAAA,CAAO,MAAA,CAAO,qBAAA,GAAwB,IAAA,CAAK,GAAA,EAAI,IAAK,GAAI,CAAA,GAC7D,MAAA;AAEJ,IAAA,QAAA,CAAS,MAAA,GAAS;AAAA,MAChB,GAAG,IAAA,CAAK,UAAA,EAAY,CAAA,CAAA,EAAI,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA;AAAA,MACjD,WAAW,MAAM,CAAA,CAAA;AAAA,MACjB,CAAA,MAAA,CAAA;AAAA,MACA,YAAY,QAAQ,CAAA,CAAA;AAAA,MACpB;AAAA,KACF,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,IAAI,CAAA;AAGZ,IAAA,IAAA,CAAK,cAAA,GAAiB,MAAA;AAAA,EACxB;AAAA,EAEQ,YAAA,GAAqB;AAC3B,IAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACrC,IAAA,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG,IAAA,CAAK,UAAA,EAAY,CAAA,oBAAA,CAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cAAc,MAAA,EAAqC;AACvD,IAAA,OAAO,QAAQ,IAAA,CAAK,SAAA,CAAU,MAAM,CAAA,EAAG,IAAA,CAAK,OAAO,MAAM,CAAA;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,UAAA,EAAgD;AAClE,IAAA,IAAI;AACF,MAAA,MAAM,OAAO,MAAM,OAAA,CAAQ,UAAA,EAAY,IAAA,CAAK,OAAO,MAAM,CAAA;AACzD,MAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,IACxB,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACF;AAIA,SAAS,eAAe,IAAA,EAA6B;AACnD,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,IAAA;AAC5C,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,CAAO,KAAA;AAAA,IAC5B,IAAI,MAAA,CAAO,CAAA,WAAA,EAAc,WAAA,CAAY,IAAI,CAAC,CAAA,QAAA,CAAU;AAAA,GACtD;AACA,EAAA,OAAO,KAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA;AAC5B;AAEA,SAAS,YAAY,GAAA,EAAqB;AACxC,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,qBAAA,EAAuB,MAAM,CAAA;AAClD;;;AChIO,IAAM,aAAN,MAAiC;AAAA,EAQtC,YAAY,MAAA,EAA0B;AAFtC,IAAA,IAAA,CAAQ,mBAAgE,EAAC;AAGvE,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,YAAA,GAAe,IAAI,YAAA,CAAa,MAA6B,CAAA;AAClE,IAAA,IAAA,CAAK,UAAA,GAAa,IAAI,UAAA,CAAW,MAAA,EAA+B,KAAK,YAAY,CAAA;AACjF,IAAA,IAAA,CAAK,iBAAiB,IAAI,cAAA;AAAA,MACxB,MAAA;AAAA,MACA,IAAA,CAAK,YAAA;AAAA,MACL,IAAA,CAAK;AAAA,KACP;AAGA,IAAA,IAAA,CAAK,UAAA,CAAW,YAAA,CAAa,MAAM,IAAA,CAAK,SAAS,CAAA;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,MAAM,KAAA,EAA+C;AACzD,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,UAAA,CAAW,OAAA;AAAA,MAChC,KAAK,UAAA,CAAW,GAAA,CAAI,IAAA,CAAK,MAAA,CAAO,UAAU,KAAK,CAAA;AAAA,MAC/C;AAAA,QACE,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,QAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,KAAK;AAAA;AAC5B,KACF;AAEA,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,KAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiB,IAAI,MAAM,CAAA,GAAA,EAAM,KAAK,CAAA,CAAE,CAAA;AAAA,IAC1D;AAEA,IAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,WAAA,CAAY,IAAI,CAAA;AAEpC,IAAA,MAAM,IAAA,CAAK,YAAA,CAAa,SAAA,CAAU,MAAM,CAAA;AACxC,IAAA,MAAM,IAAA,CAAK,eAAe,WAAA,EAAY;AAEtC,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,cAAA,CAAe,UAAA,EAAW;AAC/C,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,OAAO,CAAA;AAC7B,IAAA,IAAA,CAAK,gBAAgB,OAAO,CAAA;AAE5B,IAAA,OAAO,OAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAA,GAAwB;AAC5B,IAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,MAAA;AAE7C,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,IAAI;AACF,QAAA,MAAM,KAAK,UAAA,CAAW,KAAA,CAAM,KAAK,UAAA,CAAW,GAAA,CAAI,cAAc,CAAA,EAAG;AAAA,UAC/D,MAAA,EAAQ;AAAA,SACT,CAAA;AAAA,MACH,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,aAAa,WAAA,EAAY;AAC9B,IAAA,IAAA,CAAK,eAAe,YAAA,EAAa;AACjC,IAAA,IAAA,CAAK,OAAO,QAAA,IAAW;AACvB,IAAA,IAAA,CAAK,eAAA,CAAgB,IAAA,CAAK,cAAA,CAAe,UAAA,EAAY,CAAA;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAA,GAA4B;AAChC,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,YAAA,CAAa,SAAA,EAAU;AAE3C,IAAA,IAAI,CAAC,QAAQ,OAAO,KAAA;AACpB,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,gBAAA,CAAiB,MAAM,CAAA,EAAG;AAC9C,MAAA,MAAM,KAAK,MAAA,EAAO;AAClB,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,UAAA,CAAW,OAAA;AAAA,QAChC,KAAK,UAAA,CAAW,GAAA,CAAI,IAAA,CAAK,MAAA,CAAO,UAAU,OAAO,CAAA;AAAA,QACjD;AAAA,UACE,MAAA,EAAQ,MAAA;AAAA,UACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,UAC9C,MAAM,IAAA,CAAK,SAAA,CAAU,EAAE,YAAA,EAAc,MAAA,CAAO,cAAc;AAAA;AAC5D,OACF;AAEA,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,QAAA,MAAM,KAAK,MAAA,EAAO;AAClB,QAAA,OAAO,KAAA;AAAA,MACT;AAEA,MAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,WAAA,CAAY,IAAI,CAAA;AAEvC,MAAA,MAAM,IAAA,CAAK,YAAA,CAAa,SAAA,CAAU,SAAS,CAAA;AAC3C,MAAA,MAAM,IAAA,CAAK,cAAA,CAAe,cAAA,CAAe,SAAS,CAAA;AAClD,MAAA,IAAA,CAAK,eAAA,CAAgB,IAAA,CAAK,cAAA,CAAe,UAAA,EAAY,CAAA;AAErD,MAAA,OAAO,IAAA;AAAA,IACT,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,MAAA,CAAO,iBAAiB,GAAG,CAAA;AAChC,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAA,GAAyC;AAE7C,IAAA,MAAM,IAAA,CAAK,aAAa,cAAA,EAAe;AAEvC,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,cAAA,CAAe,WAAA,EAAY;AAGtD,IAAA,IACE,QAAQ,eAAA,IACR,OAAA,CAAQ,MAAA,IACR,IAAA,CAAK,aAAa,eAAA,CAAgB,OAAA,CAAQ,MAAM,CAAA,IAChD,CAAC,IAAA,CAAK,YAAA,CAAa,gBAAA,CAAiB,OAAA,CAAQ,MAAM,CAAA,EAClD;AACA,MAAA,MAAM,KAAK,OAAA,EAAQ;AAAA,IACrB;AAEA,IAAA,OAAO,IAAA,CAAK,eAAe,UAAA,EAAW;AAAA,EACxC;AAAA,EAEA,UAAA,GAAgC;AAC9B,IAAA,OAAO,IAAA,CAAK,eAAe,UAAA,EAAW;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,KAAA,GAA6B;AAC/B,IAAA,OAAO,IAAA,CAAK,UAAA,CAAW,KAAA,CAAM,IAAA,CAAK,KAAK,UAAU,CAAA;AAAA,EACnD;AAAA;AAAA,EAIA,UAAU,QAAA,EAA4D;AACpE,IAAA,IAAA,CAAK,gBAAA,CAAiB,KAAK,QAAQ,CAAA;AACnC,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,mBAAmB,IAAA,CAAK,gBAAA,CAAiB,OAAO,CAAC,CAAA,KAAM,MAAM,QAAQ,CAAA;AAAA,IAC5E,CAAA;AAAA,EACF;AAAA;AAAA,EAIQ,YAAY,IAAA,EAAuC;AACzD,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,QAAA,IAAY,QAAA;AACjD,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,oBAAA;AACzC,IAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,qBAAA;AAE1C,IAAA,OAAO;AAAA,MACL,aAAa,IAAA,CAAK,WAAA;AAAA,MAClB,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,oBAAA,EAAsB,wBAAA,CAAyB,IAAA,EAAM,YAAA,EAAc,QAAQ,CAAA;AAAA,MAC3E,qBAAA,EAAuB,yBAAA,CAA0B,IAAA,EAAM,aAAA,EAAe,QAAQ;AAAA,KAChF;AAAA,EACF;AAAA,EAEQ,gBAAgB,OAAA,EAAkC;AACxD,IAAA,KAAA,MAAW,QAAA,IAAY,KAAK,gBAAA,EAAkB;AAC5C,MAAA,QAAA,CAAS,OAAO,CAAA;AAAA,IAClB;AAAA,EACF;AACF;AChLA,IAAM,WAAA,GAAcA,oBAAuC,IAAI,CAAA;AAWxD,SAAS,YAAA,CAAa,EAAE,MAAA,EAAQ,QAAA,EAAU,gBAAe,EAAsB;AACpF,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIC,cAAA;AAAA,IAC5B,kBAAkB,EAAE,IAAA,EAAM,MAAM,MAAA,EAAQ,IAAA,EAAM,iBAAiB,KAAA;AAAM,GACvE;AAEA,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,cAAA,CAAS,CAAC,cAAc,CAAA;AAG1D,EAAAC,eAAA,CAAU,MAAM;AAEd,IAAA,IAAI,cAAA,EAAgB;AAEpB,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,KAAA,CAAM,mBAAmB,CAAA,CACtB,IAAA,CAAK,CAAC,GAAA,KAAQ,GAAA,CAAI,IAAA,EAAM,CAAA,CACxB,IAAA,CAAK,CAAC,IAAA,KAAS;AACd,MAAA,IAAI,CAAC,SAAA,EAAW;AACd,QAAA,UAAA,CAAW;AAAA,UACT,IAAA,EAAM,KAAK,IAAA,IAAQ,IAAA;AAAA,UACnB,MAAA,EAAQ,IAAA;AAAA;AAAA,UACR,eAAA,EAAiB,KAAK,eAAA,IAAmB;AAAA,SAC1C,CAAA;AACD,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACX,MAAA,IAAI,CAAC,SAAA,EAAW;AACd,QAAA,UAAA,CAAW,EAAE,IAAA,EAAM,IAAA,EAAM,QAAQ,IAAA,EAAM,eAAA,EAAiB,OAAO,CAAA;AAC/D,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAC,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAGL,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAO,WAAA,EAAa;AAEzB,IAAA,MAAM,QAAA,GAAW,YAAY,YAAY;AACvC,MAAA,IAAI,QAAQ,eAAA,EAAiB;AAC3B,QAAA,IAAI;AACF,UAAA,MAAM,KAAA,CAAM,mBAAA,EAAqB,EAAE,MAAA,EAAQ,QAAQ,CAAA;AACnD,UAAA,MAAM,OAAA,GAAU,MAAM,KAAA,CAAM,mBAAmB,CAAA,CAAE,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM,CAAA;AACrE,UAAA,UAAA,CAAW;AAAA,YACT,IAAA,EAAM,QAAQ,IAAA,IAAQ,IAAA;AAAA,YACtB,MAAA,EAAQ,IAAA;AAAA,YACR,eAAA,EAAiB,QAAQ,eAAA,IAAmB;AAAA,WAC7C,CAAA;AAAA,QACH,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,CAAA,EAAA,CAAI,MAAA,CAAO,gBAAA,IAAoB,EAAA,IAAM,GAAI,CAAA;AAEzC,IAAA,OAAO,MAAM,cAAc,QAAQ,CAAA;AAAA,EACrC,CAAA,EAAG,CAAC,MAAA,CAAO,WAAA,EAAa,OAAO,gBAAA,EAAkB,OAAA,CAAQ,eAAe,CAAC,CAAA;AAEzE,EAAA,MAAM,KAAA,GAAQC,iBAAA;AAAA,IACZ,OAAO,KAAA,KAAsB;AAC3B,MAAA,YAAA,CAAa,IAAI,CAAA;AACjB,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,iBAAA,EAAmB;AAAA,UACzC,MAAA,EAAQ,MAAA;AAAA,UACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,UAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,KAAK;AAAA,SAC3B,CAAA;AAED,QAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,UAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,IAAI,IAAA,EAAK;AACjC,UAAA,MAAM,IAAI,KAAA,CAAM,KAAA,IAAS,cAAc,CAAA;AAAA,QACzC;AAEA,QAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,IAAI,IAAA,EAAK;AAChC,QAAA,MAAM,aAAa,EAAE,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,iBAAiB,IAAA,EAAK;AAC/D,QAAA,UAAA,CAAW,UAAU,CAAA;AACrB,QAAA,MAAA,CAAO,UAAU,UAAU,CAAA;AAAA,MAC7B,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,MAAM,MAAA,GAASA,kBAAY,YAAY;AACrC,IAAA,MAAM,KAAA,CAAM,kBAAA,EAAoB,EAAE,MAAA,EAAQ,QAAQ,CAAA;AAClD,IAAA,UAAA,CAAW,EAAE,IAAA,EAAM,IAAA,EAAM,QAAQ,IAAA,EAAM,eAAA,EAAiB,OAAO,CAAA;AAC/D,IAAA,MAAA,CAAO,QAAA,IAAW;AAAA,EACpB,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,MAAM,OAAA,GAAUA,kBAAY,YAAY;AACtC,IAAA,MAAM,KAAA,CAAM,mBAAA,EAAqB,EAAE,MAAA,EAAQ,QAAQ,CAAA;AACnD,IAAA,MAAM,OAAA,GAAU,MAAM,KAAA,CAAM,mBAAmB,CAAA,CAAE,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM,CAAA;AACrE,IAAA,UAAA,CAAW;AAAA,MACT,IAAA,EAAM,QAAQ,IAAA,IAAQ,IAAA;AAAA,MACtB,MAAA,EAAQ,IAAA;AAAA,MACR,eAAA,EAAiB,QAAQ,eAAA,IAAmB;AAAA,KAC7C,CAAA;AAAA,EACH,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,KAAA,GAA0B;AAAA,IAC9B,OAAA;AAAA,IACA,SAAA;AAAA,IACA,KAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,uBAAOC,cAAA,CAAC,WAAA,CAAY,QAAA,EAAZ,EAAqB,OAAe,QAAA,EAAS,CAAA;AACvD;AAIO,SAAS,cAAA,GAAyD;AACvE,EAAA,MAAM,GAAA,GAAMC,iBAAW,WAAW,CAAA;AAClC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,MAAM,mDAAmD,CAAA;AAAA,EACrE;AACA,EAAA,OAAO,GAAA;AACT;;;ACvIO,SAAS,OAAA,GAA+C;AAC7D,EAAA,MAAM,EAAE,OAAA,EAAS,KAAA,EAAO,QAAQ,OAAA,EAAS,SAAA,KACvC,cAAA,EAAqB;AAEvB,EAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,MAAA,EAAQ,SAAS,SAAA,EAAU;AACtD;;;ACbO,SAAS,UAAA,GAAgD;AAC9D,EAAA,OAAO,gBAAqB,CAAE,OAAA;AAChC;ACWO,SAAS,cAAA,CAAe,OAAA,GAAiC,EAAC,EAAS;AACxE,EAAA,MAAM,EAAE,UAAA,GAAa,QAAA,EAAU,iBAAA,EAAkB,GAAI,OAAA;AACrD,EAAA,MAAM,EAAE,OAAA,EAAS,SAAA,EAAU,GAAI,cAAA,EAAe;AAE9C,EAAAH,gBAAU,MAAM;AACd,IAAA,IAAI,SAAA,EAAW;AACf,IAAA,IAAI,QAAQ,eAAA,EAAiB;AAE7B,IAAA,IAAI,iBAAA,EAAmB;AACrB,MAAA,iBAAA,EAAkB;AAClB,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,MAAA,MAAA,CAAO,SAAS,IAAA,GAAO,UAAA;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,OAAA,CAAQ,iBAAiB,SAAA,EAAW,UAAA,EAAY,iBAAiB,CAAC,CAAA;AACxE","file":"index.js","sourcesContent":["import type { ExpiryInput, ExpiryStrategy, LoginResponse } from \"../types\";\n\nconst UNIT_MAP: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n};\n\n/**\n * Parses an expiry value into seconds.\n * Accepts:\n * - number → treated as seconds\n * - string → e.g. \"15m\", \"2h\", \"2d\", \"7d\", \"1w\"\n *\n * @throws if the format is unrecognised\n */\nexport function parseExpiry(input?: ExpiryInput): number {\n if (input === undefined || input === null) {\n throw new Error(\"parseExpiry: no expiry value provided\");\n }\n\n if (typeof input === \"number\") {\n if (input <= 0) throw new Error(\"parseExpiry: value must be positive\");\n return input;\n }\n\n const trimmed = input.trim();\n\n // Pure numeric string\n if (/^\\d+$/.test(trimmed)) {\n return parseInt(trimmed, 10);\n }\n\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)\\s*([smhdw])$/i);\n if (!match) {\n throw new Error(\n `parseExpiry: unrecognised format \"${input}\". ` +\n `Expected a number or a string like \"15m\", \"2h\", \"2d\", \"7d\", \"1w\".`\n );\n }\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n return Math.floor(value * UNIT_MAP[unit]);\n}\n\n/**\n * Safely parses an expiry value, returning a fallback on failure.\n */\nexport function safeParseExpiry(\n input?: ExpiryInput,\n fallbackSeconds = 900\n): number {\n try {\n return parseExpiry(input);\n } catch {\n return fallbackSeconds;\n }\n}\n\n/**\n * Resolves the access token expiry timestamp (ms) from a login response\n * using the configured strategy.\n */\nexport function resolveAccessTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number {\n const now = Date.now();\n\n const fromBackend =\n response.accessTokenExpiresIn ?? response.expiresIn ?? undefined;\n\n if (strategy === \"backend\") {\n if (fromBackend === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"backend\" but API returned no expiry'\n );\n }\n return now + parseExpiry(fromBackend) * 1000;\n }\n\n if (strategy === \"config\") {\n if (configExpiry === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"config\" but no expiry configured'\n );\n }\n return now + parseExpiry(configExpiry) * 1000;\n }\n\n // hybrid: backend first, fallback to config\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n // Last resort: 15 minutes\n return now + 900 * 1000;\n}\n\n/**\n * Resolves the refresh token expiry timestamp (ms).\n */\nexport function resolveRefreshTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number | undefined {\n const now = Date.now();\n const fromBackend = response.refreshTokenExpiresIn;\n\n if (strategy === \"backend\") {\n return fromBackend !== undefined\n ? now + parseExpiry(fromBackend) * 1000\n : undefined;\n }\n\n if (strategy === \"config\") {\n return configExpiry !== undefined\n ? now + parseExpiry(configExpiry) * 1000\n : undefined;\n }\n\n // hybrid\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n return undefined;\n}\n","import type { AuthConfig } from \"../types\";\nimport type { TokenManager } from \"./TokenManager\";\n\ntype RefreshFn = () => Promise<boolean>;\n\n/**\n * Authenticated HTTP client that:\n * - Injects Authorization: Bearer <accessToken>\n * - Auto-refreshes on 401 and retries the original request once\n */\nexport class HttpClient {\n private readonly config: AuthConfig;\n private readonly tokenManager: TokenManager;\n private refreshFn: RefreshFn | null = null;\n private refreshPromise: Promise<boolean> | null = null;\n\n constructor(config: AuthConfig, tokenManager: TokenManager) {\n this.config = config;\n this.tokenManager = tokenManager;\n }\n\n /** Register the refresh callback (set by AuthClient to avoid circular deps) */\n setRefreshFn(fn: RefreshFn): void {\n this.refreshFn = fn;\n }\n\n /**\n * Authenticated fetch wrapper.\n * Automatically injects the Bearer token and handles 401 → refresh → retry.\n */\n async fetch(input: RequestInfo | URL, init: RequestInit = {}): Promise<Response> {\n const tokens = this.tokenManager.getTokens();\n\n const headers = new Headers(init.headers);\n if (tokens?.accessToken) {\n headers.set(\"Authorization\", `Bearer ${tokens.accessToken}`);\n }\n\n const response = await this.doFetch(input, { ...init, headers });\n\n if (response.status === 401 && this.refreshFn) {\n const refreshed = await this.deduplicatedRefresh();\n if (refreshed) {\n // Retry with new token\n const newTokens = this.tokenManager.getTokens();\n if (newTokens?.accessToken) {\n headers.set(\"Authorization\", `Bearer ${newTokens.accessToken}`);\n }\n return this.doFetch(input, { ...init, headers });\n }\n }\n\n return response;\n }\n\n /**\n * Raw fetch using the configured fetchFn or global fetch.\n */\n async doFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const fetchFn = this.config.fetchFn ?? fetch;\n return fetchFn(input as RequestInfo, init);\n }\n\n /**\n * Builds a full URL from a path relative to baseUrl.\n */\n url(path: string): string {\n return `${this.config.baseUrl.replace(/\\/$/, \"\")}/${path.replace(/^\\//, \"\")}`;\n }\n\n // ─── Private ────────────────────────────────────────────────────────────────\n\n /**\n * Ensures only one refresh request is in-flight at a time.\n */\n private async deduplicatedRefresh(): Promise<boolean> {\n if (this.refreshPromise) return this.refreshPromise;\n\n this.refreshPromise = this.refreshFn!().finally(() => {\n this.refreshPromise = null;\n });\n\n return this.refreshPromise;\n }\n}\n","import type { AuthConfig, AuthSession, AuthTokens } from \"../types\";\nimport type { HttpClient } from \"./HttpClient\";\nimport type { TokenManager } from \"./TokenManager\";\n\n/**\n * Derives and caches the current AuthSession from stored tokens.\n * Fetches the user profile from the /me endpoint when available.\n */\nexport class SessionManager<User = unknown> {\n private session: AuthSession<User> = {\n user: null,\n tokens: null,\n isAuthenticated: false,\n };\n\n private readonly config: AuthConfig<User>;\n private readonly tokenManager: TokenManager;\n private readonly httpClient: HttpClient;\n\n constructor(\n config: AuthConfig<User>,\n tokenManager: TokenManager,\n httpClient: HttpClient\n ) {\n this.config = config;\n this.tokenManager = tokenManager;\n this.httpClient = httpClient;\n }\n\n getSession(): AuthSession<User> {\n return this.session;\n }\n\n setSession(session: AuthSession<User>): void {\n this.session = session;\n }\n\n /**\n * Builds a session from stored tokens, optionally fetching the user profile.\n */\n async loadSession(): Promise<AuthSession<User>> {\n const tokens = this.tokenManager.getTokens();\n\n if (!tokens) {\n this.session = { user: null, tokens: null, isAuthenticated: false };\n return this.session;\n }\n\n // If access token is expired and refresh is also expired, clear everything\n if (\n this.tokenManager.isAccessExpired(tokens) &&\n this.tokenManager.isRefreshExpired(tokens)\n ) {\n this.tokenManager.clearTokens();\n this.session = { user: null, tokens: null, isAuthenticated: false };\n return this.session;\n }\n\n const user = await this.fetchUser(tokens);\n this.session = {\n user,\n tokens,\n isAuthenticated: true,\n };\n\n return this.session;\n }\n\n /**\n * Updates the session after a successful token refresh.\n */\n async refreshSession(tokens: AuthTokens): Promise<void> {\n const user = this.session.user ?? (await this.fetchUser(tokens));\n this.session = { user, tokens, isAuthenticated: true };\n }\n\n clearSession(): void {\n this.session = { user: null, tokens: null, isAuthenticated: false };\n }\n\n // ─── Private ────────────────────────────────────────────────────────────────\n\n private async fetchUser(tokens: AuthTokens): Promise<User | null> {\n const meEndpoint = this.config.endpoints.me;\n if (!meEndpoint) return null;\n\n try {\n const res = await this.httpClient.fetch(this.httpClient.url(meEndpoint));\n if (!res.ok) return null;\n return (await res.json()) as User;\n } catch {\n return null;\n }\n }\n}\n","/**\n * Lightweight symmetric encryption using AES-GCM via the Web Crypto API.\n * Works in both browser and Node.js (>=18) / Edge runtimes.\n */\n\nconst ALGO = \"AES-GCM\";\nconst IV_LENGTH = 12; // bytes\n\nfunction getTextEncoder() {\n return new TextEncoder();\n}\n\nfunction getTextDecoder() {\n return new TextDecoder();\n}\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n if (!secret) {\n throw new Error(\n \"[next-token-auth] `secret` is undefined. \" +\n \"If using cookie storage, ensure AUTH_SECRET is set in your environment.\"\n );\n }\n const raw = getTextEncoder().encode(secret.padEnd(32, \"0\").slice(0, 32));\n return crypto.subtle.importKey(\"raw\", raw, { name: ALGO }, false, [\n \"encrypt\",\n \"decrypt\",\n ]);\n}\n\n/**\n * Encrypts a plaintext string using AES-GCM.\n * Returns a base64url-encoded string: `<iv>.<ciphertext>`\n */\nexport async function encrypt(data: string, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n // Ensure we have a plain ArrayBuffer for SubtleCrypto\n const iv = ivArray.buffer.slice(0, IV_LENGTH) as ArrayBuffer;\n const encoded = getTextEncoder().encode(data);\n\n const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);\n\n const ivB64 = bufferToBase64(new Uint8Array(iv));\n const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));\n return `${ivB64}.${cipherB64}`;\n}\n\n/**\n * Decrypts a string produced by `encrypt`.\n */\nexport async function decrypt(data: string, secret: string): Promise<string> {\n const [ivB64, cipherB64] = data.split(\".\");\n if (!ivB64 || !cipherB64) {\n throw new Error(\"decrypt: invalid ciphertext format\");\n }\n\n const key = await deriveKey(secret);\n const ivBytes = base64ToBuffer(ivB64);\n const iv = ivBytes.buffer.slice(\n ivBytes.byteOffset,\n ivBytes.byteOffset + ivBytes.byteLength\n ) as ArrayBuffer;\n\n const cipherBytes = base64ToBuffer(cipherB64);\n const cipherBuffer = cipherBytes.buffer.slice(\n cipherBytes.byteOffset,\n cipherBytes.byteOffset + cipherBytes.byteLength\n ) as ArrayBuffer;\n\n const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);\n\n return getTextDecoder().decode(plainBuffer);\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction bufferToBase64(buffer: Uint8Array): string {\n return btoa(String.fromCharCode(...buffer))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction base64ToBuffer(b64: string): Uint8Array {\n const padded = b64.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded);\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n","import type { AuthConfig, AuthTokens } from \"../types\";\nimport { encrypt, decrypt } from \"../utils/crypto\";\n\n/**\n * Manages storage, retrieval, and expiry checks for auth tokens.\n * Supports \"cookie\" and \"memory\" storage strategies.\n */\nexport class TokenManager {\n private memoryStore: AuthTokens | null = null;\n private readonly config: AuthConfig;\n\n constructor(config: AuthConfig) {\n this.config = config;\n }\n\n // ─── Public API ─────────────────────────────────────────────────────────────\n\n getTokens(): AuthTokens | null {\n if (this.config.token.storage === \"memory\") {\n return this.memoryStore;\n }\n // Return the in-memory cache (populated by setTokens or initFromCookie)\n return this.decryptedCache;\n }\n\n /**\n * Must be called once on startup (before getTokens) when storage is \"cookie\".\n * Reads and decrypts the cookie, populating the in-memory cache.\n */\n async initFromCookie(): Promise<void> {\n if (typeof document === \"undefined\") return;\n if (this.config.token.storage !== \"cookie\") return;\n\n const raw = getCookieValue(this.cookieName());\n if (!raw) return;\n\n try {\n const json = await decrypt(decodeURIComponent(raw), this.config.secret);\n this.decryptedCache = JSON.parse(json) as AuthTokens;\n } catch {\n this.decryptedCache = null;\n }\n }\n\n async setTokens(tokens: AuthTokens): Promise<void> {\n if (this.config.token.storage === \"memory\") {\n this.memoryStore = tokens;\n return;\n }\n await this.writeToCookie(tokens);\n }\n\n clearTokens(): void {\n this.memoryStore = null;\n this.decryptedCache = null;\n if (this.config.token.storage === \"cookie\") {\n this.deleteCookie();\n }\n }\n\n isAccessExpired(tokens: AuthTokens): boolean {\n const threshold = (this.config.refreshThreshold ?? 60) * 1000;\n return Date.now() >= tokens.accessTokenExpiresAt - threshold;\n }\n\n isRefreshExpired(tokens: AuthTokens): boolean {\n if (!tokens.refreshTokenExpiresAt) return false;\n return Date.now() >= tokens.refreshTokenExpiresAt;\n }\n\n // ─── Cookie helpers (client-side only) ──────────────────────────────────────\n\n private cookieName(): string {\n return this.config.token.cookieName ?? \"next-token-auth.session\";\n }\n\n /** In-memory cache of the last successfully decrypted cookie value. */\n private decryptedCache: AuthTokens | null = null;\n\n private async writeToCookie(tokens: AuthTokens): Promise<void> {\n if (typeof document === \"undefined\") return;\n\n // Encrypt before writing — must match what the server reads via decrypt()\n const value = await encrypt(JSON.stringify(tokens), this.config.secret);\n const secure = this.config.token.secure !== false ? \"; Secure\" : \"\";\n const sameSite = this.config.token.sameSite ?? \"lax\";\n const maxAge = tokens.refreshTokenExpiresAt\n ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n document.cookie = [\n `${this.cookieName()}=${encodeURIComponent(value)}`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \");\n\n // Keep the in-memory cache in sync\n this.decryptedCache = tokens;\n }\n\n private deleteCookie(): void {\n if (typeof document === \"undefined\") return;\n document.cookie = `${this.cookieName()}=; Max-Age=0; Path=/`;\n }\n\n // ─── Server-side helpers ─────────────────────────────────────────────────────\n\n /**\n * Encrypts tokens — used internally by writeToCookie and available for\n * advanced server-side use cases.\n */\n async encryptTokens(tokens: AuthTokens): Promise<string> {\n return encrypt(JSON.stringify(tokens), this.config.secret);\n }\n\n /**\n * Decrypts tokens from a server-side cookie value.\n */\n async decryptTokens(ciphertext: string): Promise<AuthTokens | null> {\n try {\n const json = await decrypt(ciphertext, this.config.secret);\n return JSON.parse(json) as AuthTokens;\n } catch {\n return null;\n }\n }\n}\n\n// ─── Utility ──────────────────────────────────────────────────────────────────\n\nfunction getCookieValue(name: string): string | null {\n if (typeof document === \"undefined\") return null;\n const match = document.cookie.match(\n new RegExp(`(?:^|;\\\\s*)${escapeRegex(name)}=([^;]*)`)\n );\n return match ? match[1] : null;\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import type {\n AuthConfig,\n AuthSession,\n AuthTokens,\n LoginInput,\n LoginResponse,\n} from \"../types\";\nimport { resolveAccessTokenExpiry, resolveRefreshTokenExpiry } from \"../utils/expiry\";\nimport { HttpClient } from \"./HttpClient\";\nimport { SessionManager } from \"./SessionManager\";\nimport { TokenManager } from \"./TokenManager\";\n\n/**\n * Central orchestrator for authentication operations.\n * Coordinates TokenManager, SessionManager, and HttpClient.\n */\nexport class AuthClient<User = unknown> {\n readonly tokenManager: TokenManager;\n readonly sessionManager: SessionManager<User>;\n readonly httpClient: HttpClient;\n\n private readonly config: AuthConfig<User>;\n private sessionListeners: Array<(session: AuthSession<User>) => void> = [];\n\n constructor(config: AuthConfig<User>) {\n this.config = config;\n this.tokenManager = new TokenManager(config as AuthConfig<unknown>);\n this.httpClient = new HttpClient(config as AuthConfig<unknown>, this.tokenManager);\n this.sessionManager = new SessionManager(\n config,\n this.tokenManager,\n this.httpClient\n );\n\n // Wire up the refresh callback\n this.httpClient.setRefreshFn(() => this.refresh());\n }\n\n // ─── Auth Operations ────────────────────────────────────────────────────────\n\n /**\n * Authenticates the user and stores tokens.\n */\n async login(input: LoginInput): Promise<AuthSession<User>> {\n const res = await this.httpClient.doFetch(\n this.httpClient.url(this.config.endpoints.login),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(input),\n }\n );\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(`Login failed (${res.status}): ${error}`);\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const tokens = this.buildTokens(data);\n\n await this.tokenManager.setTokens(tokens);\n await this.sessionManager.loadSession();\n\n const session = this.sessionManager.getSession();\n this.config.onLogin?.(session);\n this.notifyListeners(session);\n\n return session;\n }\n\n /**\n * Logs out the user, clears tokens, and optionally calls the backend.\n */\n async logout(): Promise<void> {\n const logoutEndpoint = this.config.endpoints.logout;\n\n if (logoutEndpoint) {\n try {\n await this.httpClient.fetch(this.httpClient.url(logoutEndpoint), {\n method: \"POST\",\n });\n } catch {\n // Best-effort logout\n }\n }\n\n this.tokenManager.clearTokens();\n this.sessionManager.clearSession();\n this.config.onLogout?.();\n this.notifyListeners(this.sessionManager.getSession());\n }\n\n /**\n * Refreshes the access token using the stored refresh token.\n * Returns true on success, false on failure.\n */\n async refresh(): Promise<boolean> {\n const tokens = this.tokenManager.getTokens();\n\n if (!tokens) return false;\n if (this.tokenManager.isRefreshExpired(tokens)) {\n await this.logout();\n return false;\n }\n\n try {\n const res = await this.httpClient.doFetch(\n this.httpClient.url(this.config.endpoints.refresh),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n }\n );\n\n if (!res.ok) {\n await this.logout();\n return false;\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const newTokens = this.buildTokens(data);\n\n await this.tokenManager.setTokens(newTokens);\n await this.sessionManager.refreshSession(newTokens);\n this.notifyListeners(this.sessionManager.getSession());\n\n return true;\n } catch (err) {\n this.config.onRefreshError?.(err);\n return false;\n }\n }\n\n /**\n * Loads the session from stored tokens (call on app mount).\n */\n async initialize(): Promise<AuthSession<User>> {\n // Decrypt the cookie into the in-memory cache before any session reads\n await this.tokenManager.initFromCookie();\n\n const session = await this.sessionManager.loadSession();\n\n // Proactively refresh if access token is near expiry\n if (\n session.isAuthenticated &&\n session.tokens &&\n this.tokenManager.isAccessExpired(session.tokens) &&\n !this.tokenManager.isRefreshExpired(session.tokens)\n ) {\n await this.refresh();\n }\n\n return this.sessionManager.getSession();\n }\n\n getSession(): AuthSession<User> {\n return this.sessionManager.getSession();\n }\n\n /**\n * Returns the authenticated fetch wrapper.\n */\n get fetch(): HttpClient[\"fetch\"] {\n return this.httpClient.fetch.bind(this.httpClient);\n }\n\n // ─── Subscription ────────────────────────────────────────────────────────────\n\n subscribe(listener: (session: AuthSession<User>) => void): () => void {\n this.sessionListeners.push(listener);\n return () => {\n this.sessionListeners = this.sessionListeners.filter((l) => l !== listener);\n };\n }\n\n // ─── Private ────────────────────────────────────────────────────────────────\n\n private buildTokens(data: LoginResponse<User>): AuthTokens {\n const strategy = this.config.expiry?.strategy ?? \"hybrid\";\n const configAccess = this.config.expiry?.accessTokenExpiresIn;\n const configRefresh = this.config.expiry?.refreshTokenExpiresIn;\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(data, configAccess, strategy),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(data, configRefresh, strategy),\n };\n }\n\n private notifyListeners(session: AuthSession<User>): void {\n for (const listener of this.sessionListeners) {\n listener(session);\n }\n }\n}\n","\"use client\";\n\nimport React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport type { AuthSession, ClientAuthConfig, LoginInput } from \"../types\";\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\ninterface AuthContextValue<User = unknown> {\n session: AuthSession<User>;\n isLoading: boolean;\n login: (input: LoginInput) => Promise<void>;\n logout: () => Promise<void>;\n refresh: () => Promise<void>;\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\n// ─── Provider ─────────────────────────────────────────────────────────────────\n\ninterface AuthProviderProps {\n config: ClientAuthConfig;\n children: React.ReactNode;\n /** Pre-fetched session from server (use getLayoutSession in your layout) */\n initialSession?: AuthSession | null;\n}\n\nexport function AuthProvider({ config, children, initialSession }: AuthProviderProps) {\n const [session, setSession] = useState<AuthSession>(\n initialSession ?? { user: null, tokens: null, isAuthenticated: false }\n );\n // If the server pre-seeded the session, skip the loading phase entirely\n const [isLoading, setIsLoading] = useState(!initialSession);\n\n // Initialize session on mount\n useEffect(() => {\n // Skip if server already provided initial session\n if (initialSession) return;\n\n let cancelled = false;\n\n fetch(\"/api/auth/session\")\n .then((res) => res.json())\n .then((data) => {\n if (!cancelled) {\n setSession({\n user: data.user ?? null,\n tokens: null, // tokens are HttpOnly, never exposed to client\n isAuthenticated: data.isAuthenticated ?? false,\n });\n setIsLoading(false);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setSession({ user: null, tokens: null, isAuthenticated: false });\n setIsLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, []);\n\n // Auto-refresh timer\n useEffect(() => {\n if (!config.autoRefresh) return;\n\n const interval = setInterval(async () => {\n if (session.isAuthenticated) {\n try {\n await fetch(\"/api/auth/refresh\", { method: \"POST\" });\n const updated = await fetch(\"/api/auth/session\").then((r) => r.json());\n setSession({\n user: updated.user ?? null,\n tokens: null,\n isAuthenticated: updated.isAuthenticated ?? false,\n });\n } catch {\n // Refresh failed — session will be cleared on next page load\n }\n }\n }, (config.refreshThreshold ?? 60) * 1000);\n\n return () => clearInterval(interval);\n }, [config.autoRefresh, config.refreshThreshold, session.isAuthenticated]);\n\n const login = useCallback(\n async (input: LoginInput) => {\n setIsLoading(true);\n try {\n const res = await fetch(\"/api/auth/login\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(input),\n });\n\n if (!res.ok) {\n const { error } = await res.json();\n throw new Error(error ?? \"Login failed\");\n }\n\n const { user } = await res.json();\n const newSession = { user, tokens: null, isAuthenticated: true };\n setSession(newSession);\n config.onLogin?.(newSession);\n } finally {\n setIsLoading(false);\n }\n },\n [config]\n );\n\n const logout = useCallback(async () => {\n await fetch(\"/api/auth/logout\", { method: \"POST\" });\n setSession({ user: null, tokens: null, isAuthenticated: false });\n config.onLogout?.();\n }, [config]);\n\n const refresh = useCallback(async () => {\n await fetch(\"/api/auth/refresh\", { method: \"POST\" });\n const updated = await fetch(\"/api/auth/session\").then((r) => r.json());\n setSession({\n user: updated.user ?? null,\n tokens: null,\n isAuthenticated: updated.isAuthenticated ?? false,\n });\n }, []);\n\n const value: AuthContextValue = {\n session,\n isLoading,\n login,\n logout,\n refresh,\n };\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n// ─── Internal hook ────────────────────────────────────────────────────────────\n\nexport function useAuthContext<User = unknown>(): AuthContextValue<User> {\n const ctx = useContext(AuthContext);\n if (!ctx) {\n throw new Error(\"useAuthContext must be used within <AuthProvider>\");\n }\n return ctx as AuthContextValue<User>;\n}\n","\"use client\";\n\nimport type { AuthSession, LoginInput } from \"../../types\";\nimport { useAuthContext } from \"../AuthProvider\";\n\nexport interface UseAuthReturn<User = unknown> {\n session: AuthSession<User>;\n login: (input: LoginInput) => Promise<void>;\n logout: () => Promise<void>;\n refresh: () => Promise<void>;\n isLoading: boolean;\n}\n\n/**\n * Primary hook for authentication operations.\n *\n * @example\n * const { session, login, logout, isLoading } = useAuth();\n */\nexport function useAuth<User = unknown>(): UseAuthReturn<User> {\n const { session, login, logout, refresh, isLoading } =\n useAuthContext<User>();\n\n return { session, login, logout, refresh, isLoading };\n}\n","\"use client\";\n\nimport type { AuthSession } from \"../../types\";\nimport { useAuthContext } from \"../AuthProvider\";\n\n/**\n * Returns the current auth session without exposing login/logout actions.\n *\n * @example\n * const { user, isAuthenticated } = useSession();\n */\nexport function useSession<User = unknown>(): AuthSession<User> {\n return useAuthContext<User>().session;\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { useAuthContext } from \"../AuthProvider\";\n\nexport interface UseRequireAuthOptions {\n /** Path to redirect unauthenticated users to. @default \"/login\" */\n redirectTo?: string;\n /** Called when the user is not authenticated (use for custom redirect logic) */\n onUnauthenticated?: () => void;\n}\n\n/**\n * Redirects unauthenticated users to the login page.\n * Works with both Next.js App Router and Pages Router.\n *\n * @example\n * // App Router (client component)\n * useRequireAuth({ redirectTo: \"/login\" });\n *\n * @example\n * // Custom handler\n * useRequireAuth({ onUnauthenticated: () => router.push(\"/login\") });\n */\nexport function useRequireAuth(options: UseRequireAuthOptions = {}): void {\n const { redirectTo = \"/login\", onUnauthenticated } = options;\n const { session, isLoading } = useAuthContext();\n\n useEffect(() => {\n if (isLoading) return;\n if (session.isAuthenticated) return;\n\n if (onUnauthenticated) {\n onUnauthenticated();\n return;\n }\n\n // Works in both App Router and Pages Router environments\n if (typeof window !== \"undefined\") {\n window.location.href = redirectTo;\n }\n }, [session.isAuthenticated, isLoading, redirectTo, onUnauthenticated]);\n}\n"]}
package/dist/index.mjs CHANGED
@@ -513,14 +513,13 @@ var AuthClient = class {
513
513
  }
514
514
  };
515
515
  var AuthContext = createContext(null);
516
- function AuthProvider({ config, children }) {
517
- const [session, setSession] = useState({
518
- user: null,
519
- tokens: null,
520
- isAuthenticated: false
521
- });
522
- const [isLoading, setIsLoading] = useState(true);
516
+ function AuthProvider({ config, children, initialSession }) {
517
+ const [session, setSession] = useState(
518
+ initialSession ?? { user: null, tokens: null, isAuthenticated: false }
519
+ );
520
+ const [isLoading, setIsLoading] = useState(!initialSession);
523
521
  useEffect(() => {
522
+ if (initialSession) return;
524
523
  let cancelled = false;
525
524
  fetch("/api/auth/session").then((res) => res.json()).then((data) => {
526
525
  if (!cancelled) {