@insforge/sdk 1.0.1-refresh.2 → 1.0.1-refresh.3

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/lib/http-client.ts","../src/lib/session-storage.ts","../src/lib/token-manager.ts","../src/lib/capability-discovery.ts","../src/modules/database-postgrest.ts","../src/modules/auth.ts","../src/modules/storage.ts","../src/modules/ai.ts","../src/modules/functions.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["/**\r\n * InsForge SDK Types - only SDK-specific types here\r\n * Use @insforge/shared-schemas directly for API types\r\n */\r\n\r\nimport type { UserSchema } from '@insforge/shared-schemas';\r\n\r\nexport interface InsForgeConfig {\r\n /**\r\n * The base URL of the InsForge backend API\r\n * @default \"http://localhost:7130\"\r\n */\r\n baseUrl?: string;\r\n\r\n /**\r\n * Anonymous API key (optional)\r\n * Used for public/unauthenticated requests when no user token is set\r\n */\r\n anonKey?: string;\r\n\r\n /**\r\n * Edge Function Token (optional)\r\n * Use this when running in edge functions/serverless with a user's JWT token\r\n * This token will be used for all authenticated requests\r\n */\r\n edgeFunctionToken?: string;\r\n\r\n /**\r\n * Custom fetch implementation (useful for Node.js environments)\r\n */\r\n fetch?: typeof fetch;\r\n\r\n /**\r\n * Storage adapter for persisting tokens\r\n */\r\n storage?: TokenStorage;\r\n\r\n /**\r\n * Whether to automatically refresh tokens before they expire\r\n * @default true\r\n */\r\n autoRefreshToken?: boolean;\r\n\r\n /**\r\n * Whether to persist session in storage\r\n * @default true\r\n */\r\n persistSession?: boolean;\r\n\r\n /**\r\n * Custom headers to include with every request\r\n */\r\n headers?: Record<string, string>;\r\n}\r\n\r\nexport interface TokenStorage {\r\n getItem(key: string): string | null | Promise<string | null>;\r\n setItem(key: string, value: string): void | Promise<void>;\r\n removeItem(key: string): void | Promise<void>;\r\n}\r\n\r\nexport interface AuthSession {\r\n user: UserSchema;\r\n accessToken: string;\r\n expiresAt?: Date;\r\n}\r\n\r\nexport interface ApiError {\r\n error: string;\r\n message: string;\r\n statusCode: number;\r\n nextActions?: string;\r\n}\r\n\r\nexport class InsForgeError extends Error {\r\n public statusCode: number;\r\n public error: string;\r\n public nextActions?: string;\r\n\r\n constructor(message: string, statusCode: number, error: string, nextActions?: string) {\r\n super(message);\r\n this.name = 'InsForgeError';\r\n this.statusCode = statusCode;\r\n this.error = error;\r\n this.nextActions = nextActions;\r\n }\r\n\r\n static fromApiError(apiError: ApiError): InsForgeError {\r\n return new InsForgeError(\r\n apiError.message,\r\n apiError.statusCode,\r\n apiError.error,\r\n apiError.nextActions\r\n );\r\n }\r\n}","import { InsForgeConfig, ApiError, InsForgeError } from '../types';\r\n\r\nexport interface RequestOptions extends RequestInit {\r\n params?: Record<string, string>;\r\n}\r\n\r\n/**\r\n * Callback type for token refresh\r\n * Returns new access token or null if refresh failed\r\n */\r\nexport type RefreshCallback = () => Promise<string | null>;\r\n\r\nexport class HttpClient {\r\n public readonly baseUrl: string;\r\n public readonly fetch: typeof fetch;\r\n private defaultHeaders: Record<string, string>;\r\n private anonKey: string | undefined;\r\n private userToken: string | null = null;\r\n \r\n // Auto-refresh support\r\n private refreshCallback?: RefreshCallback;\r\n private isRefreshing = false;\r\n private refreshQueue: Array<{\r\n resolve: (token: string) => void;\r\n reject: (error: Error) => void;\r\n }> = [];\r\n\r\n constructor(config: InsForgeConfig) {\r\n this.baseUrl = config.baseUrl || 'http://localhost:7130';\r\n // Properly bind fetch to maintain its context\r\n this.fetch = config.fetch || (globalThis.fetch ? globalThis.fetch.bind(globalThis) : undefined as any);\r\n this.anonKey = config.anonKey;\r\n this.defaultHeaders = {\r\n ...config.headers,\r\n };\r\n\r\n if (!this.fetch) {\r\n throw new Error(\r\n 'Fetch is not available. Please provide a fetch implementation in the config.'\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Set the refresh callback for automatic token refresh on 401\r\n */\r\n setRefreshCallback(callback: RefreshCallback): void {\r\n this.refreshCallback = callback;\r\n }\r\n\r\n private buildUrl(path: string, params?: Record<string, string>): string {\r\n const url = new URL(path, this.baseUrl);\r\n if (params) {\r\n Object.entries(params).forEach(([key, value]) => {\r\n // For select parameter, preserve the exact formatting by normalizing whitespace\r\n // This ensures PostgREST relationship queries work correctly\r\n if (key === 'select') {\r\n // Normalize multiline select strings for PostgREST:\r\n // 1. Replace all whitespace (including newlines) with single space\r\n // 2. Remove spaces inside parentheses for proper PostgREST syntax\r\n // 3. Keep spaces after commas at the top level for readability\r\n let normalizedValue = value.replace(/\\s+/g, ' ').trim();\r\n \r\n // Fix spaces around parentheses and inside them\r\n normalizedValue = normalizedValue\r\n .replace(/\\s*\\(\\s*/g, '(') // Remove spaces around opening parens\r\n .replace(/\\s*\\)\\s*/g, ')') // Remove spaces around closing parens\r\n .replace(/\\(\\s+/g, '(') // Remove spaces after opening parens\r\n .replace(/\\s+\\)/g, ')') // Remove spaces before closing parens\r\n .replace(/,\\s+(?=[^()]*\\))/g, ','); // Remove spaces after commas inside parens\r\n \r\n url.searchParams.append(key, normalizedValue);\r\n } else {\r\n url.searchParams.append(key, value);\r\n }\r\n });\r\n }\r\n return url.toString();\r\n }\r\n\r\n async request<T>(\r\n method: string,\r\n path: string,\r\n options: RequestOptions = {}\r\n ): Promise<T> {\r\n return this.performRequest<T>(method, path, options, false);\r\n }\r\n\r\n private async performRequest<T>(\r\n method: string,\r\n path: string,\r\n options: RequestOptions = {},\r\n isRetry = false\r\n ): Promise<T> {\r\n const { params, headers = {}, body, ...fetchOptions } = options;\r\n \r\n const url = this.buildUrl(path, params);\r\n \r\n const requestHeaders: Record<string, string> = {\r\n ...this.defaultHeaders,\r\n };\r\n \r\n // Set Authorization header: prefer user token, fallback to anon key\r\n const authToken = this.userToken || this.anonKey;\r\n if (authToken) {\r\n requestHeaders['Authorization'] = `Bearer ${authToken}`;\r\n }\r\n \r\n // Handle body serialization\r\n let processedBody: any;\r\n if (body !== undefined) {\r\n // Check if body is FormData (for file uploads)\r\n if (typeof FormData !== 'undefined' && body instanceof FormData) {\r\n // Don't set Content-Type for FormData, let browser set it with boundary\r\n processedBody = body;\r\n } else {\r\n // JSON body\r\n if (method !== 'GET') {\r\n requestHeaders['Content-Type'] = 'application/json;charset=UTF-8';\r\n }\r\n processedBody = JSON.stringify(body);\r\n }\r\n }\r\n \r\n Object.assign(requestHeaders, headers);\r\n \r\n const response = await this.fetch(url, {\r\n method,\r\n headers: requestHeaders,\r\n body: processedBody,\r\n credentials: 'include', // Essential for httpOnly cookies (refresh token)\r\n ...fetchOptions,\r\n });\r\n\r\n // Handle 401 with automatic refresh (only if we have a refresh callback and this isn't already a retry)\r\n if (response.status === 401 && !isRetry && this.refreshCallback) {\r\n const newToken = await this.handleTokenRefresh();\r\n if (newToken) {\r\n this.setAuthToken(newToken);\r\n return this.performRequest<T>(method, path, options, true);\r\n }\r\n }\r\n\r\n // Handle 204 No Content\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n // Try to parse JSON response\r\n let data: any;\r\n const contentType = response.headers.get('content-type');\r\n // Check for any JSON content type (including PostgREST's vnd.pgrst.object+json)\r\n if (contentType?.includes('json')) {\r\n data = await response.json();\r\n } else {\r\n // For non-JSON responses, return text\r\n data = await response.text();\r\n }\r\n\r\n // Handle errors\r\n if (!response.ok) {\r\n if (data && typeof data === 'object' && 'error' in data) {\r\n // Add the HTTP status code if not already in the data\r\n if (!data.statusCode && !data.status) {\r\n data.statusCode = response.status;\r\n }\r\n const error = InsForgeError.fromApiError(data as ApiError);\r\n // Preserve all additional fields from the error response\r\n Object.keys(data).forEach(key => {\r\n if (key !== 'error' && key !== 'message' && key !== 'statusCode') {\r\n (error as any)[key] = data[key];\r\n }\r\n });\r\n throw error;\r\n }\r\n throw new InsForgeError(\r\n `Request failed: ${response.statusText}`,\r\n response.status,\r\n 'REQUEST_FAILED'\r\n );\r\n }\r\n\r\n return data as T;\r\n }\r\n\r\n /**\r\n * Handle token refresh with queue to prevent duplicate refreshes\r\n * Multiple concurrent 401s will wait for a single refresh to complete\r\n */\r\n private async handleTokenRefresh(): Promise<string | null> {\r\n // If already refreshing, queue this request\r\n if (this.isRefreshing) {\r\n return new Promise((resolve, reject) => {\r\n this.refreshQueue.push({ resolve, reject });\r\n });\r\n }\r\n\r\n this.isRefreshing = true;\r\n\r\n try {\r\n const newToken = await this.refreshCallback!();\r\n \r\n // Resolve all queued requests with the new token (or null if refresh failed)\r\n this.refreshQueue.forEach(({ resolve, reject }) => {\r\n if (newToken) {\r\n resolve(newToken);\r\n } else {\r\n reject(new Error('Token refresh failed'));\r\n }\r\n });\r\n this.refreshQueue = [];\r\n \r\n return newToken;\r\n } catch (error) {\r\n // Reject all queued requests\r\n this.refreshQueue.forEach(({ reject }) => {\r\n reject(error instanceof Error ? error : new Error('Token refresh failed'));\r\n });\r\n this.refreshQueue = [];\r\n \r\n return null;\r\n } finally {\r\n this.isRefreshing = false;\r\n }\r\n }\r\n\r\n get<T>(path: string, options?: RequestOptions): Promise<T> {\r\n return this.request<T>('GET', path, options);\r\n }\r\n\r\n post<T>(path: string, body?: any, options?: RequestOptions): Promise<T> {\r\n return this.request<T>('POST', path, { ...options, body });\r\n }\r\n\r\n put<T>(path: string, body?: any, options?: RequestOptions): Promise<T> {\r\n return this.request<T>('PUT', path, { ...options, body });\r\n }\r\n\r\n patch<T>(path: string, body?: any, options?: RequestOptions): Promise<T> {\r\n return this.request<T>('PATCH', path, { ...options, body });\r\n }\r\n\r\n delete<T>(path: string, options?: RequestOptions): Promise<T> {\r\n return this.request<T>('DELETE', path, options);\r\n }\r\n\r\n setAuthToken(token: string | null) {\r\n this.userToken = token;\r\n }\r\n\r\n getHeaders(): Record<string, string> {\r\n const headers = { ...this.defaultHeaders };\r\n \r\n // Include Authorization header if token is available (same logic as request method)\r\n const authToken = this.userToken || this.anonKey;\r\n if (authToken) {\r\n headers['Authorization'] = `Bearer ${authToken}`;\r\n }\r\n \r\n return headers;\r\n }\r\n}\r\n","/**\n * Session Storage Strategies for InsForge SDK\n * \n * Implements the Strategy Pattern for token storage:\n * - SecureSessionStorage: In-memory tokens + httpOnly cookie refresh (XSS-resistant)\n * - PersistentSessionStorage: localStorage-based storage (legacy/fallback)\n */\n\nimport type { UserSchema } from '@insforge/shared-schemas';\nimport type { AuthSession, TokenStorage } from '../types';\n\n// localStorage keys for persistent storage\nconst TOKEN_KEY = 'insforge-auth-token';\nconst USER_KEY = 'insforge-auth-user';\n\n// Cookie name for optimistic refresh flag\nconst AUTH_FLAG_COOKIE = 'isAuthenticated';\n\n/**\n * Strategy interface for session storage\n * All storage implementations must conform to this interface\n */\nexport interface SessionStorageStrategy {\n /** Save complete session (token + user) */\n saveSession(session: AuthSession): void;\n \n /** Get current session */\n getSession(): AuthSession | null;\n \n /** Get access token only */\n getAccessToken(): string | null;\n \n /** Update access token (e.g., after refresh) */\n setAccessToken(token: string): void;\n \n /** Get user data */\n getUser(): UserSchema | null;\n \n /** Update user data */\n setUser(user: UserSchema): void;\n \n /** Clear all session data */\n clearSession(): void;\n \n /** Check if token refresh should be attempted (e.g., on page reload) */\n shouldAttemptRefresh(): boolean;\n \n /** Get strategy identifier for debugging */\n readonly strategyId: string;\n}\n\n/**\n * Secure Session Storage Strategy\n * \n * Stores access token in memory only (cleared on page refresh).\n * Refresh token is stored in httpOnly cookie by the backend.\n * Uses an 'isAuthenticated' flag cookie to detect if refresh should be attempted.\n * \n * Security benefits:\n * - Access token not accessible to XSS attacks (in memory only)\n * - Refresh token completely inaccessible to JavaScript (httpOnly)\n */\nexport class SecureSessionStorage implements SessionStorageStrategy {\n readonly strategyId = 'secure';\n \n private accessToken: string | null = null;\n private user: UserSchema | null = null;\n\n saveSession(session: AuthSession): void {\n this.accessToken = session.accessToken;\n this.user = session.user;\n this.setAuthFlag(true);\n }\n\n getSession(): AuthSession | null {\n if (!this.accessToken) return null;\n return {\n accessToken: this.accessToken,\n user: this.user!,\n };\n }\n\n getAccessToken(): string | null {\n return this.accessToken;\n }\n\n setAccessToken(token: string): void {\n this.accessToken = token;\n }\n\n getUser(): UserSchema | null {\n return this.user;\n }\n\n setUser(user: UserSchema): void {\n this.user = user;\n }\n\n clearSession(): void {\n this.accessToken = null;\n this.user = null;\n this.setAuthFlag(false);\n }\n\n shouldAttemptRefresh(): boolean {\n // Attempt refresh if:\n // 1. No token in memory (page was refreshed)\n // 2. Auth flag cookie exists (user was previously authenticated)\n if (this.accessToken) return false;\n return this.hasAuthFlag();\n }\n\n // --- Private: Auth Flag Cookie Management ---\n\n private setAuthFlag(authenticated: boolean): void {\n if (typeof document === 'undefined') return;\n\n if (authenticated) {\n const maxAge = 7 * 24 * 60 * 60; // 7 days\n document.cookie = `${AUTH_FLAG_COOKIE}=true; path=/; max-age=${maxAge}; SameSite=Lax`;\n } else {\n document.cookie = `${AUTH_FLAG_COOKIE}=; path=/; max-age=0`;\n }\n }\n\n private hasAuthFlag(): boolean {\n if (typeof document === 'undefined') return false;\n return document.cookie.includes(`${AUTH_FLAG_COOKIE}=true`);\n }\n}\n\n/**\n * Persistent Session Storage Strategy\n * \n * Stores tokens in localStorage for persistence across page reloads.\n * Used for legacy backends or environments where httpOnly cookies aren't available.\n * \n * Note: This approach exposes tokens to XSS attacks. Use SecureSessionStorage\n * when possible.\n */\nexport class PersistentSessionStorage implements SessionStorageStrategy {\n readonly strategyId = 'persistent';\n \n private storage: TokenStorage;\n\n constructor(storage?: TokenStorage) {\n if (storage) {\n this.storage = storage;\n } else if (typeof window !== 'undefined' && window.localStorage) {\n this.storage = window.localStorage;\n } else {\n // Fallback: in-memory storage for Node.js environments\n const store = new Map<string, string>();\n this.storage = {\n getItem: (key: string) => store.get(key) || null,\n setItem: (key: string, value: string) => { store.set(key, value); },\n removeItem: (key: string) => { store.delete(key); },\n };\n }\n }\n\n saveSession(session: AuthSession): void {\n this.storage.setItem(TOKEN_KEY, session.accessToken);\n this.storage.setItem(USER_KEY, JSON.stringify(session.user));\n }\n\n getSession(): AuthSession | null {\n const token = this.storage.getItem(TOKEN_KEY);\n const userStr = this.storage.getItem(USER_KEY);\n\n if (!token || !userStr) return null;\n\n try {\n const user = JSON.parse(userStr as string);\n return { accessToken: token as string, user };\n } catch {\n this.clearSession();\n return null;\n }\n }\n\n getAccessToken(): string | null {\n const token = this.storage.getItem(TOKEN_KEY);\n return typeof token === 'string' ? token : null;\n }\n\n setAccessToken(token: string): void {\n this.storage.setItem(TOKEN_KEY, token);\n }\n\n getUser(): UserSchema | null {\n const userStr = this.storage.getItem(USER_KEY);\n if (!userStr) return null;\n try {\n return JSON.parse(userStr as string);\n } catch {\n return null;\n }\n }\n\n setUser(user: UserSchema): void {\n this.storage.setItem(USER_KEY, JSON.stringify(user));\n }\n\n clearSession(): void {\n this.storage.removeItem(TOKEN_KEY);\n this.storage.removeItem(USER_KEY);\n }\n\n shouldAttemptRefresh(): boolean {\n // In persistent mode, we always have the token in storage\n // No need to refresh on page load\n return false;\n }\n}\n","/**\r\n * Token Manager for InsForge SDK\r\n * \r\n * A thin wrapper that delegates to the underlying SessionStorageStrategy.\r\n * This class maintains backward compatibility while using the Strategy Pattern internally.\r\n */\r\n\r\nimport type { UserSchema } from '@insforge/shared-schemas';\r\nimport type { AuthSession, TokenStorage } from '../types';\r\nimport {\r\n SessionStorageStrategy,\r\n PersistentSessionStorage,\r\n} from './session-storage';\r\n\r\n/**\r\n * TokenManager - Manages session storage using the Strategy Pattern\r\n * \r\n * The actual storage implementation is delegated to a SessionStorageStrategy.\r\n * By default, uses PersistentSessionStorage until a strategy is explicitly set\r\n * via setStrategy() during client initialization.\r\n */\r\nexport class TokenManager {\r\n private strategy: SessionStorageStrategy;\r\n\r\n /**\r\n * Create a new TokenManager\r\n * @param storage - Optional custom storage adapter (used for initial PersistentSessionStorage)\r\n */\r\n constructor(storage?: TokenStorage) {\r\n // Default to persistent storage until capability discovery completes\r\n this.strategy = new PersistentSessionStorage(storage);\r\n }\r\n\r\n /**\r\n * Set the storage strategy\r\n * Called after capability discovery to switch to the appropriate strategy\r\n */\r\n setStrategy(strategy: SessionStorageStrategy): void {\r\n // Migrate existing session data if switching strategies\r\n const existingSession = this.strategy.getSession();\r\n const previousId = this.strategy.strategyId;\r\n \r\n this.strategy = strategy;\r\n \r\n // If we had a session and are switching to a different strategy, migrate it\r\n if (existingSession && previousId !== strategy.strategyId) {\r\n strategy.saveSession(existingSession);\r\n }\r\n }\r\n\r\n /**\r\n * Get the current strategy identifier\r\n */\r\n getStrategyId(): string {\r\n return this.strategy.strategyId;\r\n }\r\n\r\n // --- Delegated Methods ---\r\n\r\n /**\r\n * Save session data\r\n */\r\n saveSession(session: AuthSession): void {\r\n this.strategy.saveSession(session);\r\n }\r\n\r\n /**\r\n * Get current session\r\n */\r\n getSession(): AuthSession | null {\r\n return this.strategy.getSession();\r\n }\r\n\r\n /**\r\n * Get access token\r\n */\r\n getAccessToken(): string | null {\r\n return this.strategy.getAccessToken();\r\n }\r\n\r\n /**\r\n * Update access token (e.g., after refresh)\r\n */\r\n setAccessToken(token: string): void {\r\n this.strategy.setAccessToken(token);\r\n }\r\n\r\n /**\r\n * Get user data\r\n */\r\n getUser(): UserSchema | null {\r\n return this.strategy.getUser();\r\n }\r\n\r\n /**\r\n * Update user data\r\n */\r\n setUser(user: UserSchema): void {\r\n this.strategy.setUser(user);\r\n }\r\n\r\n /**\r\n * Clear all session data\r\n */\r\n clearSession(): void {\r\n this.strategy.clearSession();\r\n }\r\n\r\n /**\r\n * Check if token refresh should be attempted\r\n * (e.g., on page reload in secure mode)\r\n */\r\n shouldAttemptRefresh(): boolean {\r\n return this.strategy.shouldAttemptRefresh();\r\n }\r\n}\r\n","/**\n * Backend Capability Discovery for InsForge SDK\n * \n * Discovers backend capabilities via the /api/health endpoint\n * and creates appropriate storage strategies based on those capabilities.\n */\n\nimport type { TokenStorage } from '../types';\nimport {\n SessionStorageStrategy,\n SecureSessionStorage,\n PersistentSessionStorage,\n} from './session-storage';\n\n/**\n * Backend capabilities returned from /api/health\n */\nexport interface BackendCapabilities {\n /** Whether backend supports secure httpOnly cookie storage for refresh tokens */\n secureSessionStorage: boolean;\n /** Whether backend supports token refresh endpoint */\n refreshTokens: boolean;\n}\n\n/**\n * Health endpoint response shape\n */\ninterface HealthResponse {\n status: string;\n version: string;\n service: string;\n timestamp: string;\n capabilities?: BackendCapabilities;\n}\n\n/**\n * Default capabilities for legacy backends that don't return capabilities\n */\nconst DEFAULT_CAPABILITIES: BackendCapabilities = {\n secureSessionStorage: false,\n refreshTokens: false,\n};\n\n/**\n * Discover backend capabilities from the /api/health endpoint\n * \n * This is the primary method for determining which features the backend supports.\n * The SDK uses this information to select appropriate storage strategies.\n * \n * @param baseUrl - The backend base URL\n * @param fetchImpl - Optional custom fetch implementation\n * @returns Backend capabilities object\n * \n * @example\n * ```typescript\n * const capabilities = await discoverCapabilities('https://api.example.com');\n * if (capabilities.secureSessionStorage) {\n * // Use secure storage strategy\n * }\n * ```\n */\nexport async function discoverCapabilities(\n baseUrl: string,\n fetchImpl: typeof fetch = globalThis.fetch\n): Promise<BackendCapabilities> {\n try {\n const response = await fetchImpl(`${baseUrl}/api/health`, {\n method: 'GET',\n headers: {\n 'Accept': 'application/json',\n },\n });\n\n if (!response.ok) {\n return DEFAULT_CAPABILITIES;\n }\n\n const health: HealthResponse = await response.json();\n\n // If backend returns capabilities, use them\n if (health.capabilities) {\n return health.capabilities;\n }\n\n // Legacy backend without capabilities - use defaults\n return DEFAULT_CAPABILITIES;\n } catch {\n return DEFAULT_CAPABILITIES;\n }\n}\n\n/**\n * Create the appropriate session storage strategy based on backend capabilities\n * \n * This is the factory function that implements the Strategy Pattern.\n * It selects the storage implementation based on what the backend supports.\n * \n * @param capabilities - Backend capabilities from discoverCapabilities()\n * @param storage - Optional custom storage adapter (for PersistentSessionStorage)\n * @returns Appropriate SessionStorageStrategy implementation\n * \n * @example\n * ```typescript\n * const capabilities = await discoverCapabilities(baseUrl);\n * const storage = createSessionStorage(capabilities);\n * storage.saveSession({ accessToken: '...', user: {...} });\n * ```\n */\nexport function createSessionStorage(\n capabilities: BackendCapabilities,\n storage?: TokenStorage\n): SessionStorageStrategy {\n // Use secure storage when backend supports both httpOnly cookies and refresh\n if (capabilities.secureSessionStorage && capabilities.refreshTokens) {\n return new SecureSessionStorage();\n }\n\n // Fallback to persistent (localStorage) storage\n return new PersistentSessionStorage(storage);\n}\n\n/**\n * Get default capabilities (useful for testing or manual override)\n */\nexport function getDefaultCapabilities(): BackendCapabilities {\n return { ...DEFAULT_CAPABILITIES };\n}\n","/**\r\n * Database module using @supabase/postgrest-js\r\n * Complete replacement for custom QueryBuilder with full PostgREST features\r\n */\r\n\r\nimport { PostgrestClient } from '@supabase/postgrest-js';\r\nimport { HttpClient } from '../lib/http-client';\r\nimport { TokenManager } from '../lib/token-manager';\r\n\r\n\r\n/**\r\n * Custom fetch that transforms URLs and adds auth\r\n */\r\nfunction createInsForgePostgrestFetch(\r\n httpClient: HttpClient,\r\n tokenManager: TokenManager\r\n): typeof fetch {\r\n return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\r\n const url = typeof input === 'string' ? input : input.toString();\r\n const urlObj = new URL(url);\r\n \r\n // Extract table name from pathname\r\n // postgrest-js sends: http://dummy/tablename?params\r\n // We need: http://localhost:7130/api/database/records/tablename?params\r\n const tableName = urlObj.pathname.slice(1); // Remove leading /\r\n \r\n // Build InsForge URL\r\n const insforgeUrl = `${httpClient.baseUrl}/api/database/records/${tableName}${urlObj.search}`;\r\n \r\n // Get auth token from TokenManager or HttpClient\r\n const token = tokenManager.getAccessToken();\r\n const httpHeaders = httpClient.getHeaders();\r\n const authToken = token || httpHeaders['Authorization']?.replace('Bearer ', '');\r\n \r\n // Prepare headers\r\n const headers = new Headers(init?.headers);\r\n if (authToken && !headers.has('Authorization')) {\r\n headers.set('Authorization', `Bearer ${authToken}`);\r\n }\r\n \r\n // Make the actual request using native fetch\r\n const response = await fetch(insforgeUrl, {\r\n ...init,\r\n headers\r\n });\r\n \r\n return response;\r\n };\r\n}\r\n\r\n/**\r\n * Database client using postgrest-js\r\n * Drop-in replacement with FULL PostgREST capabilities\r\n */\r\nexport class Database {\r\n private postgrest: PostgrestClient<any, any, any>;\r\n \r\n constructor(httpClient: HttpClient, tokenManager: TokenManager) {\r\n // Create postgrest client with custom fetch\r\n this.postgrest = new PostgrestClient<any, any, any>('http://dummy', {\r\n fetch: createInsForgePostgrestFetch(httpClient, tokenManager),\r\n headers: {}\r\n });\r\n }\r\n \r\n /**\r\n * Create a query builder for a table\r\n * \r\n * @example\r\n * // Basic query\r\n * const { data, error } = await client.database\r\n * .from('posts')\r\n * .select('*')\r\n * .eq('user_id', userId);\r\n * \r\n * // With count (Supabase style!)\r\n * const { data, error, count } = await client.database\r\n * .from('posts')\r\n * .select('*', { count: 'exact' })\r\n * .range(0, 9);\r\n * \r\n * // Just get count, no data\r\n * const { count } = await client.database\r\n * .from('posts')\r\n * .select('*', { count: 'exact', head: true });\r\n * \r\n * // Complex queries with OR\r\n * const { data } = await client.database\r\n * .from('posts')\r\n * .select('*, users!inner(*)')\r\n * .or('status.eq.active,status.eq.pending');\r\n * \r\n * // All features work:\r\n * - Nested selects\r\n * - Foreign key expansion \r\n * - OR/AND/NOT conditions\r\n * - Count with head\r\n * - Range pagination\r\n * - Upserts\r\n */\r\n from(table: string) {\r\n // Return postgrest query builder with all features\r\n return this.postgrest.from(table);\r\n }\r\n}","/**\r\n * Auth module for InsForge SDK\r\n * Uses shared schemas for type safety\r\n */\r\n\r\nimport { HttpClient } from '../lib/http-client';\r\nimport { TokenManager } from '../lib/token-manager';\r\nimport { AuthSession, InsForgeError } from '../types';\r\nimport { Database } from './database-postgrest';\r\n\r\nimport type {\r\n CreateUserRequest,\r\n CreateUserResponse,\r\n CreateSessionRequest,\r\n CreateSessionResponse,\r\n GetCurrentSessionResponse,\r\n GetOauthUrlResponse,\r\n GetPublicAuthConfigResponse,\r\n OAuthProvidersSchema,\r\n UserIdSchema,\r\n EmailSchema,\r\n RoleSchema,\r\n SendVerificationEmailRequest,\r\n SendResetPasswordEmailRequest,\r\n ExchangeResetPasswordTokenRequest,\r\n VerifyEmailRequest,\r\n} from '@insforge/shared-schemas';\r\n\r\n/**\r\n * Dynamic profile type - represents flexible profile data from database\r\n * Fields can vary based on database schema configuration.\r\n * All fields are converted from snake_case (database) to camelCase (API)\r\n */\r\nexport type ProfileData = Record<string, any> & {\r\n id: string; // User ID (required)\r\n createdAt?: string; // PostgreSQL TIMESTAMPTZ\r\n updatedAt?: string; // PostgreSQL TIMESTAMPTZ\r\n};\r\n\r\n/**\r\n * Dynamic profile update type - for updating profile fields\r\n * Supports any fields that exist in the profile table\r\n */\r\nexport type UpdateProfileData = Partial<Record<string, any>>;\r\n\r\n/**\r\n * Convert database profile to include both snake_case and camelCase formats\r\n * Handles dynamic fields flexibly - automatically converts all snake_case keys to camelCase\r\n * \r\n * NOTE: Backward compatibility for <= v0.0.57\r\n * Both formats are returned to maintain compatibility with existing code.\r\n * For example: both created_at and createdAt are included in the result.\r\n */\r\nfunction convertDbProfileToCamelCase(dbProfile: Record<string, any>): ProfileData {\r\n const result: ProfileData = {\r\n id: dbProfile.id,\r\n };\r\n\r\n // Convert all fields - keep both snake_case and camelCase for backward compatibility (<= v0.0.57)\r\n Object.keys(dbProfile).forEach(key => {\r\n\r\n // Keep original field (snake_case) for backward compatibility (<= v0.0.57)\r\n result[key] = dbProfile[key];\r\n\r\n // Also add camelCase version if field contains underscore\r\n // e.g., created_at -> createdAt, avatar_url -> avatarUrl, etc.\r\n if (key.includes('_')) {\r\n const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\r\n result[camelKey] = dbProfile[key];\r\n }\r\n });\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Convert camelCase profile data to database format (snake_case)\r\n * Handles dynamic fields flexibly - automatically converts all camelCase keys to snake_case\r\n */\r\nfunction convertCamelCaseToDbProfile(profile: UpdateProfileData): Record<string, any> {\r\n const dbProfile: Record<string, any> = {};\r\n\r\n Object.keys(profile).forEach(key => {\r\n if (profile[key] === undefined) return;\r\n\r\n // Convert camelCase to snake_case\r\n // e.g., avatarUrl -> avatar_url, firstName -> first_name\r\n const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);\r\n dbProfile[snakeKey] = profile[key];\r\n });\r\n\r\n return dbProfile;\r\n}\r\n\r\n/**\r\n * Check if current environment is a hosted auth environment\r\n * Returns true for:\r\n * - localhost with port 7130 (hosted auth app dev)\r\n * - https://*.insforge.app (hosted auth app production)\r\n */\r\nfunction isHostedAuthEnvironment(): boolean {\r\n if (typeof window === 'undefined') {\r\n return false;\r\n }\r\n\r\n const { hostname, port, protocol } = window.location;\r\n\r\n // Check for localhost:7130\r\n if (hostname === 'localhost' && port === '7130') {\r\n return true;\r\n }\r\n\r\n // Check for https://*.insforge.app\r\n if (protocol === 'https:' && hostname.endsWith('.insforge.app')) {\r\n return true;\r\n }\r\n\r\n return false;\r\n}\r\n\r\nexport class Auth {\r\n private database: Database;\r\n private initPromise: Promise<void> | null = null;\r\n\r\n constructor(\r\n private http: HttpClient,\r\n private tokenManager: TokenManager\r\n ) {\r\n this.database = new Database(http, tokenManager);\r\n }\r\n\r\n /**\r\n * Check if an error represents an authentication failure\r\n * Used to determine appropriate HTTP status code (401 vs 500)\r\n */\r\n private isAuthenticationError(error: unknown): boolean {\r\n if (error instanceof Error) {\r\n const message = error.message.toLowerCase();\r\n const authKeywords = [\r\n 'unauthorized',\r\n 'invalid token',\r\n 'expired token',\r\n 'token expired',\r\n 'invalid refresh token',\r\n 'refresh token',\r\n 'authentication',\r\n 'not authenticated',\r\n 'session expired',\r\n ];\r\n return authKeywords.some(keyword => message.includes(keyword));\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Set the initialization promise that auth operations should wait for\r\n * This ensures TokenManager mode is set before any auth operations\r\n */\r\n setInitPromise(promise: Promise<void>): void {\r\n this.initPromise = promise;\r\n\r\n // After init is set, trigger OAuth callback detection asynchronously\r\n this.detectAuthCallbackAsync();\r\n }\r\n\r\n /**\r\n * Wait for initialization to complete (if set)\r\n */\r\n private async waitForInit(): Promise<void> {\r\n if (this.initPromise) {\r\n await this.initPromise;\r\n }\r\n }\r\n\r\n /**\r\n * Automatically detect and handle OAuth callback parameters in the URL\r\n * This runs after initialization to seamlessly complete the OAuth flow\r\n * Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)\r\n */\r\n private async detectAuthCallbackAsync(): Promise<void> {\r\n // Only run in browser environment\r\n if (typeof window === 'undefined') return;\r\n\r\n try {\r\n // Wait for initialization to complete first\r\n await this.waitForInit();\r\n\r\n const params = new URLSearchParams(window.location.search);\r\n\r\n // Backend returns: access_token, user_id, email, name (optional)\r\n const accessToken = params.get('access_token');\r\n const userId = params.get('user_id');\r\n const email = params.get('email');\r\n const name = params.get('name');\r\n\r\n // Check if we have OAuth callback parameters\r\n if (accessToken && userId && email) {\r\n // Create session with the data from backend\r\n const session: AuthSession = {\r\n accessToken,\r\n user: {\r\n id: userId,\r\n email: email,\r\n name: name || '',\r\n // These fields are not provided by backend OAuth callback\r\n // They'll be populated when calling getCurrentUser()\r\n emailVerified: false,\r\n createdAt: new Date().toISOString(),\r\n updatedAt: new Date().toISOString(),\r\n } as any,\r\n };\r\n\r\n // Save session and set auth token\r\n this.tokenManager.saveSession(session);\r\n this.http.setAuthToken(accessToken);\r\n\r\n // Clean up the URL to remove sensitive parameters\r\n const url = new URL(window.location.href);\r\n url.searchParams.delete('access_token');\r\n url.searchParams.delete('user_id');\r\n url.searchParams.delete('email');\r\n url.searchParams.delete('name');\r\n\r\n // Also handle error case from backend (line 581)\r\n if (params.has('error')) {\r\n url.searchParams.delete('error');\r\n }\r\n\r\n // Replace URL without adding to browser history\r\n window.history.replaceState({}, document.title, url.toString());\r\n }\r\n } catch {\r\n // Silently continue - don't break initialization\r\n }\r\n }\r\n\r\n /**\r\n * Sign up a new user\r\n */\r\n async signUp(request: CreateUserRequest): Promise<{\r\n data: CreateUserResponse | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n // Wait for client initialization to ensure correct storage mode\r\n await this.waitForInit();\r\n\r\n const response = await this.http.post<CreateUserResponse>('/api/auth/users', request);\r\n\r\n // Save session internally only if both accessToken and user exist\r\n if (response.accessToken && response.user) {\r\n const session: AuthSession = {\r\n accessToken: response.accessToken,\r\n user: response.user,\r\n };\r\n if (!isHostedAuthEnvironment()) {\r\n this.tokenManager.saveSession(session);\r\n }\r\n this.http.setAuthToken(response.accessToken);\r\n }\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n error instanceof Error ? error.message : 'An unexpected error occurred during sign up',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Sign in with email and password\r\n */\r\n async signInWithPassword(request: CreateSessionRequest): Promise<{\r\n data: CreateSessionResponse | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n // Wait for client initialization to ensure correct storage mode\r\n await this.waitForInit();\r\n\r\n const response = await this.http.post<CreateSessionResponse>('/api/auth/sessions', request);\r\n\r\n // Save session internally\r\n const session: AuthSession = {\r\n accessToken: response.accessToken || '',\r\n user: response.user || {\r\n id: '',\r\n email: '',\r\n name: '',\r\n emailVerified: false,\r\n createdAt: '',\r\n updatedAt: '',\r\n },\r\n };\r\n\r\n if (!isHostedAuthEnvironment()) {\r\n this.tokenManager.saveSession(session);\r\n }\r\n this.http.setAuthToken(response.accessToken || '');\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred during sign in',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Sign in with OAuth provider\r\n */\r\n async signInWithOAuth(options: {\r\n provider: OAuthProvidersSchema;\r\n redirectTo?: string;\r\n skipBrowserRedirect?: boolean;\r\n }): Promise<{\r\n data: { url?: string; provider?: string };\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const { provider, redirectTo, skipBrowserRedirect } = options;\r\n\r\n const params = redirectTo\r\n ? { redirect_uri: redirectTo }\r\n : undefined;\r\n\r\n const endpoint = `/api/auth/oauth/${provider}`;\r\n const response = await this.http.get<GetOauthUrlResponse>(endpoint, { params });\r\n\r\n // Automatically redirect in browser unless told not to\r\n if (typeof window !== 'undefined' && !skipBrowserRedirect) {\r\n window.location.href = response.authUrl;\r\n return { data: {}, error: null };\r\n }\r\n\r\n return {\r\n data: {\r\n url: response.authUrl,\r\n provider\r\n },\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: {}, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: {},\r\n error: new InsForgeError(\r\n 'An unexpected error occurred during OAuth initialization',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Sign out the current user\r\n * In modern mode, also calls backend to clear the refresh token cookie\r\n */\r\n async signOut(): Promise<{ error: InsForgeError | null }> {\r\n try {\r\n // If using secure storage, call backend to clear refresh token cookie\r\n if (this.tokenManager.getStrategyId() === 'secure') {\r\n try {\r\n await this.http.post('/api/auth/logout');\r\n } catch {\r\n // Ignore errors from logout endpoint - still clear local session\r\n }\r\n }\r\n\r\n this.tokenManager.clearSession();\r\n this.http.setAuthToken(null);\r\n return { error: null };\r\n } catch (error) {\r\n return {\r\n error: new InsForgeError(\r\n 'Failed to sign out',\r\n 500,\r\n 'SIGNOUT_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Refresh the access token using the httpOnly refresh token cookie\r\n * Only works when backend supports secure session storage (httpOnly cookies)\r\n * \r\n * @returns New access token or throws an error\r\n */\r\n async refreshToken(): Promise<string> {\r\n try {\r\n const response = await this.http.post<{ accessToken: string; user?: any }>(\r\n '/api/auth/refresh'\r\n );\r\n\r\n if (response.accessToken) {\r\n // Update token manager with new token\r\n this.tokenManager.setAccessToken(response.accessToken);\r\n this.http.setAuthToken(response.accessToken);\r\n\r\n // Update user data if provided\r\n if (response.user) {\r\n this.tokenManager.setUser(response.user);\r\n }\r\n\r\n return response.accessToken;\r\n }\r\n\r\n throw new InsForgeError(\r\n 'No access token in refresh response',\r\n 500,\r\n 'REFRESH_FAILED'\r\n );\r\n } catch (error) {\r\n if (error instanceof InsForgeError) {\r\n // Only clear session on auth-related errors\r\n if (error.statusCode === 401 || error.statusCode === 403) {\r\n this.tokenManager.clearSession();\r\n this.http.setAuthToken(null);\r\n }\r\n throw error;\r\n }\r\n\r\n // Determine if this is an auth error or network/unknown error\r\n const errorMessage = error instanceof Error ? error.message : 'Token refresh failed';\r\n const isAuthError = this.isAuthenticationError(error);\r\n \r\n // Clear session only for auth errors\r\n if (isAuthError) {\r\n this.tokenManager.clearSession();\r\n this.http.setAuthToken(null);\r\n }\r\n\r\n throw new InsForgeError(\r\n errorMessage,\r\n isAuthError ? 401 : 500,\r\n 'REFRESH_FAILED'\r\n );\r\n }\r\n }\r\n\r\n\r\n /**\r\n * Get all public authentication configuration (OAuth + Email)\r\n * Returns both OAuth providers and email authentication settings in one request\r\n * This is a public endpoint that doesn't require authentication\r\n * \r\n * @returns Complete public authentication configuration including OAuth providers and email auth settings\r\n * \r\n * @example\r\n * ```ts\r\n * const { data, error } = await insforge.auth.getPublicAuthConfig();\r\n * if (data) {\r\n * console.log(`OAuth providers: ${data.oauth.data.length}`);\r\n * console.log(`Password min length: ${data.email.passwordMinLength}`);\r\n * }\r\n * ```\r\n */\r\n async getPublicAuthConfig(): Promise<{\r\n data: GetPublicAuthConfigResponse | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.get<GetPublicAuthConfigResponse>('/api/auth/public-config');\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while fetching public authentication configuration',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n\r\n /**\r\n * Get the current user with full profile information\r\n * Returns both auth info (id, email, role) and profile data (dynamic fields from users table)\r\n */\r\n async getCurrentUser(): Promise<{\r\n data: {\r\n user: {\r\n id: UserIdSchema;\r\n email: EmailSchema;\r\n role: RoleSchema;\r\n };\r\n profile: ProfileData | null;\r\n } | null;\r\n error: any | null;\r\n }> {\r\n try {\r\n // Check if we have a token\r\n const session = this.tokenManager.getSession();\r\n if (!session?.accessToken) {\r\n return { data: null, error: null };\r\n }\r\n\r\n // Call the API for auth info\r\n this.http.setAuthToken(session.accessToken);\r\n const authResponse = await this.http.get<GetCurrentSessionResponse>('/api/auth/sessions/current');\r\n\r\n // Get the user's profile using query builder\r\n const { data: profile, error: profileError } = await this.database\r\n .from('users')\r\n .select('*')\r\n .eq('id', authResponse.user.id)\r\n .single();\r\n\r\n // For database errors, return PostgrestError directly\r\n if (profileError && (profileError as any).code !== 'PGRST116') { // PGRST116 = not found\r\n return { data: null, error: profileError };\r\n }\r\n\r\n return {\r\n data: {\r\n user: authResponse.user,\r\n profile: profile ? convertDbProfileToCamelCase(profile) : null\r\n },\r\n error: null\r\n };\r\n } catch (error) {\r\n // If unauthorized, clear session\r\n if (error instanceof InsForgeError && error.statusCode === 401) {\r\n await this.signOut();\r\n return { data: null, error: null };\r\n }\r\n\r\n // Pass through all other errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while fetching user',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get any user's profile by ID\r\n * Returns profile information from the users table (dynamic fields)\r\n */\r\n async getProfile(userId: string): Promise<{\r\n data: ProfileData | null;\r\n error: any | null;\r\n }> {\r\n const { data, error } = await this.database\r\n .from('users')\r\n .select('*')\r\n .eq('id', userId)\r\n .single();\r\n\r\n // Handle not found as null, not error\r\n if (error && (error as any).code === 'PGRST116') {\r\n return { data: null, error: null };\r\n }\r\n\r\n // Convert database format to camelCase format\r\n if (data) {\r\n return { data: convertDbProfileToCamelCase(data), error: null };\r\n }\r\n\r\n // Return PostgrestError directly for database operations\r\n return { data: null, error };\r\n }\r\n\r\n /**\r\n * Get the current session (only session data, no API call)\r\n * Returns the stored JWT token and basic user info from local storage\r\n */\r\n getCurrentSession(): {\r\n data: { session: AuthSession | null };\r\n error: InsForgeError | null;\r\n } {\r\n try {\r\n const session = this.tokenManager.getSession();\r\n\r\n if (session?.accessToken) {\r\n this.http.setAuthToken(session.accessToken);\r\n return { data: { session }, error: null };\r\n }\r\n\r\n return { data: { session: null }, error: null };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: { session: null }, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: { session: null },\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while getting session',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Set/Update the current user's profile\r\n * Updates profile information in the users table (supports any dynamic fields)\r\n */\r\n async setProfile(profile: UpdateProfileData): Promise<{\r\n data: ProfileData | null;\r\n error: any | null;\r\n }> {\r\n // Get current session to get user ID\r\n const session = this.tokenManager.getSession();\r\n if (!session?.accessToken) {\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'No authenticated user found',\r\n 401,\r\n 'UNAUTHENTICATED'\r\n )\r\n };\r\n }\r\n\r\n // If no user ID in session (edge function scenario), fetch it\r\n if (!session.user?.id) {\r\n const { data, error } = await this.getCurrentUser();\r\n if (error) {\r\n return { data: null, error };\r\n }\r\n if (data?.user) {\r\n // Update session with minimal user info\r\n session.user = {\r\n id: data.user.id,\r\n email: data.user.email,\r\n name: '',\r\n emailVerified: false,\r\n createdAt: new Date().toISOString(),\r\n updatedAt: new Date().toISOString(),\r\n };\r\n this.tokenManager.saveSession(session);\r\n }\r\n }\r\n\r\n // Convert camelCase format to database format (snake_case)\r\n const dbProfile = convertCamelCaseToDbProfile(profile);\r\n\r\n // Update the profile using query builder\r\n const { data, error } = await this.database\r\n .from('users')\r\n .update(dbProfile)\r\n .eq('id', session.user.id)\r\n .select()\r\n .single();\r\n\r\n // Convert database format back to camelCase format\r\n if (data) {\r\n return { data: convertDbProfileToCamelCase(data), error: null };\r\n }\r\n\r\n // Return PostgrestError directly for database operations\r\n return { data: null, error };\r\n }\r\n\r\n /**\r\n * Send email verification (code or link based on config)\r\n *\r\n * Send email verification using the method configured in auth settings (verifyEmailMethod).\r\n * When method is 'code', sends a 6-digit numeric code. When method is 'link', sends a magic link.\r\n * Prevents user enumeration by returning success even if email doesn't exist.\r\n */\r\n async sendVerificationEmail(request: SendVerificationEmailRequest): Promise<{\r\n data: { success: boolean; message: string } | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<{ success: boolean; message: string }>(\r\n '/api/auth/email/send-verification',\r\n request\r\n );\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while sending verification code',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Send password reset (code or link based on config)\r\n *\r\n * Send password reset email using the method configured in auth settings (resetPasswordMethod).\r\n * When method is 'code', sends a 6-digit numeric code for two-step flow.\r\n * When method is 'link', sends a magic link.\r\n * Prevents user enumeration by returning success even if email doesn't exist.\r\n */\r\n async sendResetPasswordEmail(request: SendResetPasswordEmailRequest): Promise<{\r\n data: { success: boolean; message: string } | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<{ success: boolean; message: string }>(\r\n '/api/auth/email/send-reset-password',\r\n request\r\n );\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while sending password reset code',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Exchange reset password code for reset token\r\n *\r\n * Step 1 of two-step password reset flow (only used when resetPasswordMethod is 'code'):\r\n * 1. Verify the 6-digit code sent to user's email\r\n * 2. Return a reset token that can be used to actually reset the password\r\n *\r\n * This endpoint is not used when resetPasswordMethod is 'link' (magic link flow is direct).\r\n */\r\n async exchangeResetPasswordToken(request: ExchangeResetPasswordTokenRequest): Promise<{\r\n data: { token: string; expiresAt: string } | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<{ token: string; expiresAt: string }>(\r\n '/api/auth/email/exchange-reset-password-token',\r\n request\r\n );\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while verifying reset code',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Reset password with token\r\n *\r\n * Reset user password with a token. The token can be:\r\n * - Magic link token (64-character hex token from send-reset-password when method is 'link')\r\n * - Reset token (from exchange-reset-password-token after code verification when method is 'code')\r\n *\r\n * Both token types use RESET_PASSWORD purpose and are verified the same way.\r\n *\r\n * Flow summary:\r\n * - Code method: send-reset-password → exchange-reset-password-token → reset-password (with resetToken)\r\n * - Link method: send-reset-password → reset-password (with link token directly)\r\n */\r\n async resetPassword(request: { newPassword: string; otp: string }): Promise<{\r\n data: { message: string; redirectTo?: string } | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<{ message: string; redirectTo?: string }>(\r\n '/api/auth/email/reset-password',\r\n request\r\n );\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while resetting password',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Verify email with code or link\r\n *\r\n * Verify email address using the method configured in auth settings (verifyEmailMethod):\r\n * - Code verification: Provide both `email` and `otp` (6-digit numeric code)\r\n * - Link verification: Provide only `otp` (64-character hex token from magic link)\r\n *\r\n * Successfully verified users will receive a session token.\r\n *\r\n * The email verification link sent to users always points to the backend API endpoint.\r\n * If `verifyEmailRedirectTo` is configured, the backend will redirect to that URL after successful verification.\r\n * Otherwise, a default success page is displayed.\r\n */\r\n async verifyEmail(request: VerifyEmailRequest): Promise<{\r\n data: { accessToken: string; user?: any; redirectTo?: string } | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n // Wait for client initialization to ensure correct storage mode\r\n await this.waitForInit();\r\n\r\n const response = await this.http.post<{ accessToken: string; user?: any; redirectTo?: string }>(\r\n '/api/auth/email/verify',\r\n request\r\n );\r\n\r\n // Save session if we got a token\r\n if (response.accessToken) {\r\n const session: AuthSession = {\r\n accessToken: response.accessToken,\r\n user: response.user || {} as any,\r\n };\r\n this.tokenManager.saveSession(session);\r\n this.http.setAuthToken(response.accessToken);\r\n }\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while verifying email',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n}","/**\r\n * Storage module for InsForge SDK\r\n * Handles file uploads, downloads, and bucket management\r\n */\r\n\r\nimport { HttpClient } from '../lib/http-client';\r\nimport { InsForgeError } from '../types';\r\nimport type { \r\n StorageFileSchema,\r\n ListObjectsResponseSchema\r\n} from '@insforge/shared-schemas';\r\n\r\nexport interface StorageResponse<T> {\r\n data: T | null;\r\n error: InsForgeError | null;\r\n}\r\n\r\ninterface UploadStrategy {\r\n method: 'direct' | 'presigned';\r\n uploadUrl: string;\r\n fields?: Record<string, string>;\r\n key: string;\r\n confirmRequired: boolean;\r\n confirmUrl?: string;\r\n expiresAt?: Date;\r\n}\r\n\r\ninterface DownloadStrategy {\r\n method: 'direct' | 'presigned';\r\n url: string;\r\n expiresAt?: Date;\r\n}\r\n\r\n/**\r\n * Storage bucket operations\r\n */\r\nexport class StorageBucket {\r\n constructor(\r\n private bucketName: string,\r\n private http: HttpClient\r\n ) {}\r\n\r\n /**\r\n * Upload a file with a specific key\r\n * Uses the upload strategy from backend (direct or presigned)\r\n * @param path - The object key/path\r\n * @param file - File or Blob to upload\r\n */\r\n async upload(\r\n path: string,\r\n file: File | Blob\r\n ): Promise<StorageResponse<StorageFileSchema>> {\r\n try {\r\n // Get upload strategy from backend - this is required\r\n const strategyResponse = await this.http.post<UploadStrategy>(\r\n `/api/storage/buckets/${this.bucketName}/upload-strategy`,\r\n {\r\n filename: path,\r\n contentType: file.type || 'application/octet-stream',\r\n size: file.size\r\n }\r\n );\r\n\r\n // Use presigned URL if available\r\n if (strategyResponse.method === 'presigned') {\r\n return await this.uploadWithPresignedUrl(strategyResponse, file);\r\n }\r\n\r\n // Use direct upload if strategy says so\r\n if (strategyResponse.method === 'direct') {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n\r\n const response = await this.http.request<StorageFileSchema>(\r\n 'PUT',\r\n `/api/storage/buckets/${this.bucketName}/objects/${encodeURIComponent(path)}`,\r\n {\r\n body: formData as any,\r\n headers: {\r\n // Don't set Content-Type, let browser set multipart boundary\r\n }\r\n }\r\n );\r\n\r\n return { data: response, error: null };\r\n }\r\n\r\n throw new InsForgeError(\r\n `Unsupported upload method: ${strategyResponse.method}`,\r\n 500,\r\n 'STORAGE_ERROR'\r\n );\r\n } catch (error) {\r\n return { \r\n data: null, \r\n error: error instanceof InsForgeError ? error : new InsForgeError(\r\n 'Upload failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Upload a file with auto-generated key\r\n * Uses the upload strategy from backend (direct or presigned)\r\n * @param file - File or Blob to upload\r\n */\r\n async uploadAuto(\r\n file: File | Blob\r\n ): Promise<StorageResponse<StorageFileSchema>> {\r\n try {\r\n const filename = file instanceof File ? file.name : 'file';\r\n \r\n // Get upload strategy from backend - this is required\r\n const strategyResponse = await this.http.post<UploadStrategy>(\r\n `/api/storage/buckets/${this.bucketName}/upload-strategy`,\r\n {\r\n filename,\r\n contentType: file.type || 'application/octet-stream',\r\n size: file.size\r\n }\r\n );\r\n\r\n // Use presigned URL if available\r\n if (strategyResponse.method === 'presigned') {\r\n return await this.uploadWithPresignedUrl(strategyResponse, file);\r\n }\r\n\r\n // Use direct upload if strategy says so\r\n if (strategyResponse.method === 'direct') {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n\r\n const response = await this.http.request<StorageFileSchema>(\r\n 'POST',\r\n `/api/storage/buckets/${this.bucketName}/objects`,\r\n {\r\n body: formData as any,\r\n headers: {\r\n // Don't set Content-Type, let browser set multipart boundary\r\n }\r\n }\r\n );\r\n\r\n return { data: response, error: null };\r\n }\r\n\r\n throw new InsForgeError(\r\n `Unsupported upload method: ${strategyResponse.method}`,\r\n 500,\r\n 'STORAGE_ERROR'\r\n );\r\n } catch (error) {\r\n return { \r\n data: null, \r\n error: error instanceof InsForgeError ? error : new InsForgeError(\r\n 'Upload failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Internal method to handle presigned URL uploads\r\n */\r\n private async uploadWithPresignedUrl(\r\n strategy: UploadStrategy,\r\n file: File | Blob\r\n ): Promise<StorageResponse<StorageFileSchema>> {\r\n try {\r\n // Upload to presigned URL (e.g., S3)\r\n const formData = new FormData();\r\n \r\n // Add all fields from the presigned URL\r\n if (strategy.fields) {\r\n Object.entries(strategy.fields).forEach(([key, value]) => {\r\n formData.append(key, value);\r\n });\r\n }\r\n \r\n // File must be the last field for S3\r\n formData.append('file', file);\r\n\r\n const uploadResponse = await fetch(strategy.uploadUrl, {\r\n method: 'POST',\r\n body: formData\r\n });\r\n\r\n if (!uploadResponse.ok) {\r\n throw new InsForgeError(\r\n `Upload to storage failed: ${uploadResponse.statusText}`,\r\n uploadResponse.status,\r\n 'STORAGE_ERROR'\r\n );\r\n }\r\n\r\n // Confirm upload with backend if required\r\n if (strategy.confirmRequired && strategy.confirmUrl) {\r\n const confirmResponse = await this.http.post<StorageFileSchema>(\r\n strategy.confirmUrl,\r\n {\r\n size: file.size,\r\n contentType: file.type || 'application/octet-stream'\r\n }\r\n );\r\n\r\n return { data: confirmResponse, error: null };\r\n }\r\n\r\n // If no confirmation required, return basic file info\r\n return {\r\n data: {\r\n key: strategy.key,\r\n bucket: this.bucketName,\r\n size: file.size,\r\n mimeType: file.type || 'application/octet-stream',\r\n uploadedAt: new Date().toISOString(),\r\n url: this.getPublicUrl(strategy.key)\r\n } as StorageFileSchema,\r\n error: null\r\n };\r\n } catch (error) {\r\n throw error instanceof InsForgeError ? error : new InsForgeError(\r\n 'Presigned upload failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Download a file\r\n * Uses the download strategy from backend (direct or presigned)\r\n * @param path - The object key/path\r\n * Returns the file as a Blob\r\n */\r\n async download(path: string): Promise<{ data: Blob | null; error: InsForgeError | null }> {\r\n try {\r\n // Get download strategy from backend - this is required\r\n const strategyResponse = await this.http.post<DownloadStrategy>(\r\n `/api/storage/buckets/${this.bucketName}/objects/${encodeURIComponent(path)}/download-strategy`,\r\n { expiresIn: 3600 }\r\n );\r\n\r\n // Use URL from strategy\r\n const downloadUrl = strategyResponse.url;\r\n \r\n // Download from the URL\r\n const headers: HeadersInit = {};\r\n \r\n // Only add auth header for direct downloads (not presigned URLs)\r\n if (strategyResponse.method === 'direct') {\r\n Object.assign(headers, this.http.getHeaders());\r\n }\r\n \r\n const response = await fetch(downloadUrl, {\r\n method: 'GET',\r\n headers\r\n });\r\n\r\n if (!response.ok) {\r\n try {\r\n const error = await response.json();\r\n throw InsForgeError.fromApiError(error);\r\n } catch {\r\n throw new InsForgeError(\r\n `Download failed: ${response.statusText}`,\r\n response.status,\r\n 'STORAGE_ERROR'\r\n );\r\n }\r\n }\r\n\r\n const blob = await response.blob();\r\n return { data: blob, error: null };\r\n } catch (error) {\r\n return { \r\n data: null, \r\n error: error instanceof InsForgeError ? error : new InsForgeError(\r\n 'Download failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get public URL for a file\r\n * @param path - The object key/path\r\n */\r\n getPublicUrl(path: string): string {\r\n return `${this.http.baseUrl}/api/storage/buckets/${this.bucketName}/objects/${encodeURIComponent(path)}`;\r\n }\r\n\r\n /**\r\n * List objects in the bucket\r\n * @param prefix - Filter by key prefix\r\n * @param search - Search in file names\r\n * @param limit - Maximum number of results (default: 100, max: 1000)\r\n * @param offset - Number of results to skip\r\n */\r\n async list(options?: {\r\n prefix?: string;\r\n search?: string;\r\n limit?: number;\r\n offset?: number;\r\n }): Promise<StorageResponse<ListObjectsResponseSchema>> {\r\n try {\r\n const params: Record<string, string> = {};\r\n \r\n if (options?.prefix) params.prefix = options.prefix;\r\n if (options?.search) params.search = options.search;\r\n if (options?.limit) params.limit = options.limit.toString();\r\n if (options?.offset) params.offset = options.offset.toString();\r\n\r\n const response = await this.http.get<ListObjectsResponseSchema>(\r\n `/api/storage/buckets/${this.bucketName}/objects`,\r\n { params }\r\n );\r\n\r\n return { data: response, error: null };\r\n } catch (error) {\r\n return { \r\n data: null, \r\n error: error instanceof InsForgeError ? error : new InsForgeError(\r\n 'List failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Delete a file\r\n * @param path - The object key/path\r\n */\r\n async remove(path: string): Promise<StorageResponse<{ message: string }>> {\r\n try {\r\n const response = await this.http.delete<{ message: string }>(\r\n `/api/storage/buckets/${this.bucketName}/objects/${encodeURIComponent(path)}`\r\n );\r\n\r\n return { data: response, error: null };\r\n } catch (error) {\r\n return { \r\n data: null, \r\n error: error instanceof InsForgeError ? error : new InsForgeError(\r\n 'Delete failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Storage module for file operations\r\n */\r\nexport class Storage {\r\n constructor(private http: HttpClient) {}\r\n\r\n /**\r\n * Get a bucket instance for operations\r\n * @param bucketName - Name of the bucket\r\n */\r\n from(bucketName: string): StorageBucket {\r\n return new StorageBucket(bucketName, this.http);\r\n }\r\n}","/**\r\n * AI Module for Insforge SDK\r\n * Response format roughly matches OpenAI SDK for compatibility\r\n *\r\n * The backend handles all the complexity of different AI providers\r\n * and returns a unified format. This SDK transforms responses to match OpenAI-like format.\r\n */\r\n\r\nimport { HttpClient } from \"../lib/http-client\";\r\nimport {\r\n ChatCompletionRequest,\r\n ChatCompletionResponse,\r\n ImageGenerationRequest,\r\n ImageGenerationResponse,\r\n} from \"@insforge/shared-schemas\";\r\n\r\nexport class AI {\r\n public readonly chat: Chat;\r\n public readonly images: Images;\r\n\r\n constructor(private http: HttpClient) {\r\n this.chat = new Chat(http);\r\n this.images = new Images(http);\r\n }\r\n}\r\n\r\nclass Chat {\r\n public readonly completions: ChatCompletions;\r\n\r\n constructor(http: HttpClient) {\r\n this.completions = new ChatCompletions(http);\r\n }\r\n}\r\n\r\nclass ChatCompletions {\r\n constructor(private http: HttpClient) {}\r\n\r\n /**\r\n * Create a chat completion - OpenAI-like response format\r\n *\r\n * @example\r\n * ```typescript\r\n * // Non-streaming\r\n * const completion = await client.ai.chat.completions.create({\r\n * model: 'gpt-4',\r\n * messages: [{ role: 'user', content: 'Hello!' }]\r\n * });\r\n * console.log(completion.choices[0].message.content);\r\n *\r\n * // With images\r\n * const response = await client.ai.chat.completions.create({\r\n * model: 'gpt-4-vision',\r\n * messages: [{\r\n * role: 'user',\r\n * content: 'What is in this image?',\r\n * images: [{ url: 'https://example.com/image.jpg' }]\r\n * }]\r\n * });\r\n *\r\n * // Streaming - returns async iterable\r\n * const stream = await client.ai.chat.completions.create({\r\n * model: 'gpt-4',\r\n * messages: [{ role: 'user', content: 'Tell me a story' }],\r\n * stream: true\r\n * });\r\n *\r\n * for await (const chunk of stream) {\r\n * if (chunk.choices[0]?.delta?.content) {\r\n * process.stdout.write(chunk.choices[0].delta.content);\r\n * }\r\n * }\r\n * ```\r\n */\r\n async create(params: ChatCompletionRequest): Promise<any> {\r\n // Backend already expects camelCase, no transformation needed\r\n const backendParams = {\r\n model: params.model,\r\n messages: params.messages,\r\n temperature: params.temperature,\r\n maxTokens: params.maxTokens,\r\n topP: params.topP,\r\n stream: params.stream,\r\n };\r\n\r\n // For streaming, return an async iterable that yields OpenAI-like chunks\r\n if (params.stream) {\r\n const headers = this.http.getHeaders();\r\n headers[\"Content-Type\"] = \"application/json\";\r\n\r\n const response = await this.http.fetch(\r\n `${this.http.baseUrl}/api/ai/chat/completion`,\r\n {\r\n method: \"POST\",\r\n headers,\r\n body: JSON.stringify(backendParams),\r\n }\r\n );\r\n\r\n if (!response.ok) {\r\n const error = await response.json();\r\n throw new Error(error.error || \"Stream request failed\");\r\n }\r\n\r\n // Return async iterable that parses SSE and transforms to OpenAI-like format\r\n return this.parseSSEStream(response, params.model);\r\n }\r\n\r\n // Non-streaming: transform response to OpenAI-like format\r\n const response: ChatCompletionResponse = await this.http.post(\r\n \"/api/ai/chat/completion\",\r\n backendParams\r\n );\r\n\r\n // Transform to OpenAI-like format\r\n const content = response.text || \"\";\r\n\r\n return {\r\n id: `chatcmpl-${Date.now()}`,\r\n object: \"chat.completion\",\r\n created: Math.floor(Date.now() / 1000),\r\n model: response.metadata?.model,\r\n choices: [\r\n {\r\n index: 0,\r\n message: {\r\n role: \"assistant\",\r\n content,\r\n },\r\n finish_reason: \"stop\",\r\n },\r\n ],\r\n usage: response.metadata?.usage || {\r\n prompt_tokens: 0,\r\n completion_tokens: 0,\r\n total_tokens: 0,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Parse SSE stream into async iterable of OpenAI-like chunks\r\n */\r\n private async *parseSSEStream(\r\n response: Response,\r\n model: string\r\n ): AsyncIterableIterator<any> {\r\n const reader = response.body!.getReader();\r\n const decoder = new TextDecoder();\r\n let buffer = \"\";\r\n\r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n buffer += decoder.decode(value, { stream: true });\r\n const lines = buffer.split(\"\\n\");\r\n buffer = lines.pop() || \"\";\r\n\r\n for (const line of lines) {\r\n if (line.startsWith(\"data: \")) {\r\n const dataStr = line.slice(6).trim();\r\n if (dataStr) {\r\n try {\r\n const data = JSON.parse(dataStr);\r\n\r\n // Transform to OpenAI-like streaming format\r\n if (data.chunk || data.content) {\r\n yield {\r\n id: `chatcmpl-${Date.now()}`,\r\n object: \"chat.completion.chunk\",\r\n created: Math.floor(Date.now() / 1000),\r\n model,\r\n choices: [\r\n {\r\n index: 0,\r\n delta: {\r\n content: data.chunk || data.content,\r\n },\r\n finish_reason: data.done ? \"stop\" : null,\r\n },\r\n ],\r\n };\r\n }\r\n\r\n // If we received the done signal, we can stop\r\n if (data.done) {\r\n reader.releaseLock();\r\n return;\r\n }\r\n } catch (e) {\r\n // Skip invalid JSON\r\n console.warn(\"Failed to parse SSE data:\", dataStr);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n}\r\n\r\nclass Images {\r\n constructor(private http: HttpClient) {}\r\n\r\n /**\r\n * Generate images - OpenAI-like response format\r\n *\r\n * @example\r\n * ```typescript\r\n * // Text-to-image\r\n * const response = await client.ai.images.generate({\r\n * model: 'dall-e-3',\r\n * prompt: 'A sunset over mountains',\r\n * });\r\n * console.log(response.images[0].url);\r\n *\r\n * // Image-to-image (with input images)\r\n * const response = await client.ai.images.generate({\r\n * model: 'stable-diffusion-xl',\r\n * prompt: 'Transform this into a watercolor painting',\r\n * images: [\r\n * { url: 'https://example.com/input.jpg' },\r\n * // or base64-encoded Data URI:\r\n * { url: 'data:image/jpeg;base64,/9j/4AAQ...' }\r\n * ]\r\n * });\r\n * ```\r\n */\r\n async generate(params: ImageGenerationRequest): Promise<any> {\r\n const response: ImageGenerationResponse = await this.http.post(\r\n \"/api/ai/image/generation\",\r\n params\r\n );\r\n \r\n // Build data array based on response content\r\n let data: Array<{ b64_json?: string; content?: string }> = [];\r\n \r\n if (response.images && response.images.length > 0) {\r\n // Has images - extract base64 and include text\r\n data = response.images.map(img => ({\r\n b64_json: img.imageUrl.replace(/^data:image\\/\\w+;base64,/, ''),\r\n content: response.text\r\n }));\r\n } else if (response.text) {\r\n // Text-only response\r\n data = [{ content: response.text }];\r\n }\r\n \r\n // Return OpenAI-compatible format\r\n return {\r\n created: Math.floor(Date.now() / 1000),\r\n data,\r\n ...(response.metadata?.usage && {\r\n usage: {\r\n total_tokens: response.metadata.usage.totalTokens || 0,\r\n input_tokens: response.metadata.usage.promptTokens || 0,\r\n output_tokens: response.metadata.usage.completionTokens || 0,\r\n }\r\n })\r\n };\r\n }\r\n}\r\n","import { HttpClient } from '../lib/http-client';\r\n\r\nexport interface FunctionInvokeOptions {\r\n /**\r\n * The body of the request\r\n */\r\n body?: any;\r\n \r\n /**\r\n * Custom headers to send with the request\r\n */\r\n headers?: Record<string, string>;\r\n \r\n /**\r\n * HTTP method (default: POST)\r\n */\r\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\r\n}\r\n\r\n/**\r\n * Edge Functions client for invoking serverless functions\r\n * \r\n * @example\r\n * ```typescript\r\n * // Invoke a function with JSON body\r\n * const { data, error } = await client.functions.invoke('hello-world', {\r\n * body: { name: 'World' }\r\n * });\r\n * \r\n * // GET request\r\n * const { data, error } = await client.functions.invoke('get-data', {\r\n * method: 'GET'\r\n * });\r\n * ```\r\n */\r\nexport class Functions {\r\n private http: HttpClient;\r\n\r\n constructor(http: HttpClient) {\r\n this.http = http;\r\n }\r\n\r\n /**\r\n * Invokes an Edge Function\r\n * @param slug The function slug to invoke\r\n * @param options Request options\r\n */\r\n async invoke<T = any>(\r\n slug: string,\r\n options: FunctionInvokeOptions = {}\r\n ): Promise<{ data: T | null; error: Error | null }> {\r\n try {\r\n const { method = 'POST', body, headers = {} } = options;\r\n \r\n // Simple path: /functions/{slug}\r\n const path = `/functions/${slug}`;\r\n \r\n // Use the HTTP client's request method\r\n const data = await this.http.request<T>(\r\n method,\r\n path,\r\n { body, headers }\r\n );\r\n \r\n return { data, error: null };\r\n } catch (error: any) {\r\n // The HTTP client throws InsForgeError with all properties from the response\r\n // including error, message, details, statusCode, etc.\r\n // We need to preserve all of that information\r\n return { \r\n data: null, \r\n error: error // Pass through the full error object with all properties\r\n };\r\n }\r\n }\r\n}","import { InsForgeConfig } from './types';\r\nimport { HttpClient } from './lib/http-client';\r\nimport { TokenManager } from './lib/token-manager';\r\nimport {\r\n discoverCapabilities,\r\n createSessionStorage,\r\n BackendCapabilities,\r\n} from './lib/capability-discovery';\r\nimport { Auth } from './modules/auth';\r\nimport { Database } from './modules/database-postgrest';\r\nimport { Storage } from './modules/storage';\r\nimport { AI } from './modules/ai';\r\nimport { Functions } from './modules/functions';\r\n\r\n/**\r\n * Main InsForge SDK Client\r\n * \r\n * @example\r\n * ```typescript\r\n * import { InsForgeClient } from '@insforge/sdk';\r\n * \r\n * const client = new InsForgeClient({\r\n * baseUrl: 'http://localhost:7130'\r\n * });\r\n * \r\n * // Wait for initialization (optional but recommended)\r\n * await client.initialize();\r\n * \r\n * // Authentication\r\n * const session = await client.auth.signUp({\r\n * email: 'user@example.com',\r\n * password: 'password123',\r\n * name: 'John Doe'\r\n * });\r\n * \r\n * // Database operations\r\n * const { data, error } = await client.database\r\n * .from('posts')\r\n * .select('*')\r\n * .eq('user_id', session.user.id)\r\n * .order('created_at', { ascending: false })\r\n * .limit(10);\r\n * \r\n * // Insert data\r\n * const { data: newPost } = await client.database\r\n * .from('posts')\r\n * .insert({ title: 'Hello', content: 'World' })\r\n * .single();\r\n * \r\n * // Invoke edge functions\r\n * const { data, error } = await client.functions.invoke('my-function', {\r\n * body: { message: 'Hello from SDK' }\r\n * });\r\n * ```\r\n */\r\nexport class InsForgeClient {\r\n private http: HttpClient;\r\n private tokenManager: TokenManager;\r\n private initialized = false;\r\n private initializationPromise: Promise<void> | null = null;\r\n private capabilities: BackendCapabilities | null = null;\r\n\r\n public readonly auth: Auth;\r\n public readonly database: Database;\r\n public readonly storage: Storage;\r\n public readonly ai: AI;\r\n public readonly functions: Functions;\r\n\r\n constructor(config: InsForgeConfig = {}) {\r\n this.http = new HttpClient(config);\r\n this.tokenManager = new TokenManager(config.storage);\r\n\r\n // Check for edge function token\r\n if (config.edgeFunctionToken) {\r\n this.http.setAuthToken(config.edgeFunctionToken);\r\n this.tokenManager.saveSession({\r\n accessToken: config.edgeFunctionToken,\r\n user: {} as any, // Will be populated by getCurrentUser()\r\n });\r\n }\r\n\r\n // Create auth module\r\n this.auth = new Auth(this.http, this.tokenManager);\r\n\r\n // Set up refresh callback for auto-refresh on 401\r\n this.http.setRefreshCallback(async () => {\r\n try {\r\n return await this.auth.refreshToken();\r\n } catch {\r\n return null;\r\n }\r\n });\r\n\r\n // Check for existing session in storage (for initial load)\r\n const existingSession = this.tokenManager.getSession();\r\n if (existingSession?.accessToken) {\r\n this.http.setAuthToken(existingSession.accessToken);\r\n }\r\n\r\n // Initialize other modules\r\n this.database = new Database(this.http, this.tokenManager);\r\n this.storage = new Storage(this.http);\r\n this.ai = new AI(this.http);\r\n this.functions = new Functions(this.http);\r\n\r\n // Start async initialization (non-blocking)\r\n this.initializationPromise = this.initializeAsync();\r\n\r\n // Set init promise on auth module so auth operations wait for initialization\r\n this.auth.setInitPromise(this.initializationPromise);\r\n }\r\n\r\n /**\r\n * Initialize the client by discovering backend capabilities\r\n * This is called automatically on construction but can be awaited for guaranteed initialization\r\n * \r\n * @example\r\n * ```typescript\r\n * const client = new InsForgeClient({ baseUrl: 'https://api.example.com' });\r\n * await client.initialize(); // Wait for capability discovery\r\n * ```\r\n */\r\n async initialize(): Promise<void> {\r\n if (this.initializationPromise) {\r\n await this.initializationPromise;\r\n }\r\n }\r\n\r\n /**\r\n * Internal async initialization - discovers capabilities and configures storage strategy\r\n */\r\n private async initializeAsync(): Promise<void> {\r\n if (this.initialized) return;\r\n\r\n try {\r\n // Discover backend capabilities\r\n this.capabilities = await discoverCapabilities(\r\n this.http.baseUrl,\r\n this.http.fetch\r\n );\r\n\r\n // Create and set appropriate storage strategy\r\n const strategy = createSessionStorage(this.capabilities);\r\n this.tokenManager.setStrategy(strategy);\r\n\r\n // If secure storage and should attempt refresh, do so\r\n if (this.capabilities.refreshTokens && this.tokenManager.shouldAttemptRefresh()) {\r\n try {\r\n const newToken = await this.auth.refreshToken();\r\n this.http.setAuthToken(newToken);\r\n } catch {\r\n // Refresh failed - session expired or invalid\r\n this.tokenManager.clearSession();\r\n this.http.setAuthToken(null);\r\n }\r\n }\r\n\r\n this.initialized = true;\r\n } catch {\r\n // If discovery fails, continue with default (persistent) storage\r\n this.initialized = true;\r\n }\r\n }\r\n\r\n /**\r\n * Get the underlying HTTP client for custom requests\r\n * \r\n * @example\r\n * ```typescript\r\n * const httpClient = client.getHttpClient();\r\n * const customData = await httpClient.get('/api/custom-endpoint');\r\n * ```\r\n */\r\n getHttpClient(): HttpClient {\r\n return this.http;\r\n }\r\n\r\n /**\r\n * Get the discovered backend capabilities\r\n */\r\n getCapabilities(): BackendCapabilities | null {\r\n return this.capabilities;\r\n }\r\n\r\n /**\r\n * Get the current storage strategy identifier\r\n */\r\n getStorageStrategy(): string {\r\n return this.tokenManager.getStrategyId();\r\n }\r\n\r\n /**\r\n * Check if the client has been fully initialized\r\n */\r\n isInitialized(): boolean {\r\n return this.initialized;\r\n }\r\n}\r\n","/**\r\n * @insforge/sdk - TypeScript SDK for InsForge Backend-as-a-Service\r\n * \r\n * @packageDocumentation\r\n */\r\n\r\n// Main client\r\nexport { InsForgeClient } from './client';\r\n\r\n// Types\r\nexport type {\r\n InsForgeConfig,\r\n InsForgeConfig as ClientOptions, // Alias for compatibility\r\n TokenStorage,\r\n AuthSession,\r\n ApiError,\r\n} from './types';\r\n\r\nexport { InsForgeError } from './types';\r\n\r\n// Re-export shared schemas that SDK users will need\r\nexport type {\r\n UserSchema,\r\n CreateUserRequest,\r\n CreateSessionRequest,\r\n AuthErrorResponse,\r\n} from '@insforge/shared-schemas';\r\n\r\n// Re-export auth module for advanced usage\r\nexport { Auth } from './modules/auth';\r\n\r\nexport type { ProfileData, UpdateProfileData } from './modules/auth';\r\n\r\n// Re-export database module (using postgrest-js)\r\nexport { Database } from './modules/database-postgrest';\r\n// Note: QueryBuilder is no longer exported as we use postgrest-js QueryBuilder internally\r\n\r\n// Re-export storage module and types\r\nexport { Storage, StorageBucket } from './modules/storage';\r\nexport type { StorageResponse } from './modules/storage';\r\n\r\n// Re-export AI module\r\nexport { AI } from './modules/ai';\r\n\r\n// Re-export Functions module\r\nexport { Functions } from './modules/functions';\r\nexport type { FunctionInvokeOptions } from './modules/functions';\r\n\r\n// Re-export HTTP client for advanced usage\r\nexport { HttpClient } from './lib/http-client';\r\n\r\n// Re-export Token Manager\r\nexport { TokenManager } from './lib/token-manager';\r\n\r\n// Re-export capability discovery utilities\r\nexport { discoverCapabilities, createSessionStorage, getDefaultCapabilities } from './lib/capability-discovery';\r\nexport type { BackendCapabilities } from './lib/capability-discovery';\r\n\r\n// Re-export session storage strategies for advanced usage\r\nexport {\r\n SecureSessionStorage,\r\n PersistentSessionStorage,\r\n} from './lib/session-storage';\r\nexport type { SessionStorageStrategy } from './lib/session-storage';\r\n\r\n// Factory function for creating clients (Supabase-style)\r\nimport { InsForgeClient } from './client';\r\nimport { InsForgeConfig } from './types';\r\n\r\nexport function createClient(config: InsForgeConfig): InsForgeClient {\r\n return new InsForgeClient(config);\r\n}\r\n\r\n// Default export for convenience\r\nexport default InsForgeClient;\r\n"],"mappings":"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","names":["data","error","response"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/lib/http-client.ts","../src/lib/session-storage.ts","../src/lib/token-manager.ts","../src/lib/backend-config.ts","../src/modules/database-postgrest.ts","../src/modules/auth.ts","../src/modules/storage.ts","../src/modules/ai.ts","../src/modules/functions.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["/**\r\n * InsForge SDK Types - only SDK-specific types here\r\n * Use @insforge/shared-schemas directly for API types\r\n */\r\n\r\nimport type { UserSchema } from '@insforge/shared-schemas';\r\n\r\nexport interface InsForgeConfig {\r\n /**\r\n * The base URL of the InsForge backend API\r\n * @default \"http://localhost:7130\"\r\n */\r\n baseUrl?: string;\r\n\r\n /**\r\n * Anonymous API key (optional)\r\n * Used for public/unauthenticated requests when no user token is set\r\n */\r\n anonKey?: string;\r\n\r\n /**\r\n * Edge Function Token (optional)\r\n * Use this when running in edge functions/serverless with a user's JWT token\r\n * This token will be used for all authenticated requests\r\n */\r\n edgeFunctionToken?: string;\r\n\r\n /**\r\n * Custom fetch implementation (useful for Node.js environments)\r\n */\r\n fetch?: typeof fetch;\r\n\r\n /**\r\n * Storage adapter for persisting tokens\r\n */\r\n storage?: TokenStorage;\r\n\r\n /**\r\n * Whether to automatically refresh tokens before they expire\r\n * @default true\r\n */\r\n autoRefreshToken?: boolean;\r\n\r\n /**\r\n * Whether to persist session in storage\r\n * @default true\r\n */\r\n persistSession?: boolean;\r\n\r\n /**\r\n * Custom headers to include with every request\r\n */\r\n headers?: Record<string, string>;\r\n}\r\n\r\nexport interface TokenStorage {\r\n getItem(key: string): string | null | Promise<string | null>;\r\n setItem(key: string, value: string): void | Promise<void>;\r\n removeItem(key: string): void | Promise<void>;\r\n}\r\n\r\nexport interface AuthSession {\r\n user: UserSchema;\r\n accessToken: string;\r\n expiresAt?: Date;\r\n}\r\n\r\nexport interface ApiError {\r\n error: string;\r\n message: string;\r\n statusCode: number;\r\n nextActions?: string;\r\n}\r\n\r\nexport class InsForgeError extends Error {\r\n public statusCode: number;\r\n public error: string;\r\n public nextActions?: string;\r\n\r\n constructor(message: string, statusCode: number, error: string, nextActions?: string) {\r\n super(message);\r\n this.name = 'InsForgeError';\r\n this.statusCode = statusCode;\r\n this.error = error;\r\n this.nextActions = nextActions;\r\n }\r\n\r\n static fromApiError(apiError: ApiError): InsForgeError {\r\n return new InsForgeError(\r\n apiError.message,\r\n apiError.statusCode,\r\n apiError.error,\r\n apiError.nextActions\r\n );\r\n }\r\n}","import { InsForgeConfig, ApiError, InsForgeError } from '../types';\r\n\r\nexport interface RequestOptions extends RequestInit {\r\n params?: Record<string, string>;\r\n}\r\n\r\n/**\r\n * Callback type for token refresh\r\n * Returns new access token or null if refresh failed\r\n */\r\nexport type RefreshCallback = () => Promise<string | null>;\r\n\r\nexport class HttpClient {\r\n public readonly baseUrl: string;\r\n public readonly fetch: typeof fetch;\r\n private defaultHeaders: Record<string, string>;\r\n private anonKey: string | undefined;\r\n private userToken: string | null = null;\r\n \r\n // Auto-refresh support\r\n private refreshCallback?: RefreshCallback;\r\n private isRefreshing = false;\r\n private refreshQueue: Array<{\r\n resolve: (token: string) => void;\r\n reject: (error: Error) => void;\r\n }> = [];\r\n\r\n constructor(config: InsForgeConfig) {\r\n this.baseUrl = config.baseUrl || 'http://localhost:7130';\r\n // Properly bind fetch to maintain its context\r\n this.fetch = config.fetch || (globalThis.fetch ? globalThis.fetch.bind(globalThis) : undefined as any);\r\n this.anonKey = config.anonKey;\r\n this.defaultHeaders = {\r\n ...config.headers,\r\n };\r\n\r\n if (!this.fetch) {\r\n throw new Error(\r\n 'Fetch is not available. Please provide a fetch implementation in the config.'\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Set the refresh callback for automatic token refresh on 401\r\n */\r\n setRefreshCallback(callback: RefreshCallback): void {\r\n this.refreshCallback = callback;\r\n }\r\n\r\n private buildUrl(path: string, params?: Record<string, string>): string {\r\n const url = new URL(path, this.baseUrl);\r\n if (params) {\r\n Object.entries(params).forEach(([key, value]) => {\r\n // For select parameter, preserve the exact formatting by normalizing whitespace\r\n // This ensures PostgREST relationship queries work correctly\r\n if (key === 'select') {\r\n // Normalize multiline select strings for PostgREST:\r\n // 1. Replace all whitespace (including newlines) with single space\r\n // 2. Remove spaces inside parentheses for proper PostgREST syntax\r\n // 3. Keep spaces after commas at the top level for readability\r\n let normalizedValue = value.replace(/\\s+/g, ' ').trim();\r\n \r\n // Fix spaces around parentheses and inside them\r\n normalizedValue = normalizedValue\r\n .replace(/\\s*\\(\\s*/g, '(') // Remove spaces around opening parens\r\n .replace(/\\s*\\)\\s*/g, ')') // Remove spaces around closing parens\r\n .replace(/\\(\\s+/g, '(') // Remove spaces after opening parens\r\n .replace(/\\s+\\)/g, ')') // Remove spaces before closing parens\r\n .replace(/,\\s+(?=[^()]*\\))/g, ','); // Remove spaces after commas inside parens\r\n \r\n url.searchParams.append(key, normalizedValue);\r\n } else {\r\n url.searchParams.append(key, value);\r\n }\r\n });\r\n }\r\n return url.toString();\r\n }\r\n\r\n async request<T>(\r\n method: string,\r\n path: string,\r\n options: RequestOptions = {}\r\n ): Promise<T> {\r\n return this.performRequest<T>(method, path, options, false);\r\n }\r\n\r\n private async performRequest<T>(\r\n method: string,\r\n path: string,\r\n options: RequestOptions = {},\r\n isRetry = false\r\n ): Promise<T> {\r\n const { params, headers = {}, body, ...fetchOptions } = options;\r\n \r\n const url = this.buildUrl(path, params);\r\n \r\n const requestHeaders: Record<string, string> = {\r\n ...this.defaultHeaders,\r\n };\r\n \r\n // Set Authorization header: prefer user token, fallback to anon key\r\n const authToken = this.userToken || this.anonKey;\r\n if (authToken) {\r\n requestHeaders['Authorization'] = `Bearer ${authToken}`;\r\n }\r\n \r\n // Handle body serialization\r\n let processedBody: any;\r\n if (body !== undefined) {\r\n // Check if body is FormData (for file uploads)\r\n if (typeof FormData !== 'undefined' && body instanceof FormData) {\r\n // Don't set Content-Type for FormData, let browser set it with boundary\r\n processedBody = body;\r\n } else {\r\n // JSON body\r\n if (method !== 'GET') {\r\n requestHeaders['Content-Type'] = 'application/json;charset=UTF-8';\r\n }\r\n processedBody = JSON.stringify(body);\r\n }\r\n }\r\n \r\n Object.assign(requestHeaders, headers);\r\n \r\n const response = await this.fetch(url, {\r\n method,\r\n headers: requestHeaders,\r\n body: processedBody,\r\n credentials: 'include', // Essential for httpOnly cookies (refresh token)\r\n ...fetchOptions,\r\n });\r\n\r\n // Handle 401 with automatic refresh (only if we have a refresh callback and this isn't already a retry)\r\n if (response.status === 401 && !isRetry && this.refreshCallback) {\r\n const newToken = await this.handleTokenRefresh();\r\n if (newToken) {\r\n this.setAuthToken(newToken);\r\n return this.performRequest<T>(method, path, options, true);\r\n }\r\n }\r\n\r\n // Handle 204 No Content\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n // Try to parse JSON response\r\n let data: any;\r\n const contentType = response.headers.get('content-type');\r\n // Check for any JSON content type (including PostgREST's vnd.pgrst.object+json)\r\n if (contentType?.includes('json')) {\r\n data = await response.json();\r\n } else {\r\n // For non-JSON responses, return text\r\n data = await response.text();\r\n }\r\n\r\n // Handle errors\r\n if (!response.ok) {\r\n if (data && typeof data === 'object' && 'error' in data) {\r\n // Add the HTTP status code if not already in the data\r\n if (!data.statusCode && !data.status) {\r\n data.statusCode = response.status;\r\n }\r\n const error = InsForgeError.fromApiError(data as ApiError);\r\n // Preserve all additional fields from the error response\r\n Object.keys(data).forEach(key => {\r\n if (key !== 'error' && key !== 'message' && key !== 'statusCode') {\r\n (error as any)[key] = data[key];\r\n }\r\n });\r\n throw error;\r\n }\r\n throw new InsForgeError(\r\n `Request failed: ${response.statusText}`,\r\n response.status,\r\n 'REQUEST_FAILED'\r\n );\r\n }\r\n\r\n return data as T;\r\n }\r\n\r\n /**\r\n * Handle token refresh with queue to prevent duplicate refreshes\r\n * Multiple concurrent 401s will wait for a single refresh to complete\r\n */\r\n private async handleTokenRefresh(): Promise<string | null> {\r\n // If already refreshing, queue this request\r\n if (this.isRefreshing) {\r\n return new Promise((resolve, reject) => {\r\n this.refreshQueue.push({ resolve, reject });\r\n });\r\n }\r\n\r\n this.isRefreshing = true;\r\n\r\n try {\r\n const newToken = await this.refreshCallback!();\r\n \r\n // Resolve all queued requests with the new token (or null if refresh failed)\r\n this.refreshQueue.forEach(({ resolve, reject }) => {\r\n if (newToken) {\r\n resolve(newToken);\r\n } else {\r\n reject(new Error('Token refresh failed'));\r\n }\r\n });\r\n this.refreshQueue = [];\r\n \r\n return newToken;\r\n } catch (error) {\r\n // Reject all queued requests\r\n this.refreshQueue.forEach(({ reject }) => {\r\n reject(error instanceof Error ? error : new Error('Token refresh failed'));\r\n });\r\n this.refreshQueue = [];\r\n \r\n return null;\r\n } finally {\r\n this.isRefreshing = false;\r\n }\r\n }\r\n\r\n get<T>(path: string, options?: RequestOptions): Promise<T> {\r\n return this.request<T>('GET', path, options);\r\n }\r\n\r\n post<T>(path: string, body?: any, options?: RequestOptions): Promise<T> {\r\n return this.request<T>('POST', path, { ...options, body });\r\n }\r\n\r\n put<T>(path: string, body?: any, options?: RequestOptions): Promise<T> {\r\n return this.request<T>('PUT', path, { ...options, body });\r\n }\r\n\r\n patch<T>(path: string, body?: any, options?: RequestOptions): Promise<T> {\r\n return this.request<T>('PATCH', path, { ...options, body });\r\n }\r\n\r\n delete<T>(path: string, options?: RequestOptions): Promise<T> {\r\n return this.request<T>('DELETE', path, options);\r\n }\r\n\r\n setAuthToken(token: string | null) {\r\n this.userToken = token;\r\n }\r\n\r\n getHeaders(): Record<string, string> {\r\n const headers = { ...this.defaultHeaders };\r\n \r\n // Include Authorization header if token is available (same logic as request method)\r\n const authToken = this.userToken || this.anonKey;\r\n if (authToken) {\r\n headers['Authorization'] = `Bearer ${authToken}`;\r\n }\r\n \r\n return headers;\r\n }\r\n}\r\n","/**\n * Session Storage Strategies for InsForge SDK\n * \n * Implements the Strategy Pattern for token storage:\n * - SecureSessionStorage: In-memory tokens + httpOnly cookie refresh (XSS-resistant)\n * - LocalSessionStorage: localStorage-based storage (legacy/fallback)\n */\n\nimport type { UserSchema } from '@insforge/shared-schemas';\nimport type { AuthSession, TokenStorage } from '../types';\n\n// localStorage keys for persistent storage\nconst TOKEN_KEY = 'insforge-auth-token';\nconst USER_KEY = 'insforge-auth-user';\n\n// Cookie name for optimistic refresh flag\nconst AUTH_FLAG_COOKIE = 'isAuthenticated';\n\n/**\n * Strategy interface for session storage\n * All storage implementations must conform to this interface\n */\nexport interface SessionStorageStrategy {\n /** Save complete session (token + user) */\n saveSession(session: AuthSession): void;\n \n /** Get current session */\n getSession(): AuthSession | null;\n \n /** Get access token only */\n getAccessToken(): string | null;\n \n /** Update access token (e.g., after refresh) */\n setAccessToken(token: string): void;\n \n /** Get user data */\n getUser(): UserSchema | null;\n \n /** Update user data */\n setUser(user: UserSchema): void;\n \n /** Clear all session data */\n clearSession(): void;\n \n /** Check if token refresh should be attempted (e.g., on page reload) */\n shouldAttemptRefresh(): boolean;\n \n /** Get strategy identifier for debugging */\n readonly strategyId: string;\n}\n\n/**\n * Secure Session Storage Strategy\n * \n * Stores access token in memory only (cleared on page refresh).\n * Refresh token is stored in httpOnly cookie by the backend.\n * Uses an 'isAuthenticated' flag cookie to detect if refresh should be attempted.\n * \n * Security benefits:\n * - Access token not accessible to XSS attacks (in memory only)\n * - Refresh token completely inaccessible to JavaScript (httpOnly)\n */\nexport class SecureSessionStorage implements SessionStorageStrategy {\n readonly strategyId = 'secure';\n \n private accessToken: string | null = null;\n private user: UserSchema | null = null;\n\n saveSession(session: AuthSession): void {\n this.accessToken = session.accessToken;\n this.user = session.user;\n this.setAuthFlag(true);\n }\n\n getSession(): AuthSession | null {\n if (!this.accessToken) return null;\n return {\n accessToken: this.accessToken,\n user: this.user!,\n };\n }\n\n getAccessToken(): string | null {\n return this.accessToken;\n }\n\n setAccessToken(token: string): void {\n this.accessToken = token;\n }\n\n getUser(): UserSchema | null {\n return this.user;\n }\n\n setUser(user: UserSchema): void {\n this.user = user;\n }\n\n clearSession(): void {\n this.accessToken = null;\n this.user = null;\n this.setAuthFlag(false);\n }\n\n shouldAttemptRefresh(): boolean {\n // Attempt refresh if:\n // 1. No token in memory (page was refreshed)\n // 2. Auth flag cookie exists (user was previously authenticated)\n if (this.accessToken) return false;\n return this.hasAuthFlag();\n }\n\n // --- Private: Auth Flag Cookie Management ---\n\n private setAuthFlag(authenticated: boolean): void {\n if (typeof document === 'undefined') return;\n\n if (authenticated) {\n const maxAge = 7 * 24 * 60 * 60; // 7 days\n document.cookie = `${AUTH_FLAG_COOKIE}=true; path=/; max-age=${maxAge}; SameSite=Lax`;\n } else {\n document.cookie = `${AUTH_FLAG_COOKIE}=; path=/; max-age=0`;\n }\n }\n\n private hasAuthFlag(): boolean {\n if (typeof document === 'undefined') return false;\n return document.cookie.includes(`${AUTH_FLAG_COOKIE}=true`);\n }\n}\n\n/**\n * Local Session Storage Strategy\n * \n * Stores tokens in localStorage for persistence across page reloads.\n * Used for legacy backends or environments where httpOnly cookies aren't available.\n * \n * Note: This approach exposes tokens to XSS attacks. Use SecureSessionStorage\n * when possible.\n */\nexport class LocalSessionStorage implements SessionStorageStrategy {\n readonly strategyId = 'local';\n \n private storage: TokenStorage;\n\n constructor(storage?: TokenStorage) {\n if (storage) {\n this.storage = storage;\n } else if (typeof window !== 'undefined' && window.localStorage) {\n this.storage = window.localStorage;\n } else {\n // Fallback: in-memory storage for Node.js environments\n const store = new Map<string, string>();\n this.storage = {\n getItem: (key: string) => store.get(key) || null,\n setItem: (key: string, value: string) => { store.set(key, value); },\n removeItem: (key: string) => { store.delete(key); },\n };\n }\n }\n\n saveSession(session: AuthSession): void {\n this.storage.setItem(TOKEN_KEY, session.accessToken);\n this.storage.setItem(USER_KEY, JSON.stringify(session.user));\n }\n\n getSession(): AuthSession | null {\n const token = this.storage.getItem(TOKEN_KEY);\n const userStr = this.storage.getItem(USER_KEY);\n\n if (!token || !userStr) return null;\n\n try {\n const user = JSON.parse(userStr as string);\n return { accessToken: token as string, user };\n } catch {\n this.clearSession();\n return null;\n }\n }\n\n getAccessToken(): string | null {\n const token = this.storage.getItem(TOKEN_KEY);\n return typeof token === 'string' ? token : null;\n }\n\n setAccessToken(token: string): void {\n this.storage.setItem(TOKEN_KEY, token);\n }\n\n getUser(): UserSchema | null {\n const userStr = this.storage.getItem(USER_KEY);\n if (!userStr) return null;\n try {\n return JSON.parse(userStr as string);\n } catch {\n return null;\n }\n }\n\n setUser(user: UserSchema): void {\n this.storage.setItem(USER_KEY, JSON.stringify(user));\n }\n\n clearSession(): void {\n this.storage.removeItem(TOKEN_KEY);\n this.storage.removeItem(USER_KEY);\n }\n\n shouldAttemptRefresh(): boolean {\n // In persistent mode, we always have the token in storage\n // No need to refresh on page load\n return false;\n }\n}\n","/**\r\n * Token Manager for InsForge SDK\r\n * \r\n * A thin wrapper that delegates to the underlying SessionStorageStrategy.\r\n * This class maintains backward compatibility while using the Strategy Pattern internally.\r\n */\r\n\r\nimport type { UserSchema } from '@insforge/shared-schemas';\r\nimport type { AuthSession, TokenStorage } from '../types';\r\nimport {\r\n SessionStorageStrategy,\r\n LocalSessionStorage,\r\n} from './session-storage';\r\n\r\n/**\r\n * TokenManager - Manages session storage using the Strategy Pattern\r\n * \r\n * The actual storage implementation is delegated to a SessionStorageStrategy.\r\n * By default, uses LocalSessionStorage until a strategy is explicitly set\r\n * via setStrategy() during client initialization.\r\n */\r\nexport class TokenManager {\r\n private strategy: SessionStorageStrategy;\r\n\r\n /**\r\n * Create a new TokenManager\r\n * @param storage - Optional custom storage adapter (used for initial LocalSessionStorage)\r\n */\r\n constructor(storage?: TokenStorage) {\r\n // Default to persistent storage until capability discovery completes\r\n this.strategy = new LocalSessionStorage(storage);\r\n }\r\n\r\n /**\r\n * Set the storage strategy\r\n * Called after capability discovery to switch to the appropriate strategy\r\n */\r\n setStrategy(strategy: SessionStorageStrategy): void {\r\n // Migrate existing session data if switching strategies\r\n const existingSession = this.strategy.getSession();\r\n const previousId = this.strategy.strategyId;\r\n \r\n this.strategy = strategy;\r\n \r\n // If we had a session and are switching to a different strategy, migrate it\r\n if (existingSession && previousId !== strategy.strategyId) {\r\n strategy.saveSession(existingSession);\r\n }\r\n }\r\n\r\n /**\r\n * Get the current strategy identifier\r\n */\r\n getStrategyId(): string {\r\n return this.strategy.strategyId;\r\n }\r\n\r\n // --- Delegated Methods ---\r\n\r\n /**\r\n * Save session data\r\n */\r\n saveSession(session: AuthSession): void {\r\n this.strategy.saveSession(session);\r\n }\r\n\r\n /**\r\n * Get current session\r\n */\r\n getSession(): AuthSession | null {\r\n return this.strategy.getSession();\r\n }\r\n\r\n /**\r\n * Get access token\r\n */\r\n getAccessToken(): string | null {\r\n return this.strategy.getAccessToken();\r\n }\r\n\r\n /**\r\n * Update access token (e.g., after refresh)\r\n */\r\n setAccessToken(token: string): void {\r\n this.strategy.setAccessToken(token);\r\n }\r\n\r\n /**\r\n * Get user data\r\n */\r\n getUser(): UserSchema | null {\r\n return this.strategy.getUser();\r\n }\r\n\r\n /**\r\n * Update user data\r\n */\r\n setUser(user: UserSchema): void {\r\n this.strategy.setUser(user);\r\n }\r\n\r\n /**\r\n * Clear all session data\r\n */\r\n clearSession(): void {\r\n this.strategy.clearSession();\r\n }\r\n\r\n /**\r\n * Check if token refresh should be attempted\r\n * (e.g., on page reload in secure mode)\r\n */\r\n shouldAttemptRefresh(): boolean {\r\n return this.strategy.shouldAttemptRefresh();\r\n }\r\n}\r\n","/**\n * Backend Configuration for InsForge SDK\n * \n * Fetches backend configuration via the /api/health endpoint\n * and creates appropriate storage strategies based on that configuration.\n */\n\nimport type { TokenStorage } from '../types';\nimport {\n SessionStorageStrategy,\n SecureSessionStorage,\n LocalSessionStorage,\n} from './session-storage';\n\n/**\n * Backend configuration returned from /api/health\n */\nexport interface BackendConfig {\n /** Whether backend supports secure httpOnly cookie storage for refresh tokens */\n secureSessionStorage: boolean;\n /** Whether backend supports token refresh endpoint */\n refreshTokens: boolean;\n}\n\n/**\n * Health endpoint response shape\n */\ninterface HealthResponse {\n status: string;\n version: string;\n service: string;\n timestamp: string;\n config?: BackendConfig;\n}\n\n/**\n * Default configuration for legacy backends\n */\nconst DEFAULT_CONFIG: BackendConfig = {\n secureSessionStorage: false,\n refreshTokens: false,\n};\n\n/**\n * Fetch backend configuration from the /api/health endpoint\n * \n * This is the primary method for determining which features the backend supports.\n * The SDK uses this information to select appropriate storage strategies.\n * \n * @param baseUrl - The backend base URL\n * @param fetchImpl - Optional custom fetch implementation\n * @returns Backend configuration object\n * \n * @example\n * ```typescript\n * const config = await discoverBackendConfig('https://api.example.com');\n * if (config.secureSessionStorage) {\n * // Use secure storage strategy\n * }\n * ```\n */\nexport async function discoverBackendConfig(\n baseUrl: string,\n fetchImpl: typeof fetch = globalThis.fetch\n): Promise<BackendConfig> {\n try {\n const response = await fetchImpl(`${baseUrl}/api/health`, {\n method: 'GET',\n headers: {\n 'Accept': 'application/json',\n },\n });\n\n if (!response.ok) {\n return DEFAULT_CONFIG;\n }\n\n const health: HealthResponse = await response.json();\n\n // If backend returns config, use it\n if (health.config) {\n return health.config;\n }\n\n // Legacy backend without config - use defaults\n return DEFAULT_CONFIG;\n } catch {\n return DEFAULT_CONFIG;\n }\n}\n\n/**\n * Create the appropriate session storage strategy based on backend configuration\n * \n * This is the factory function that implements the Strategy Pattern.\n * It selects the storage implementation based on what the backend supports.\n * \n * @param config - Backend configuration from discoverBackendConfig()\n * @param storage - Optional custom storage adapter (for LocalSessionStorage)\n * @returns Appropriate SessionStorageStrategy implementation\n * \n * @example\n * ```typescript\n * const config = await discoverBackendConfig(baseUrl);\n * const storage = createSessionStorage(config);\n * storage.saveSession({ accessToken: '...', user: {...} });\n * ```\n */\nexport function createSessionStorage(\n config: BackendConfig,\n storage?: TokenStorage\n): SessionStorageStrategy {\n // Use secure storage when backend supports both httpOnly cookies and refresh\n if (config.secureSessionStorage && config.refreshTokens) {\n return new SecureSessionStorage();\n }\n\n // Fallback to persistent (localStorage) storage\n return new LocalSessionStorage(storage);\n}\n\n/**\n * Get default backend configuration (useful for testing or manual override)\n */\nexport function getDefaultBackendConfig(): BackendConfig {\n return { ...DEFAULT_CONFIG };\n}\n","/**\r\n * Database module using @supabase/postgrest-js\r\n * Complete replacement for custom QueryBuilder with full PostgREST features\r\n */\r\n\r\nimport { PostgrestClient } from '@supabase/postgrest-js';\r\nimport { HttpClient } from '../lib/http-client';\r\nimport { TokenManager } from '../lib/token-manager';\r\n\r\n\r\n/**\r\n * Custom fetch that transforms URLs and adds auth\r\n */\r\nfunction createInsForgePostgrestFetch(\r\n httpClient: HttpClient,\r\n tokenManager: TokenManager\r\n): typeof fetch {\r\n return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\r\n const url = typeof input === 'string' ? input : input.toString();\r\n const urlObj = new URL(url);\r\n \r\n // Extract table name from pathname\r\n // postgrest-js sends: http://dummy/tablename?params\r\n // We need: http://localhost:7130/api/database/records/tablename?params\r\n const tableName = urlObj.pathname.slice(1); // Remove leading /\r\n \r\n // Build InsForge URL\r\n const insforgeUrl = `${httpClient.baseUrl}/api/database/records/${tableName}${urlObj.search}`;\r\n \r\n // Get auth token from TokenManager or HttpClient\r\n const token = tokenManager.getAccessToken();\r\n const httpHeaders = httpClient.getHeaders();\r\n const authToken = token || httpHeaders['Authorization']?.replace('Bearer ', '');\r\n \r\n // Prepare headers\r\n const headers = new Headers(init?.headers);\r\n if (authToken && !headers.has('Authorization')) {\r\n headers.set('Authorization', `Bearer ${authToken}`);\r\n }\r\n \r\n // Make the actual request using native fetch\r\n const response = await fetch(insforgeUrl, {\r\n ...init,\r\n headers\r\n });\r\n \r\n return response;\r\n };\r\n}\r\n\r\n/**\r\n * Database client using postgrest-js\r\n * Drop-in replacement with FULL PostgREST capabilities\r\n */\r\nexport class Database {\r\n private postgrest: PostgrestClient<any, any, any>;\r\n \r\n constructor(httpClient: HttpClient, tokenManager: TokenManager) {\r\n // Create postgrest client with custom fetch\r\n this.postgrest = new PostgrestClient<any, any, any>('http://dummy', {\r\n fetch: createInsForgePostgrestFetch(httpClient, tokenManager),\r\n headers: {}\r\n });\r\n }\r\n \r\n /**\r\n * Create a query builder for a table\r\n * \r\n * @example\r\n * // Basic query\r\n * const { data, error } = await client.database\r\n * .from('posts')\r\n * .select('*')\r\n * .eq('user_id', userId);\r\n * \r\n * // With count (Supabase style!)\r\n * const { data, error, count } = await client.database\r\n * .from('posts')\r\n * .select('*', { count: 'exact' })\r\n * .range(0, 9);\r\n * \r\n * // Just get count, no data\r\n * const { count } = await client.database\r\n * .from('posts')\r\n * .select('*', { count: 'exact', head: true });\r\n * \r\n * // Complex queries with OR\r\n * const { data } = await client.database\r\n * .from('posts')\r\n * .select('*, users!inner(*)')\r\n * .or('status.eq.active,status.eq.pending');\r\n * \r\n * // All features work:\r\n * - Nested selects\r\n * - Foreign key expansion \r\n * - OR/AND/NOT conditions\r\n * - Count with head\r\n * - Range pagination\r\n * - Upserts\r\n */\r\n from(table: string) {\r\n // Return postgrest query builder with all features\r\n return this.postgrest.from(table);\r\n }\r\n}","/**\r\n * Auth module for InsForge SDK\r\n * Uses shared schemas for type safety\r\n */\r\n\r\nimport { HttpClient } from '../lib/http-client';\r\nimport { TokenManager } from '../lib/token-manager';\r\nimport { AuthSession, InsForgeError } from '../types';\r\nimport { Database } from './database-postgrest';\r\n\r\nimport type {\r\n CreateUserRequest,\r\n CreateUserResponse,\r\n CreateSessionRequest,\r\n CreateSessionResponse,\r\n GetCurrentSessionResponse,\r\n GetOauthUrlResponse,\r\n GetPublicAuthConfigResponse,\r\n OAuthProvidersSchema,\r\n UserIdSchema,\r\n EmailSchema,\r\n RoleSchema,\r\n SendVerificationEmailRequest,\r\n SendResetPasswordEmailRequest,\r\n ExchangeResetPasswordTokenRequest,\r\n VerifyEmailRequest,\r\n} from '@insforge/shared-schemas';\r\n\r\n/**\r\n * Dynamic profile type - represents flexible profile data from database\r\n * Fields can vary based on database schema configuration.\r\n * All fields are converted from snake_case (database) to camelCase (API)\r\n */\r\nexport type ProfileData = Record<string, any> & {\r\n id: string; // User ID (required)\r\n createdAt?: string; // PostgreSQL TIMESTAMPTZ\r\n updatedAt?: string; // PostgreSQL TIMESTAMPTZ\r\n};\r\n\r\n/**\r\n * Dynamic profile update type - for updating profile fields\r\n * Supports any fields that exist in the profile table\r\n */\r\nexport type UpdateProfileData = Partial<Record<string, any>>;\r\n\r\n/**\r\n * Convert database profile to include both snake_case and camelCase formats\r\n * Handles dynamic fields flexibly - automatically converts all snake_case keys to camelCase\r\n * \r\n * NOTE: Backward compatibility for <= v0.0.57\r\n * Both formats are returned to maintain compatibility with existing code.\r\n * For example: both created_at and createdAt are included in the result.\r\n */\r\nfunction convertDbProfileToCamelCase(dbProfile: Record<string, any>): ProfileData {\r\n const result: ProfileData = {\r\n id: dbProfile.id,\r\n };\r\n\r\n // Convert all fields - keep both snake_case and camelCase for backward compatibility (<= v0.0.57)\r\n Object.keys(dbProfile).forEach(key => {\r\n\r\n // Keep original field (snake_case) for backward compatibility (<= v0.0.57)\r\n result[key] = dbProfile[key];\r\n\r\n // Also add camelCase version if field contains underscore\r\n // e.g., created_at -> createdAt, avatar_url -> avatarUrl, etc.\r\n if (key.includes('_')) {\r\n const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\r\n result[camelKey] = dbProfile[key];\r\n }\r\n });\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Convert camelCase profile data to database format (snake_case)\r\n * Handles dynamic fields flexibly - automatically converts all camelCase keys to snake_case\r\n */\r\nfunction convertCamelCaseToDbProfile(profile: UpdateProfileData): Record<string, any> {\r\n const dbProfile: Record<string, any> = {};\r\n\r\n Object.keys(profile).forEach(key => {\r\n if (profile[key] === undefined) return;\r\n\r\n // Convert camelCase to snake_case\r\n // e.g., avatarUrl -> avatar_url, firstName -> first_name\r\n const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);\r\n dbProfile[snakeKey] = profile[key];\r\n });\r\n\r\n return dbProfile;\r\n}\r\n\r\n/**\r\n * Check if current environment is a hosted auth environment\r\n * Returns true for:\r\n * - localhost with port 7130 (hosted auth app dev)\r\n * - https://*.insforge.app (hosted auth app production)\r\n */\r\nfunction isHostedAuthEnvironment(): boolean {\r\n if (typeof window === 'undefined') {\r\n return false;\r\n }\r\n\r\n const { hostname, port, protocol } = window.location;\r\n\r\n // Check for localhost:7130\r\n if (hostname === 'localhost' && port === '7130') {\r\n return true;\r\n }\r\n\r\n // Check for https://*.insforge.app\r\n if (protocol === 'https:' && hostname.endsWith('.insforge.app')) {\r\n return true;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Auth state change event types\r\n * Following Supabase pattern for consistency\r\n */\r\nexport type AuthStateChangeEvent = \r\n | 'INITIAL_SESSION' // Sent to each subscriber after initialization (with or without session)\r\n | 'SIGNED_IN' // User signed in (login, OAuth callback, email verification)\r\n | 'SIGNED_OUT' // User signed out\r\n | 'TOKEN_REFRESHED'; // Access token was refreshed\r\n\r\n/**\r\n * Auth state change callback type\r\n */\r\nexport type AuthStateChangeCallback = (\r\n event: AuthStateChangeEvent,\r\n session: AuthSession | null\r\n) => void;\r\n\r\nexport class Auth {\r\n private database: Database;\r\n private authStateListeners: Set<AuthStateChangeCallback> = new Set();\r\n private initializePromise: Promise<void>;\r\n\r\n constructor(\r\n private http: HttpClient,\r\n private tokenManager: TokenManager,\r\n initializePromise?: Promise<void>\r\n ) {\r\n this.database = new Database(http, tokenManager);\r\n // Default to resolved promise if not provided (for backward compatibility)\r\n this.initializePromise = initializePromise ?? Promise.resolve();\r\n }\r\n\r\n /**\r\n * Subscribe to auth state changes\r\n * \r\n * New subscribers will receive an INITIAL_SESSION event after initialization completes.\r\n * This ensures no race condition where subscribers miss the initial state.\r\n * \r\n * @param callback - Function called when auth state changes\r\n * @returns Unsubscribe function\r\n * \r\n * @example\r\n * ```typescript\r\n * const { data: { subscription } } = client.auth.onAuthStateChange((event, session) => {\r\n * if (event === 'SIGNED_IN') {\r\n * console.log('User signed in:', session?.user.email);\r\n * } else if (event === 'SIGNED_OUT') {\r\n * console.log('User signed out');\r\n * }\r\n * });\r\n * \r\n * // Later: unsubscribe\r\n * subscription.unsubscribe();\r\n * ```\r\n */\r\n onAuthStateChange(callback: AuthStateChangeCallback): {\r\n data: { subscription: { unsubscribe: () => void } };\r\n } {\r\n this.authStateListeners.add(callback);\r\n \r\n // After initialization completes, send initial session state to this specific subscriber\r\n // This handles the race condition where subscriber registers after initialization\r\n // Following Supabase pattern: each subscriber gets their own INITIAL_SESSION\r\n ;(async () => {\r\n await this.initializePromise;\r\n \r\n // Check if still subscribed (might have unsubscribed during await)\r\n if (this.authStateListeners.has(callback)) {\r\n const session = this.tokenManager.getSession();\r\n try {\r\n // Send INITIAL_SESSION (Supabase always sends this event type for initial state)\r\n callback('INITIAL_SESSION', session);\r\n } catch (error) {\r\n console.error('[Auth] Error in auth state change listener:', error);\r\n }\r\n }\r\n })();\r\n \r\n return {\r\n data: {\r\n subscription: {\r\n unsubscribe: () => {\r\n this.authStateListeners.delete(callback);\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n /**\r\n * Emit auth state change to all listeners\r\n * @internal\r\n */\r\n _emitAuthStateChange(event: AuthStateChangeEvent, session: AuthSession | null): void {\r\n this.authStateListeners.forEach(callback => {\r\n try {\r\n callback(event, session);\r\n } catch (error) {\r\n console.error('[Auth] Error in auth state change listener:', error);\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Check if an error represents an authentication failure\r\n * Used to determine appropriate HTTP status code (401 vs 500)\r\n */\r\n private isAuthenticationError(error: unknown): boolean {\r\n if (error instanceof Error) {\r\n const message = error.message.toLowerCase();\r\n const authKeywords = [\r\n 'unauthorized',\r\n 'invalid token',\r\n 'expired token',\r\n 'token expired',\r\n 'invalid refresh token',\r\n 'refresh token',\r\n 'authentication',\r\n 'not authenticated',\r\n 'session expired',\r\n ];\r\n return authKeywords.some(keyword => message.includes(keyword));\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Detect and handle OAuth callback parameters in the URL.\r\n * Called by client after initialization.\r\n */\r\n detectAuthCallback(): void {\r\n // Only run in browser environment\r\n if (typeof window === 'undefined') return;\r\n\r\n try {\r\n const params = new URLSearchParams(window.location.search);\r\n\r\n // Backend returns: access_token, user_id, email, name (optional)\r\n const accessToken = params.get('access_token');\r\n const userId = params.get('user_id');\r\n const email = params.get('email');\r\n const name = params.get('name');\r\n\r\n // Check if we have OAuth callback parameters\r\n if (accessToken && userId && email) {\r\n // Create session with the data from backend\r\n const session: AuthSession = {\r\n accessToken,\r\n user: {\r\n id: userId,\r\n email: email,\r\n name: name || '',\r\n // These fields are not provided by backend OAuth callback\r\n // They'll be populated when calling getCurrentUser()\r\n emailVerified: false,\r\n createdAt: new Date().toISOString(),\r\n updatedAt: new Date().toISOString(),\r\n } as any,\r\n };\r\n\r\n // Save session and set auth token\r\n this.tokenManager.saveSession(session);\r\n this.http.setAuthToken(accessToken);\r\n\r\n // Clean up the URL to remove sensitive parameters\r\n const url = new URL(window.location.href);\r\n url.searchParams.delete('access_token');\r\n url.searchParams.delete('user_id');\r\n url.searchParams.delete('email');\r\n url.searchParams.delete('name');\r\n\r\n // Also handle error case from backend (line 581)\r\n if (params.has('error')) {\r\n url.searchParams.delete('error');\r\n }\r\n\r\n // Replace URL without adding to browser history\r\n window.history.replaceState({}, document.title, url.toString());\r\n \r\n // Emit auth state change\r\n this._emitAuthStateChange('SIGNED_IN', session);\r\n }\r\n } catch {\r\n // Silently continue - don't break initialization\r\n }\r\n }\r\n\r\n /**\r\n * Sign up a new user\r\n */\r\n async signUp(request: CreateUserRequest): Promise<{\r\n data: CreateUserResponse | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<CreateUserResponse>('/api/auth/users', request);\r\n\r\n // Save session internally only if both accessToken and user exist\r\n if (response.accessToken && response.user) {\r\n const session: AuthSession = {\r\n accessToken: response.accessToken,\r\n user: response.user,\r\n };\r\n if (!isHostedAuthEnvironment()) {\r\n this.tokenManager.saveSession(session);\r\n }\r\n this.http.setAuthToken(response.accessToken);\r\n \r\n // Emit auth state change\r\n this._emitAuthStateChange('SIGNED_IN', session);\r\n }\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n error instanceof Error ? error.message : 'An unexpected error occurred during sign up',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Sign in with email and password\r\n */\r\n async signInWithPassword(request: CreateSessionRequest): Promise<{\r\n data: CreateSessionResponse | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<CreateSessionResponse>('/api/auth/sessions', request);\r\n\r\n // Save session internally\r\n const session: AuthSession = {\r\n accessToken: response.accessToken || '',\r\n user: response.user || {\r\n id: '',\r\n email: '',\r\n name: '',\r\n emailVerified: false,\r\n createdAt: '',\r\n updatedAt: '',\r\n },\r\n };\r\n\r\n if (!isHostedAuthEnvironment()) {\r\n this.tokenManager.saveSession(session);\r\n }\r\n this.http.setAuthToken(response.accessToken || '');\r\n \r\n // Emit auth state change\r\n this._emitAuthStateChange('SIGNED_IN', session);\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred during sign in',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Sign in with OAuth provider\r\n */\r\n async signInWithOAuth(options: {\r\n provider: OAuthProvidersSchema;\r\n redirectTo?: string;\r\n skipBrowserRedirect?: boolean;\r\n }): Promise<{\r\n data: { url?: string; provider?: string };\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const { provider, redirectTo, skipBrowserRedirect } = options;\r\n\r\n const params = redirectTo\r\n ? { redirect_uri: redirectTo }\r\n : undefined;\r\n\r\n const endpoint = `/api/auth/oauth/${provider}`;\r\n const response = await this.http.get<GetOauthUrlResponse>(endpoint, { params });\r\n\r\n // Automatically redirect in browser unless told not to\r\n if (typeof window !== 'undefined' && !skipBrowserRedirect) {\r\n window.location.href = response.authUrl;\r\n return { data: {}, error: null };\r\n }\r\n\r\n return {\r\n data: {\r\n url: response.authUrl,\r\n provider\r\n },\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: {}, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: {},\r\n error: new InsForgeError(\r\n 'An unexpected error occurred during OAuth initialization',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Sign out the current user\r\n * In modern mode, also calls backend to clear the refresh token cookie\r\n */\r\n async signOut(): Promise<{ error: InsForgeError | null }> {\r\n try {\r\n // If using secure storage, call backend to clear refresh token cookie\r\n if (this.tokenManager.getStrategyId() === 'secure') {\r\n try {\r\n await this.http.post('/api/auth/logout');\r\n } catch {\r\n // Ignore errors from logout endpoint - still clear local session\r\n }\r\n }\r\n\r\n this.tokenManager.clearSession();\r\n this.http.setAuthToken(null);\r\n \r\n // Emit auth state change\r\n this._emitAuthStateChange('SIGNED_OUT', null);\r\n \r\n return { error: null };\r\n } catch (error) {\r\n return {\r\n error: new InsForgeError(\r\n 'Failed to sign out',\r\n 500,\r\n 'SIGNOUT_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Refresh the access token using the httpOnly refresh token cookie\r\n * Only works when backend supports secure session storage (httpOnly cookies)\r\n * \r\n * @returns New access token or throws an error\r\n */\r\n async refreshToken(): Promise<string> {\r\n try {\r\n const response = await this.http.post<{ accessToken: string; user?: any }>(\r\n '/api/auth/refresh'\r\n );\r\n\r\n if (response.accessToken) {\r\n // Update token manager with new token\r\n this.tokenManager.setAccessToken(response.accessToken);\r\n this.http.setAuthToken(response.accessToken);\r\n\r\n // Update user data if provided\r\n if (response.user) {\r\n this.tokenManager.setUser(response.user);\r\n }\r\n\r\n // Emit auth state change with updated session\r\n const session = this.tokenManager.getSession();\r\n this._emitAuthStateChange('TOKEN_REFRESHED', session);\r\n\r\n return response.accessToken;\r\n }\r\n\r\n throw new InsForgeError(\r\n 'No access token in refresh response',\r\n 500,\r\n 'REFRESH_FAILED'\r\n );\r\n } catch (error) {\r\n if (error instanceof InsForgeError) {\r\n // Only clear session on auth-related errors\r\n if (error.statusCode === 401 || error.statusCode === 403) {\r\n this.tokenManager.clearSession();\r\n this.http.setAuthToken(null);\r\n }\r\n throw error;\r\n }\r\n\r\n // Determine if this is an auth error or network/unknown error\r\n const errorMessage = error instanceof Error ? error.message : 'Token refresh failed';\r\n const isAuthError = this.isAuthenticationError(error);\r\n \r\n // Clear session only for auth errors\r\n if (isAuthError) {\r\n this.tokenManager.clearSession();\r\n this.http.setAuthToken(null);\r\n }\r\n\r\n throw new InsForgeError(\r\n errorMessage,\r\n isAuthError ? 401 : 500,\r\n 'REFRESH_FAILED'\r\n );\r\n }\r\n }\r\n\r\n\r\n /**\r\n * Get all public authentication configuration (OAuth + Email)\r\n * Returns both OAuth providers and email authentication settings in one request\r\n * This is a public endpoint that doesn't require authentication\r\n * \r\n * @returns Complete public authentication configuration including OAuth providers and email auth settings\r\n * \r\n * @example\r\n * ```ts\r\n * const { data, error } = await insforge.auth.getPublicAuthConfig();\r\n * if (data) {\r\n * console.log(`OAuth providers: ${data.oauth.data.length}`);\r\n * console.log(`Password min length: ${data.email.passwordMinLength}`);\r\n * }\r\n * ```\r\n */\r\n async getPublicAuthConfig(): Promise<{\r\n data: GetPublicAuthConfigResponse | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.get<GetPublicAuthConfigResponse>('/api/auth/public-config');\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while fetching public authentication configuration',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n\r\n /**\r\n * Get the current user with full profile information\r\n * Returns both auth info (id, email, role) and profile data (dynamic fields from users table)\r\n */\r\n async getCurrentUser(): Promise<{\r\n data: {\r\n user: {\r\n id: UserIdSchema;\r\n email: EmailSchema;\r\n role: RoleSchema;\r\n };\r\n profile: ProfileData | null;\r\n } | null;\r\n error: any | null;\r\n }> {\r\n try {\r\n // Check if we have a token\r\n const session = this.tokenManager.getSession();\r\n if (!session?.accessToken) {\r\n return { data: null, error: null };\r\n }\r\n\r\n // Call the API for auth info\r\n this.http.setAuthToken(session.accessToken);\r\n const authResponse = await this.http.get<GetCurrentSessionResponse>('/api/auth/sessions/current');\r\n\r\n // Get the user's profile using query builder\r\n const { data: profile, error: profileError } = await this.database\r\n .from('users')\r\n .select('*')\r\n .eq('id', authResponse.user.id)\r\n .single();\r\n\r\n // For database errors, return PostgrestError directly\r\n if (profileError && (profileError as any).code !== 'PGRST116') { // PGRST116 = not found\r\n return { data: null, error: profileError };\r\n }\r\n\r\n return {\r\n data: {\r\n user: authResponse.user,\r\n profile: profile ? convertDbProfileToCamelCase(profile) : null\r\n },\r\n error: null\r\n };\r\n } catch (error) {\r\n // If unauthorized, clear session\r\n if (error instanceof InsForgeError && error.statusCode === 401) {\r\n await this.signOut();\r\n return { data: null, error: null };\r\n }\r\n\r\n // Pass through all other errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while fetching user',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get any user's profile by ID\r\n * Returns profile information from the users table (dynamic fields)\r\n */\r\n async getProfile(userId: string): Promise<{\r\n data: ProfileData | null;\r\n error: any | null;\r\n }> {\r\n const { data, error } = await this.database\r\n .from('users')\r\n .select('*')\r\n .eq('id', userId)\r\n .single();\r\n\r\n // Handle not found as null, not error\r\n if (error && (error as any).code === 'PGRST116') {\r\n return { data: null, error: null };\r\n }\r\n\r\n // Convert database format to camelCase format\r\n if (data) {\r\n return { data: convertDbProfileToCamelCase(data), error: null };\r\n }\r\n\r\n // Return PostgrestError directly for database operations\r\n return { data: null, error };\r\n }\r\n\r\n /**\r\n * Get the current session (only session data, no API call)\r\n * Returns the stored JWT token and basic user info from local storage\r\n */\r\n getCurrentSession(): {\r\n data: { session: AuthSession | null };\r\n error: InsForgeError | null;\r\n } {\r\n try {\r\n const session = this.tokenManager.getSession();\r\n\r\n if (session?.accessToken) {\r\n this.http.setAuthToken(session.accessToken);\r\n return { data: { session }, error: null };\r\n }\r\n\r\n return { data: { session: null }, error: null };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: { session: null }, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: { session: null },\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while getting session',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Set/Update the current user's profile\r\n * Updates profile information in the users table (supports any dynamic fields)\r\n */\r\n async setProfile(profile: UpdateProfileData): Promise<{\r\n data: ProfileData | null;\r\n error: any | null;\r\n }> {\r\n // Get current session to get user ID\r\n const session = this.tokenManager.getSession();\r\n if (!session?.accessToken) {\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'No authenticated user found',\r\n 401,\r\n 'UNAUTHENTICATED'\r\n )\r\n };\r\n }\r\n\r\n // If no user ID in session (edge function scenario), fetch it\r\n if (!session.user?.id) {\r\n const { data, error } = await this.getCurrentUser();\r\n if (error) {\r\n return { data: null, error };\r\n }\r\n if (data?.user) {\r\n // Update session with minimal user info\r\n session.user = {\r\n id: data.user.id,\r\n email: data.user.email,\r\n name: '',\r\n emailVerified: false,\r\n createdAt: new Date().toISOString(),\r\n updatedAt: new Date().toISOString(),\r\n };\r\n this.tokenManager.saveSession(session);\r\n }\r\n }\r\n\r\n // Convert camelCase format to database format (snake_case)\r\n const dbProfile = convertCamelCaseToDbProfile(profile);\r\n\r\n // Update the profile using query builder\r\n const { data, error } = await this.database\r\n .from('users')\r\n .update(dbProfile)\r\n .eq('id', session.user.id)\r\n .select()\r\n .single();\r\n\r\n // Convert database format back to camelCase format\r\n if (data) {\r\n return { data: convertDbProfileToCamelCase(data), error: null };\r\n }\r\n\r\n // Return PostgrestError directly for database operations\r\n return { data: null, error };\r\n }\r\n\r\n /**\r\n * Send email verification (code or link based on config)\r\n *\r\n * Send email verification using the method configured in auth settings (verifyEmailMethod).\r\n * When method is 'code', sends a 6-digit numeric code. When method is 'link', sends a magic link.\r\n * Prevents user enumeration by returning success even if email doesn't exist.\r\n */\r\n async sendVerificationEmail(request: SendVerificationEmailRequest): Promise<{\r\n data: { success: boolean; message: string } | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<{ success: boolean; message: string }>(\r\n '/api/auth/email/send-verification',\r\n request\r\n );\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while sending verification code',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Send password reset (code or link based on config)\r\n *\r\n * Send password reset email using the method configured in auth settings (resetPasswordMethod).\r\n * When method is 'code', sends a 6-digit numeric code for two-step flow.\r\n * When method is 'link', sends a magic link.\r\n * Prevents user enumeration by returning success even if email doesn't exist.\r\n */\r\n async sendResetPasswordEmail(request: SendResetPasswordEmailRequest): Promise<{\r\n data: { success: boolean; message: string } | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<{ success: boolean; message: string }>(\r\n '/api/auth/email/send-reset-password',\r\n request\r\n );\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while sending password reset code',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Exchange reset password code for reset token\r\n *\r\n * Step 1 of two-step password reset flow (only used when resetPasswordMethod is 'code'):\r\n * 1. Verify the 6-digit code sent to user's email\r\n * 2. Return a reset token that can be used to actually reset the password\r\n *\r\n * This endpoint is not used when resetPasswordMethod is 'link' (magic link flow is direct).\r\n */\r\n async exchangeResetPasswordToken(request: ExchangeResetPasswordTokenRequest): Promise<{\r\n data: { token: string; expiresAt: string } | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<{ token: string; expiresAt: string }>(\r\n '/api/auth/email/exchange-reset-password-token',\r\n request\r\n );\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while verifying reset code',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Reset password with token\r\n *\r\n * Reset user password with a token. The token can be:\r\n * - Magic link token (64-character hex token from send-reset-password when method is 'link')\r\n * - Reset token (from exchange-reset-password-token after code verification when method is 'code')\r\n *\r\n * Both token types use RESET_PASSWORD purpose and are verified the same way.\r\n *\r\n * Flow summary:\r\n * - Code method: send-reset-password → exchange-reset-password-token → reset-password (with resetToken)\r\n * - Link method: send-reset-password → reset-password (with link token directly)\r\n */\r\n async resetPassword(request: { newPassword: string; otp: string }): Promise<{\r\n data: { message: string; redirectTo?: string } | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<{ message: string; redirectTo?: string }>(\r\n '/api/auth/email/reset-password',\r\n request\r\n );\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while resetting password',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Verify email with code or link\r\n *\r\n * Verify email address using the method configured in auth settings (verifyEmailMethod):\r\n * - Code verification: Provide both `email` and `otp` (6-digit numeric code)\r\n * - Link verification: Provide only `otp` (64-character hex token from magic link)\r\n *\r\n * Successfully verified users will receive a session token.\r\n *\r\n * The email verification link sent to users always points to the backend API endpoint.\r\n * If `verifyEmailRedirectTo` is configured, the backend will redirect to that URL after successful verification.\r\n * Otherwise, a default success page is displayed.\r\n */\r\n async verifyEmail(request: VerifyEmailRequest): Promise<{\r\n data: { accessToken: string; user?: any; redirectTo?: string } | null;\r\n error: InsForgeError | null;\r\n }> {\r\n try {\r\n const response = await this.http.post<{ accessToken: string; user?: any; redirectTo?: string }>(\r\n '/api/auth/email/verify',\r\n request\r\n );\r\n\r\n // Save session if we got a token\r\n if (response.accessToken) {\r\n const session: AuthSession = {\r\n accessToken: response.accessToken,\r\n user: response.user || {} as any,\r\n };\r\n this.tokenManager.saveSession(session);\r\n this.http.setAuthToken(response.accessToken);\r\n \r\n // Emit auth state change\r\n this._emitAuthStateChange('SIGNED_IN', session);\r\n }\r\n\r\n return {\r\n data: response,\r\n error: null\r\n };\r\n } catch (error) {\r\n // Pass through API errors unchanged\r\n if (error instanceof InsForgeError) {\r\n return { data: null, error };\r\n }\r\n\r\n // Generic fallback for unexpected errors\r\n return {\r\n data: null,\r\n error: new InsForgeError(\r\n 'An unexpected error occurred while verifying email',\r\n 500,\r\n 'UNEXPECTED_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n}","/**\r\n * Storage module for InsForge SDK\r\n * Handles file uploads, downloads, and bucket management\r\n */\r\n\r\nimport { HttpClient } from '../lib/http-client';\r\nimport { InsForgeError } from '../types';\r\nimport type { \r\n StorageFileSchema,\r\n ListObjectsResponseSchema\r\n} from '@insforge/shared-schemas';\r\n\r\nexport interface StorageResponse<T> {\r\n data: T | null;\r\n error: InsForgeError | null;\r\n}\r\n\r\ninterface UploadStrategy {\r\n method: 'direct' | 'presigned';\r\n uploadUrl: string;\r\n fields?: Record<string, string>;\r\n key: string;\r\n confirmRequired: boolean;\r\n confirmUrl?: string;\r\n expiresAt?: Date;\r\n}\r\n\r\ninterface DownloadStrategy {\r\n method: 'direct' | 'presigned';\r\n url: string;\r\n expiresAt?: Date;\r\n}\r\n\r\n/**\r\n * Storage bucket operations\r\n */\r\nexport class StorageBucket {\r\n constructor(\r\n private bucketName: string,\r\n private http: HttpClient\r\n ) {}\r\n\r\n /**\r\n * Upload a file with a specific key\r\n * Uses the upload strategy from backend (direct or presigned)\r\n * @param path - The object key/path\r\n * @param file - File or Blob to upload\r\n */\r\n async upload(\r\n path: string,\r\n file: File | Blob\r\n ): Promise<StorageResponse<StorageFileSchema>> {\r\n try {\r\n // Get upload strategy from backend - this is required\r\n const strategyResponse = await this.http.post<UploadStrategy>(\r\n `/api/storage/buckets/${this.bucketName}/upload-strategy`,\r\n {\r\n filename: path,\r\n contentType: file.type || 'application/octet-stream',\r\n size: file.size\r\n }\r\n );\r\n\r\n // Use presigned URL if available\r\n if (strategyResponse.method === 'presigned') {\r\n return await this.uploadWithPresignedUrl(strategyResponse, file);\r\n }\r\n\r\n // Use direct upload if strategy says so\r\n if (strategyResponse.method === 'direct') {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n\r\n const response = await this.http.request<StorageFileSchema>(\r\n 'PUT',\r\n `/api/storage/buckets/${this.bucketName}/objects/${encodeURIComponent(path)}`,\r\n {\r\n body: formData as any,\r\n headers: {\r\n // Don't set Content-Type, let browser set multipart boundary\r\n }\r\n }\r\n );\r\n\r\n return { data: response, error: null };\r\n }\r\n\r\n throw new InsForgeError(\r\n `Unsupported upload method: ${strategyResponse.method}`,\r\n 500,\r\n 'STORAGE_ERROR'\r\n );\r\n } catch (error) {\r\n return { \r\n data: null, \r\n error: error instanceof InsForgeError ? error : new InsForgeError(\r\n 'Upload failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Upload a file with auto-generated key\r\n * Uses the upload strategy from backend (direct or presigned)\r\n * @param file - File or Blob to upload\r\n */\r\n async uploadAuto(\r\n file: File | Blob\r\n ): Promise<StorageResponse<StorageFileSchema>> {\r\n try {\r\n const filename = file instanceof File ? file.name : 'file';\r\n \r\n // Get upload strategy from backend - this is required\r\n const strategyResponse = await this.http.post<UploadStrategy>(\r\n `/api/storage/buckets/${this.bucketName}/upload-strategy`,\r\n {\r\n filename,\r\n contentType: file.type || 'application/octet-stream',\r\n size: file.size\r\n }\r\n );\r\n\r\n // Use presigned URL if available\r\n if (strategyResponse.method === 'presigned') {\r\n return await this.uploadWithPresignedUrl(strategyResponse, file);\r\n }\r\n\r\n // Use direct upload if strategy says so\r\n if (strategyResponse.method === 'direct') {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n\r\n const response = await this.http.request<StorageFileSchema>(\r\n 'POST',\r\n `/api/storage/buckets/${this.bucketName}/objects`,\r\n {\r\n body: formData as any,\r\n headers: {\r\n // Don't set Content-Type, let browser set multipart boundary\r\n }\r\n }\r\n );\r\n\r\n return { data: response, error: null };\r\n }\r\n\r\n throw new InsForgeError(\r\n `Unsupported upload method: ${strategyResponse.method}`,\r\n 500,\r\n 'STORAGE_ERROR'\r\n );\r\n } catch (error) {\r\n return { \r\n data: null, \r\n error: error instanceof InsForgeError ? error : new InsForgeError(\r\n 'Upload failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Internal method to handle presigned URL uploads\r\n */\r\n private async uploadWithPresignedUrl(\r\n strategy: UploadStrategy,\r\n file: File | Blob\r\n ): Promise<StorageResponse<StorageFileSchema>> {\r\n try {\r\n // Upload to presigned URL (e.g., S3)\r\n const formData = new FormData();\r\n \r\n // Add all fields from the presigned URL\r\n if (strategy.fields) {\r\n Object.entries(strategy.fields).forEach(([key, value]) => {\r\n formData.append(key, value);\r\n });\r\n }\r\n \r\n // File must be the last field for S3\r\n formData.append('file', file);\r\n\r\n const uploadResponse = await fetch(strategy.uploadUrl, {\r\n method: 'POST',\r\n body: formData\r\n });\r\n\r\n if (!uploadResponse.ok) {\r\n throw new InsForgeError(\r\n `Upload to storage failed: ${uploadResponse.statusText}`,\r\n uploadResponse.status,\r\n 'STORAGE_ERROR'\r\n );\r\n }\r\n\r\n // Confirm upload with backend if required\r\n if (strategy.confirmRequired && strategy.confirmUrl) {\r\n const confirmResponse = await this.http.post<StorageFileSchema>(\r\n strategy.confirmUrl,\r\n {\r\n size: file.size,\r\n contentType: file.type || 'application/octet-stream'\r\n }\r\n );\r\n\r\n return { data: confirmResponse, error: null };\r\n }\r\n\r\n // If no confirmation required, return basic file info\r\n return {\r\n data: {\r\n key: strategy.key,\r\n bucket: this.bucketName,\r\n size: file.size,\r\n mimeType: file.type || 'application/octet-stream',\r\n uploadedAt: new Date().toISOString(),\r\n url: this.getPublicUrl(strategy.key)\r\n } as StorageFileSchema,\r\n error: null\r\n };\r\n } catch (error) {\r\n throw error instanceof InsForgeError ? error : new InsForgeError(\r\n 'Presigned upload failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Download a file\r\n * Uses the download strategy from backend (direct or presigned)\r\n * @param path - The object key/path\r\n * Returns the file as a Blob\r\n */\r\n async download(path: string): Promise<{ data: Blob | null; error: InsForgeError | null }> {\r\n try {\r\n // Get download strategy from backend - this is required\r\n const strategyResponse = await this.http.post<DownloadStrategy>(\r\n `/api/storage/buckets/${this.bucketName}/objects/${encodeURIComponent(path)}/download-strategy`,\r\n { expiresIn: 3600 }\r\n );\r\n\r\n // Use URL from strategy\r\n const downloadUrl = strategyResponse.url;\r\n \r\n // Download from the URL\r\n const headers: HeadersInit = {};\r\n \r\n // Only add auth header for direct downloads (not presigned URLs)\r\n if (strategyResponse.method === 'direct') {\r\n Object.assign(headers, this.http.getHeaders());\r\n }\r\n \r\n const response = await fetch(downloadUrl, {\r\n method: 'GET',\r\n headers\r\n });\r\n\r\n if (!response.ok) {\r\n try {\r\n const error = await response.json();\r\n throw InsForgeError.fromApiError(error);\r\n } catch {\r\n throw new InsForgeError(\r\n `Download failed: ${response.statusText}`,\r\n response.status,\r\n 'STORAGE_ERROR'\r\n );\r\n }\r\n }\r\n\r\n const blob = await response.blob();\r\n return { data: blob, error: null };\r\n } catch (error) {\r\n return { \r\n data: null, \r\n error: error instanceof InsForgeError ? error : new InsForgeError(\r\n 'Download failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get public URL for a file\r\n * @param path - The object key/path\r\n */\r\n getPublicUrl(path: string): string {\r\n return `${this.http.baseUrl}/api/storage/buckets/${this.bucketName}/objects/${encodeURIComponent(path)}`;\r\n }\r\n\r\n /**\r\n * List objects in the bucket\r\n * @param prefix - Filter by key prefix\r\n * @param search - Search in file names\r\n * @param limit - Maximum number of results (default: 100, max: 1000)\r\n * @param offset - Number of results to skip\r\n */\r\n async list(options?: {\r\n prefix?: string;\r\n search?: string;\r\n limit?: number;\r\n offset?: number;\r\n }): Promise<StorageResponse<ListObjectsResponseSchema>> {\r\n try {\r\n const params: Record<string, string> = {};\r\n \r\n if (options?.prefix) params.prefix = options.prefix;\r\n if (options?.search) params.search = options.search;\r\n if (options?.limit) params.limit = options.limit.toString();\r\n if (options?.offset) params.offset = options.offset.toString();\r\n\r\n const response = await this.http.get<ListObjectsResponseSchema>(\r\n `/api/storage/buckets/${this.bucketName}/objects`,\r\n { params }\r\n );\r\n\r\n return { data: response, error: null };\r\n } catch (error) {\r\n return { \r\n data: null, \r\n error: error instanceof InsForgeError ? error : new InsForgeError(\r\n 'List failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Delete a file\r\n * @param path - The object key/path\r\n */\r\n async remove(path: string): Promise<StorageResponse<{ message: string }>> {\r\n try {\r\n const response = await this.http.delete<{ message: string }>(\r\n `/api/storage/buckets/${this.bucketName}/objects/${encodeURIComponent(path)}`\r\n );\r\n\r\n return { data: response, error: null };\r\n } catch (error) {\r\n return { \r\n data: null, \r\n error: error instanceof InsForgeError ? error : new InsForgeError(\r\n 'Delete failed',\r\n 500,\r\n 'STORAGE_ERROR'\r\n )\r\n };\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Storage module for file operations\r\n */\r\nexport class Storage {\r\n constructor(private http: HttpClient) {}\r\n\r\n /**\r\n * Get a bucket instance for operations\r\n * @param bucketName - Name of the bucket\r\n */\r\n from(bucketName: string): StorageBucket {\r\n return new StorageBucket(bucketName, this.http);\r\n }\r\n}","/**\r\n * AI Module for Insforge SDK\r\n * Response format roughly matches OpenAI SDK for compatibility\r\n *\r\n * The backend handles all the complexity of different AI providers\r\n * and returns a unified format. This SDK transforms responses to match OpenAI-like format.\r\n */\r\n\r\nimport { HttpClient } from \"../lib/http-client\";\r\nimport {\r\n ChatCompletionRequest,\r\n ChatCompletionResponse,\r\n ImageGenerationRequest,\r\n ImageGenerationResponse,\r\n} from \"@insforge/shared-schemas\";\r\n\r\nexport class AI {\r\n public readonly chat: Chat;\r\n public readonly images: Images;\r\n\r\n constructor(private http: HttpClient) {\r\n this.chat = new Chat(http);\r\n this.images = new Images(http);\r\n }\r\n}\r\n\r\nclass Chat {\r\n public readonly completions: ChatCompletions;\r\n\r\n constructor(http: HttpClient) {\r\n this.completions = new ChatCompletions(http);\r\n }\r\n}\r\n\r\nclass ChatCompletions {\r\n constructor(private http: HttpClient) {}\r\n\r\n /**\r\n * Create a chat completion - OpenAI-like response format\r\n *\r\n * @example\r\n * ```typescript\r\n * // Non-streaming\r\n * const completion = await client.ai.chat.completions.create({\r\n * model: 'gpt-4',\r\n * messages: [{ role: 'user', content: 'Hello!' }]\r\n * });\r\n * console.log(completion.choices[0].message.content);\r\n *\r\n * // With images\r\n * const response = await client.ai.chat.completions.create({\r\n * model: 'gpt-4-vision',\r\n * messages: [{\r\n * role: 'user',\r\n * content: 'What is in this image?',\r\n * images: [{ url: 'https://example.com/image.jpg' }]\r\n * }]\r\n * });\r\n *\r\n * // Streaming - returns async iterable\r\n * const stream = await client.ai.chat.completions.create({\r\n * model: 'gpt-4',\r\n * messages: [{ role: 'user', content: 'Tell me a story' }],\r\n * stream: true\r\n * });\r\n *\r\n * for await (const chunk of stream) {\r\n * if (chunk.choices[0]?.delta?.content) {\r\n * process.stdout.write(chunk.choices[0].delta.content);\r\n * }\r\n * }\r\n * ```\r\n */\r\n async create(params: ChatCompletionRequest): Promise<any> {\r\n // Backend already expects camelCase, no transformation needed\r\n const backendParams = {\r\n model: params.model,\r\n messages: params.messages,\r\n temperature: params.temperature,\r\n maxTokens: params.maxTokens,\r\n topP: params.topP,\r\n stream: params.stream,\r\n };\r\n\r\n // For streaming, return an async iterable that yields OpenAI-like chunks\r\n if (params.stream) {\r\n const headers = this.http.getHeaders();\r\n headers[\"Content-Type\"] = \"application/json\";\r\n\r\n const response = await this.http.fetch(\r\n `${this.http.baseUrl}/api/ai/chat/completion`,\r\n {\r\n method: \"POST\",\r\n headers,\r\n body: JSON.stringify(backendParams),\r\n }\r\n );\r\n\r\n if (!response.ok) {\r\n const error = await response.json();\r\n throw new Error(error.error || \"Stream request failed\");\r\n }\r\n\r\n // Return async iterable that parses SSE and transforms to OpenAI-like format\r\n return this.parseSSEStream(response, params.model);\r\n }\r\n\r\n // Non-streaming: transform response to OpenAI-like format\r\n const response: ChatCompletionResponse = await this.http.post(\r\n \"/api/ai/chat/completion\",\r\n backendParams\r\n );\r\n\r\n // Transform to OpenAI-like format\r\n const content = response.text || \"\";\r\n\r\n return {\r\n id: `chatcmpl-${Date.now()}`,\r\n object: \"chat.completion\",\r\n created: Math.floor(Date.now() / 1000),\r\n model: response.metadata?.model,\r\n choices: [\r\n {\r\n index: 0,\r\n message: {\r\n role: \"assistant\",\r\n content,\r\n },\r\n finish_reason: \"stop\",\r\n },\r\n ],\r\n usage: response.metadata?.usage || {\r\n prompt_tokens: 0,\r\n completion_tokens: 0,\r\n total_tokens: 0,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Parse SSE stream into async iterable of OpenAI-like chunks\r\n */\r\n private async *parseSSEStream(\r\n response: Response,\r\n model: string\r\n ): AsyncIterableIterator<any> {\r\n const reader = response.body!.getReader();\r\n const decoder = new TextDecoder();\r\n let buffer = \"\";\r\n\r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n buffer += decoder.decode(value, { stream: true });\r\n const lines = buffer.split(\"\\n\");\r\n buffer = lines.pop() || \"\";\r\n\r\n for (const line of lines) {\r\n if (line.startsWith(\"data: \")) {\r\n const dataStr = line.slice(6).trim();\r\n if (dataStr) {\r\n try {\r\n const data = JSON.parse(dataStr);\r\n\r\n // Transform to OpenAI-like streaming format\r\n if (data.chunk || data.content) {\r\n yield {\r\n id: `chatcmpl-${Date.now()}`,\r\n object: \"chat.completion.chunk\",\r\n created: Math.floor(Date.now() / 1000),\r\n model,\r\n choices: [\r\n {\r\n index: 0,\r\n delta: {\r\n content: data.chunk || data.content,\r\n },\r\n finish_reason: data.done ? \"stop\" : null,\r\n },\r\n ],\r\n };\r\n }\r\n\r\n // If we received the done signal, we can stop\r\n if (data.done) {\r\n reader.releaseLock();\r\n return;\r\n }\r\n } catch (e) {\r\n // Skip invalid JSON\r\n console.warn(\"Failed to parse SSE data:\", dataStr);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n}\r\n\r\nclass Images {\r\n constructor(private http: HttpClient) {}\r\n\r\n /**\r\n * Generate images - OpenAI-like response format\r\n *\r\n * @example\r\n * ```typescript\r\n * // Text-to-image\r\n * const response = await client.ai.images.generate({\r\n * model: 'dall-e-3',\r\n * prompt: 'A sunset over mountains',\r\n * });\r\n * console.log(response.images[0].url);\r\n *\r\n * // Image-to-image (with input images)\r\n * const response = await client.ai.images.generate({\r\n * model: 'stable-diffusion-xl',\r\n * prompt: 'Transform this into a watercolor painting',\r\n * images: [\r\n * { url: 'https://example.com/input.jpg' },\r\n * // or base64-encoded Data URI:\r\n * { url: 'data:image/jpeg;base64,/9j/4AAQ...' }\r\n * ]\r\n * });\r\n * ```\r\n */\r\n async generate(params: ImageGenerationRequest): Promise<any> {\r\n const response: ImageGenerationResponse = await this.http.post(\r\n \"/api/ai/image/generation\",\r\n params\r\n );\r\n \r\n // Build data array based on response content\r\n let data: Array<{ b64_json?: string; content?: string }> = [];\r\n \r\n if (response.images && response.images.length > 0) {\r\n // Has images - extract base64 and include text\r\n data = response.images.map(img => ({\r\n b64_json: img.imageUrl.replace(/^data:image\\/\\w+;base64,/, ''),\r\n content: response.text\r\n }));\r\n } else if (response.text) {\r\n // Text-only response\r\n data = [{ content: response.text }];\r\n }\r\n \r\n // Return OpenAI-compatible format\r\n return {\r\n created: Math.floor(Date.now() / 1000),\r\n data,\r\n ...(response.metadata?.usage && {\r\n usage: {\r\n total_tokens: response.metadata.usage.totalTokens || 0,\r\n input_tokens: response.metadata.usage.promptTokens || 0,\r\n output_tokens: response.metadata.usage.completionTokens || 0,\r\n }\r\n })\r\n };\r\n }\r\n}\r\n","import { HttpClient } from '../lib/http-client';\r\n\r\nexport interface FunctionInvokeOptions {\r\n /**\r\n * The body of the request\r\n */\r\n body?: any;\r\n \r\n /**\r\n * Custom headers to send with the request\r\n */\r\n headers?: Record<string, string>;\r\n \r\n /**\r\n * HTTP method (default: POST)\r\n */\r\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\r\n}\r\n\r\n/**\r\n * Edge Functions client for invoking serverless functions\r\n * \r\n * @example\r\n * ```typescript\r\n * // Invoke a function with JSON body\r\n * const { data, error } = await client.functions.invoke('hello-world', {\r\n * body: { name: 'World' }\r\n * });\r\n * \r\n * // GET request\r\n * const { data, error } = await client.functions.invoke('get-data', {\r\n * method: 'GET'\r\n * });\r\n * ```\r\n */\r\nexport class Functions {\r\n private http: HttpClient;\r\n\r\n constructor(http: HttpClient) {\r\n this.http = http;\r\n }\r\n\r\n /**\r\n * Invokes an Edge Function\r\n * @param slug The function slug to invoke\r\n * @param options Request options\r\n */\r\n async invoke<T = any>(\r\n slug: string,\r\n options: FunctionInvokeOptions = {}\r\n ): Promise<{ data: T | null; error: Error | null }> {\r\n try {\r\n const { method = 'POST', body, headers = {} } = options;\r\n \r\n // Simple path: /functions/{slug}\r\n const path = `/functions/${slug}`;\r\n \r\n // Use the HTTP client's request method\r\n const data = await this.http.request<T>(\r\n method,\r\n path,\r\n { body, headers }\r\n );\r\n \r\n return { data, error: null };\r\n } catch (error: any) {\r\n // The HTTP client throws InsForgeError with all properties from the response\r\n // including error, message, details, statusCode, etc.\r\n // We need to preserve all of that information\r\n return { \r\n data: null, \r\n error: error // Pass through the full error object with all properties\r\n };\r\n }\r\n }\r\n}","import { InsForgeConfig } from './types';\r\nimport { HttpClient } from './lib/http-client';\r\nimport { TokenManager } from './lib/token-manager';\r\nimport {\r\n discoverBackendConfig,\r\n createSessionStorage,\r\n BackendConfig,\r\n} from './lib/backend-config';\r\nimport { Auth } from './modules/auth';\r\nimport { Database } from './modules/database-postgrest';\r\nimport { Storage } from './modules/storage';\r\nimport { AI } from './modules/ai';\r\nimport { Functions } from './modules/functions';\r\n\r\n/**\r\n * Main InsForge SDK Client\r\n * \r\n * The client automatically initializes in the background and emits auth state changes.\r\n * Subscribe to `auth.onAuthStateChange` to be notified when initialization completes.\r\n * \r\n * @example\r\n * ```typescript\r\n * import { InsForgeClient } from '@insforge/sdk';\r\n * \r\n * // Create client - synchronous, immediately usable\r\n * const client = new InsForgeClient({\r\n * baseUrl: 'http://localhost:7130'\r\n * });\r\n * \r\n * // Subscribe to auth state changes\r\n * client.auth.onAuthStateChange((event, session) => {\r\n * console.log('Auth event:', event);\r\n * if (session) {\r\n * console.log('User:', session.user.email);\r\n * }\r\n * });\r\n * \r\n * // Client is immediately usable for auth operations\r\n * const { data, error } = await client.auth.signInWithPassword({\r\n * email: 'user@example.com',\r\n * password: 'password123'\r\n * });\r\n * ```\r\n */\r\nexport class InsForgeClient {\r\n private http: HttpClient;\r\n private tokenManager: TokenManager;\r\n private backendConfig: BackendConfig | null = null;\r\n \r\n /**\r\n * Promise that resolves when initialization is complete.\r\n * Use this to ensure operations wait for the client to be ready.\r\n */\r\n private initializePromise: Promise<void>;\r\n private initializeResolve!: () => void;\r\n\r\n public readonly auth: Auth;\r\n public readonly database: Database;\r\n public readonly storage: Storage;\r\n public readonly ai: AI;\r\n public readonly functions: Functions;\r\n\r\n constructor(config: InsForgeConfig = {}) {\r\n // Create initialization promise\r\n this.initializePromise = new Promise((resolve) => {\r\n this.initializeResolve = resolve;\r\n });\r\n\r\n this.http = new HttpClient(config);\r\n this.tokenManager = new TokenManager(config.storage);\r\n\r\n // Create auth module with initializePromise for proper INITIAL_SESSION handling\r\n this.auth = new Auth(this.http, this.tokenManager, this.initializePromise);\r\n\r\n // Check for edge function token (server-side usage)\r\n if (config.edgeFunctionToken) {\r\n this.http.setAuthToken(config.edgeFunctionToken);\r\n // Save to token manager so getCurrentUser() works\r\n this.tokenManager.saveSession({\r\n accessToken: config.edgeFunctionToken,\r\n user: {} as any, // Will be populated by getCurrentUser()\r\n });\r\n }\r\n\r\n // Set up refresh callback for auto-refresh on 401\r\n this.http.setRefreshCallback(async () => {\r\n try {\r\n return await this.auth.refreshToken();\r\n } catch {\r\n return null;\r\n }\r\n });\r\n\r\n // Check for existing session in storage (for initial load)\r\n const existingSession = this.tokenManager.getSession();\r\n if (existingSession?.accessToken) {\r\n this.http.setAuthToken(existingSession.accessToken);\r\n }\r\n\r\n // Initialize other modules\r\n this.database = new Database(this.http, this.tokenManager);\r\n this.storage = new Storage(this.http);\r\n this.ai = new AI(this.http);\r\n this.functions = new Functions(this.http);\r\n\r\n // Start async initialization (fire and forget)\r\n // This will emit INITIAL_SESSION event when complete\r\n this._initializeAsync();\r\n }\r\n\r\n /**\r\n * Internal async initialization - discovers backend config and recovers session.\r\n * Emits INITIAL_SESSION event when complete.\r\n * @internal\r\n */\r\n private async _initializeAsync(): Promise<void> {\r\n try {\r\n // Discover backend configuration\r\n this.backendConfig = await discoverBackendConfig(\r\n this.http.baseUrl,\r\n this.http.fetch\r\n );\r\n\r\n // Create and set appropriate storage strategy\r\n const strategy = createSessionStorage(this.backendConfig);\r\n this.tokenManager.setStrategy(strategy);\r\n\r\n // Detect OAuth callback first (might set session)\r\n this.auth.detectAuthCallback();\r\n\r\n // Check if we already have a session from OAuth callback\r\n let currentSession = this.tokenManager.getSession();\r\n \r\n // If no session from OAuth but backend supports refresh tokens, try to recover\r\n if (!currentSession?.accessToken && this.backendConfig.refreshTokens) {\r\n if (this.tokenManager.shouldAttemptRefresh()) {\r\n try {\r\n await this.auth.refreshToken();\r\n currentSession = this.tokenManager.getSession();\r\n } catch {\r\n // Refresh failed - no valid session\r\n this.tokenManager.clearSession();\r\n this.http.setAuthToken(null);\r\n }\r\n }\r\n }\r\n\r\n // Mark initialization complete\r\n // Each subscriber will receive INITIAL_SESSION via onAuthStateChange\r\n // (following Supabase pattern - no broadcast here, each subscriber gets it individually)\r\n this.initializeResolve();\r\n } catch {\r\n // If discovery fails, continue with default (persistent) storage\r\n // Still detect OAuth callback\r\n this.auth.detectAuthCallback();\r\n \r\n // Mark initialization complete\r\n this.initializeResolve();\r\n }\r\n }\r\n\r\n /**\r\n * Wait for client initialization to complete\r\n * @returns Promise that resolves when initialization is done\r\n */\r\n async waitForInitialization(): Promise<void> {\r\n return this.initializePromise;\r\n }\r\n\r\n /**\r\n * Get the underlying HTTP client for custom requests\r\n */\r\n getHttpClient(): HttpClient {\r\n return this.http;\r\n }\r\n\r\n /**\r\n * Get the discovered backend configuration\r\n */\r\n getBackendConfig(): BackendConfig | null {\r\n return this.backendConfig;\r\n }\r\n\r\n /**\r\n * Get the current storage strategy identifier\r\n */\r\n getStorageStrategy(): string {\r\n return this.tokenManager.getStrategyId();\r\n }\r\n}\r\n\r\n/**\r\n * Create an InsForge client.\r\n * This is a convenience alias for `new InsForgeClient(config)`.\r\n * \r\n * Note: The client initializes asynchronously in the background.\r\n * Subscribe to `auth.onAuthStateChange` to be notified when ready.\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createClient } from '@insforge/sdk';\r\n * \r\n * const client = createClient({\r\n * baseUrl: 'http://localhost:7130'\r\n * });\r\n * \r\n * // Subscribe to auth state changes\r\n * client.auth.onAuthStateChange((event, session) => {\r\n * if (event === 'INITIAL_SESSION') {\r\n * // Initialization complete\r\n * console.log('Ready!', session ? 'Logged in' : 'Not logged in');\r\n * }\r\n * });\r\n * ```\r\n */\r\nexport function createClient(config: InsForgeConfig = {}): InsForgeClient {\r\n return new InsForgeClient(config);\r\n}\r\n","/**\r\n * @insforge/sdk - TypeScript SDK for InsForge Backend-as-a-Service\r\n * \r\n * @packageDocumentation\r\n */\r\n\r\n// Main client\r\nexport { InsForgeClient } from './client';\r\n\r\n// Types\r\nexport type {\r\n InsForgeConfig,\r\n InsForgeConfig as ClientOptions, // Alias for compatibility\r\n TokenStorage,\r\n AuthSession,\r\n ApiError,\r\n} from './types';\r\n\r\nexport { InsForgeError } from './types';\r\n\r\n// Re-export shared schemas that SDK users will need\r\nexport type {\r\n UserSchema,\r\n CreateUserRequest,\r\n CreateSessionRequest,\r\n AuthErrorResponse,\r\n} from '@insforge/shared-schemas';\r\n\r\n// Re-export auth module for advanced usage\r\nexport { Auth } from './modules/auth';\r\n\r\nexport type {\r\n ProfileData,\r\n UpdateProfileData,\r\n AuthStateChangeEvent,\r\n AuthStateChangeCallback,\r\n} from './modules/auth';\r\n\r\n// Re-export database module (using postgrest-js)\r\nexport { Database } from './modules/database-postgrest';\r\n// Note: QueryBuilder is no longer exported as we use postgrest-js QueryBuilder internally\r\n\r\n// Re-export storage module and types\r\nexport { Storage, StorageBucket } from './modules/storage';\r\nexport type { StorageResponse } from './modules/storage';\r\n\r\n// Re-export AI module\r\nexport { AI } from './modules/ai';\r\n\r\n// Re-export Functions module\r\nexport { Functions } from './modules/functions';\r\nexport type { FunctionInvokeOptions } from './modules/functions';\r\n\r\n// Re-export HTTP client for advanced usage\r\nexport { HttpClient } from './lib/http-client';\r\n\r\n// Re-export Token Manager\r\nexport { TokenManager } from './lib/token-manager';\r\n\r\n// Re-export backend config utilities\r\nexport { discoverBackendConfig, createSessionStorage, getDefaultBackendConfig } from './lib/backend-config';\r\nexport type { BackendConfig } from './lib/backend-config';\r\n\r\n// Re-export session storage strategies for advanced usage\r\nexport {\r\n SecureSessionStorage,\r\n LocalSessionStorage,\r\n} from './lib/session-storage';\r\nexport type { SessionStorageStrategy } from './lib/session-storage';\r\n\r\n// Factory function for creating clients (synchronous, recommended)\r\nexport { createClient } from './client';\r\n\r\n// Default export for convenience\r\nimport { InsForgeClient as DefaultClient } from './client';\r\nexport default DefaultClient;\r\n"],"mappings":"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","names":["data","error","response"]}