@spacelr/sdk 0.1.4 → 0.1.6

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":["../../../../libs/sdk/src/core/errors.ts","../../../../libs/sdk/src/core/http-client.ts","../../../../libs/sdk/src/core/token-storage.ts","../../../../libs/sdk/src/core/token-manager.ts","../../../../libs/sdk/src/types/common.ts","../../../../libs/sdk/src/core/pkce.ts","../../../../libs/sdk/src/core/realtime.ts","../../../../libs/sdk/src/modules/auth.module.ts","../../../../libs/sdk/src/modules/storage.module.ts","../../../../libs/sdk/src/modules/database.module.ts","../../../../libs/sdk/src/modules/notifications.module.ts","../../../../libs/sdk/src/client.ts"],"sourcesContent":["export class SpacelrError extends Error {\n readonly code: string;\n readonly statusCode?: number;\n readonly details?: Record<string, unknown>;\n\n constructor(\n message: string,\n code: string,\n statusCode?: number,\n details?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'SpacelrError';\n this.code = code;\n this.statusCode = statusCode;\n this.details = details;\n }\n}\n\nexport class SpacelrAuthError extends SpacelrError {\n constructor(\n message: string,\n statusCode = 401,\n details?: Record<string, unknown>\n ) {\n super(message, 'AUTH_ERROR', statusCode, details);\n this.name = 'SpacelrAuthError';\n }\n}\n\nexport class SpacelrNetworkError extends SpacelrError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'NETWORK_ERROR', undefined, details);\n this.name = 'SpacelrNetworkError';\n }\n}\n\nexport class SpacelrTimeoutError extends SpacelrError {\n constructor(timeoutMs: number) {\n super(\n `Request timed out after ${timeoutMs}ms`,\n 'TIMEOUT_ERROR',\n undefined,\n { timeoutMs }\n );\n this.name = 'SpacelrTimeoutError';\n }\n}\n\nexport class SpacelrTwoFactorRequiredError extends SpacelrError {\n readonly twoFactorToken: string;\n\n constructor(twoFactorToken: string, details?: Record<string, unknown>) {\n super(\n 'Two-factor authentication required',\n 'TWO_FACTOR_REQUIRED',\n 200,\n details\n );\n this.name = 'SpacelrTwoFactorRequiredError';\n this.twoFactorToken = twoFactorToken;\n }\n}\n\nexport class SpacelrEmailVerificationRequiredError extends SpacelrError {\n readonly emailSent: boolean;\n\n constructor(emailSent: boolean, details?: Record<string, unknown>) {\n super(\n emailSent\n ? 'Email verification required. A new verification email has been sent.'\n : 'Email verification required. Please check your inbox for the verification email.',\n 'EMAIL_VERIFICATION_REQUIRED',\n 200,\n details\n );\n this.name = 'SpacelrEmailVerificationRequiredError';\n this.emailSent = emailSent;\n }\n}\n","import { SpacelrClientConfig, ApiResponse } from '../types';\nimport {\n SpacelrError,\n SpacelrAuthError,\n SpacelrNetworkError,\n SpacelrTimeoutError,\n SpacelrTwoFactorRequiredError,\n SpacelrEmailVerificationRequiredError,\n} from './errors';\nimport { TokenManager } from './token-manager';\n\nexport interface HttpRequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n path: string;\n body?: unknown;\n authenticated?: boolean;\n headers?: Record<string, string>;\n query?: Record<string, string | number | undefined>;\n /** Send cookies cross-origin (credentials: 'include'). Defaults to true for /auth/ paths. */\n withCredentials?: boolean;\n}\n\nexport class HttpClient {\n private config: SpacelrClientConfig;\n private tokenManager: TokenManager;\n\n constructor(config: SpacelrClientConfig, tokenManager: TokenManager) {\n this.config = config;\n this.tokenManager = tokenManager;\n }\n\n async request<T>(options: HttpRequestOptions): Promise<T> {\n const url = this.buildUrl(options.path, options.query);\n const headers = await this.buildHeaders(options);\n const timeout = this.config.timeout ?? 30000;\n const includeCredentials = options.withCredentials ?? options.path.startsWith('/auth/');\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method: options.method,\n headers,\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n ...(includeCredentials && { credentials: 'include' as RequestCredentials }),\n });\n\n const responseBody = await this.parseResponse(response);\n\n if (!response.ok) {\n this.throwHttpError(response.status, responseBody);\n }\n\n return this.extractData<T>(responseBody);\n } catch (error) {\n if (error instanceof SpacelrError) throw error;\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n throw new SpacelrTimeoutError(timeout);\n }\n\n throw new SpacelrNetworkError(\n error instanceof Error ? error.message : 'Network request failed'\n );\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async uploadForm<T>(\n path: string,\n formData: FormData,\n onProgress?: (event: { loaded: number; total: number }) => void,\n ): Promise<T> {\n const url = this.buildUrl(path);\n const headers = await this.buildFormHeaders();\n const timeoutMs = this.config.timeout ?? 30000;\n\n // Use XMLHttpRequest when progress tracking is needed\n if (onProgress) {\n return this.uploadFormWithProgress<T>(url, headers, formData, onProgress, timeoutMs);\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: formData,\n signal: controller.signal,\n });\n\n const responseBody = await this.parseResponse(response);\n\n if (!response.ok) {\n this.throwHttpError(response.status, responseBody);\n }\n\n return this.extractData<T>(responseBody);\n } catch (error) {\n if (error instanceof SpacelrError) throw error;\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n throw new SpacelrTimeoutError(timeoutMs);\n }\n\n throw new SpacelrNetworkError(\n error instanceof Error ? error.message : 'Network request failed'\n );\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private uploadFormWithProgress<T>(\n url: string,\n headers: Record<string, string>,\n formData: FormData,\n onProgress: (event: { loaded: number; total: number }) => void,\n timeoutMs: number,\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open('POST', url);\n xhr.timeout = timeoutMs;\n\n for (const [key, value] of Object.entries(headers)) {\n xhr.setRequestHeader(key, value);\n }\n\n xhr.upload.addEventListener('progress', (e) => {\n if (e.lengthComputable) {\n onProgress({ loaded: e.loaded, total: e.total });\n }\n });\n\n xhr.addEventListener('load', () => {\n try {\n const contentType = xhr.getResponseHeader('content-type') ?? '';\n if (!contentType.includes('application/json')) {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve({ success: true, data: xhr.responseText } as unknown as T);\n } else {\n reject(new SpacelrNetworkError(`HTTP ${xhr.status}: ${xhr.responseText.slice(0, 200)}`));\n }\n return;\n }\n const body = JSON.parse(xhr.responseText);\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve(this.extractData<T>(body));\n } else {\n this.throwHttpError(xhr.status, body);\n }\n } catch (error) {\n if (error instanceof SpacelrError) {\n reject(error);\n } else {\n reject(new SpacelrNetworkError('Failed to parse response'));\n }\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new SpacelrNetworkError('Network request failed'));\n });\n\n xhr.addEventListener('timeout', () => {\n xhr.abort();\n reject(new SpacelrTimeoutError(timeoutMs));\n });\n\n xhr.send(formData);\n });\n }\n\n private buildUrl(\n path: string,\n query?: Record<string, string | number | undefined>\n ): string {\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n // .well-known endpoints are served at the origin without the API prefix\n const base = cleanPath.startsWith('/.well-known')\n ? new URL(this.config.apiUrl).origin\n : this.config.apiUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}${cleanPath}`);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n }\n\n private async buildHeaders(\n options: HttpRequestOptions\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'x-client-id': this.config.clientId,\n 'x-project-id': this.config.projectId,\n ...options.headers,\n };\n\n if (options.authenticated) {\n const token = await this.tokenManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n }\n\n return headers;\n }\n\n private async buildFormHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'x-client-id': this.config.clientId,\n 'x-project-id': this.config.projectId,\n };\n\n const token = await this.tokenManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n return headers;\n }\n\n private async parseResponse(\n response: Response\n ): Promise<ApiResponse | Record<string, unknown>> {\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('application/json')) {\n return response.json();\n }\n const text = await response.text();\n return { success: response.ok, data: text };\n }\n\n private throwHttpError(\n statusCode: number,\n body: ApiResponse | Record<string, unknown>\n ): never {\n const apiBody = body as ApiResponse;\n const message =\n apiBody.error?.message ?? `HTTP ${statusCode}`;\n const code = apiBody.error?.code ?? `HTTP_${statusCode}`;\n const details = apiBody.error?.details;\n\n if (statusCode === 401 || statusCode === 403) {\n throw new SpacelrAuthError(message, statusCode, details);\n }\n\n throw new SpacelrError(message, code, statusCode, details);\n }\n\n private extractData<T>(body: ApiResponse | Record<string, unknown>): T {\n const apiBody = body as ApiResponse;\n // If the response wraps data in a standard envelope, extract it\n if ('success' in apiBody && apiBody.data !== undefined) {\n const data = apiBody.data as Record<string, unknown>;\n // Check if the response indicates email verification is required\n if (data['emailVerificationRequired'] === true) {\n throw new SpacelrEmailVerificationRequiredError(data['emailSent'] === true);\n }\n // Check if the response indicates a 2FA challenge\n if (data['twoFactorRequired'] === true && typeof data['twoFactorToken'] === 'string') {\n throw new SpacelrTwoFactorRequiredError(data['twoFactorToken']);\n }\n return apiBody.data as T;\n }\n // Check unwrapped responses\n const rawBody = body as Record<string, unknown>;\n if (rawBody['emailVerificationRequired'] === true) {\n throw new SpacelrEmailVerificationRequiredError(rawBody['emailSent'] === true);\n }\n if (rawBody['twoFactorRequired'] === true && typeof rawBody['twoFactorToken'] === 'string') {\n throw new SpacelrTwoFactorRequiredError(rawBody['twoFactorToken']);\n }\n // Otherwise return the whole body (e.g. OIDC endpoints return raw objects)\n return body as T;\n }\n}\n","import { StoredTokens, TokenStorage } from '../types';\n\nexport type { TokenStorage };\n\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: StoredTokens | null = null;\n\n async getTokens(): Promise<StoredTokens | null> {\n return this.tokens;\n }\n\n async setTokens(tokens: StoredTokens): Promise<void> {\n this.tokens = tokens;\n }\n\n async clearTokens(): Promise<void> {\n this.tokens = null;\n }\n}\n\nexport class BrowserTokenStorage implements TokenStorage {\n private readonly storageKey: string;\n\n constructor(storageKey = 'spacelr_tokens') {\n this.storageKey = storageKey;\n }\n\n async getTokens(): Promise<StoredTokens | null> {\n try {\n const raw = localStorage.getItem(this.storageKey);\n if (!raw) return null;\n return JSON.parse(raw) as StoredTokens;\n } catch {\n return null;\n }\n }\n\n async setTokens(tokens: StoredTokens): Promise<void> {\n try {\n localStorage.setItem(this.storageKey, JSON.stringify(tokens));\n } catch {\n // Quota exceeded or private browsing — silently ignore\n }\n }\n\n async clearTokens(): Promise<void> {\n try {\n localStorage.removeItem(this.storageKey);\n } catch {\n // localStorage unavailable — silently ignore\n }\n }\n}\n","import { StoredTokens, TokenStorage } from '../types';\nimport { MemoryTokenStorage } from './token-storage';\n\nexport type RefreshCallback = (refreshToken: string) => Promise<StoredTokens>;\n\nexport class TokenManager {\n private storage: TokenStorage;\n private refreshBufferSeconds: number;\n private refreshCallback: RefreshCallback | null = null;\n private refreshPromise: Promise<StoredTokens> | null = null;\n\n constructor(storage?: TokenStorage, refreshBufferSeconds = 60) {\n this.storage = storage ?? new MemoryTokenStorage();\n this.refreshBufferSeconds = refreshBufferSeconds;\n }\n\n setRefreshCallback(callback: RefreshCallback): void {\n this.refreshCallback = callback;\n }\n\n async getAccessToken(): Promise<string | null> {\n const tokens = await this.storage.getTokens();\n if (!tokens) return null;\n\n if (this.isTokenExpired(tokens)) {\n const refreshed = await this.tryRefresh(tokens);\n return refreshed?.accessToken ?? null;\n }\n\n if (this.shouldRefresh(tokens)) {\n // Trigger background refresh but return current token\n this.tryRefresh(tokens).catch(() => {\n // Ignore background refresh failures\n });\n }\n\n return tokens.accessToken;\n }\n\n async setTokens(tokens: StoredTokens): Promise<void> {\n await this.storage.setTokens(tokens);\n }\n\n async clearTokens(): Promise<void> {\n await this.storage.clearTokens();\n this.refreshPromise = null;\n }\n\n async getStoredTokens(): Promise<StoredTokens | null> {\n return this.storage.getTokens();\n }\n\n private isTokenExpired(tokens: StoredTokens): boolean {\n if (!tokens.expiresAt) return false;\n return Date.now() >= tokens.expiresAt * 1000;\n }\n\n private shouldRefresh(tokens: StoredTokens): boolean {\n if (!tokens.expiresAt || !tokens.refreshToken) return false;\n const bufferMs = this.refreshBufferSeconds * 1000;\n return Date.now() >= tokens.expiresAt * 1000 - bufferMs;\n }\n\n private async tryRefresh(\n tokens: StoredTokens\n ): Promise<StoredTokens | null> {\n if (!tokens.refreshToken || !this.refreshCallback) return null;\n\n // Deduplicate concurrent refresh calls\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.executeRefresh(tokens.refreshToken);\n\n try {\n const result = await this.refreshPromise;\n return result;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async executeRefresh(refreshToken: string): Promise<StoredTokens> {\n const callback = this.refreshCallback as RefreshCallback;\n const newTokens = await callback(refreshToken);\n await this.storage.setTokens(newTokens);\n return newTokens;\n }\n}\n","export enum FileVisibility {\n PRIVATE = 'private',\n SHARED = 'shared',\n PUBLIC = 'public',\n}\n\nexport enum GrantType {\n AUTHORIZATION_CODE = 'authorization_code',\n CLIENT_CREDENTIALS = 'client_credentials',\n REFRESH_TOKEN = 'refresh_token',\n}\n\nexport enum CodeChallengeMethod {\n PLAIN = 'plain',\n S256 = 'S256',\n}\n\nexport enum SharePermission {\n READ = 'read',\n WRITE = 'write',\n}\n\nexport interface ApiResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: {\n message: string;\n code: string;\n details?: Record<string, unknown>;\n };\n}\n","import { PKCEChallenge, CodeChallengeMethod } from '../types';\n\nfunction generateRandomBytes(length: number): Uint8Array {\n if (\n typeof globalThis.crypto !== 'undefined' &&\n globalThis.crypto.getRandomValues\n ) {\n const bytes = new Uint8Array(length);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n }\n\n // Node.js fallback\n const nodeCrypto = require('crypto') as typeof import('crypto');\n return new Uint8Array(nodeCrypto.randomBytes(length));\n}\n\nfunction base64UrlEncode(buffer: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < buffer.length; i++) {\n binary += String.fromCharCode(buffer[i]);\n }\n\n const base64 = btoa(binary);\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nasync function sha256(input: string): Promise<Uint8Array> {\n if (\n typeof globalThis.crypto !== 'undefined' &&\n globalThis.crypto.subtle\n ) {\n const encoder = new TextEncoder();\n const data = encoder.encode(input);\n const hash = await globalThis.crypto.subtle.digest('SHA-256', data);\n return new Uint8Array(hash);\n }\n\n // Node.js fallback\n const nodeCrypto = require('crypto') as typeof import('crypto');\n const hash = nodeCrypto.createHash('sha256').update(input).digest();\n return new Uint8Array(hash);\n}\n\nexport async function generatePKCEChallenge(): Promise<PKCEChallenge> {\n const randomBytes = generateRandomBytes(32);\n const codeVerifier = base64UrlEncode(randomBytes);\n\n const hashBytes = await sha256(codeVerifier);\n const codeChallenge = base64UrlEncode(hashBytes);\n\n return {\n codeVerifier,\n codeChallenge,\n codeChallengeMethod: CodeChallengeMethod.S256,\n };\n}\n","import { io, Socket } from 'socket.io-client';\n\n// NOTE: This interface is duplicated in @spacelr-workspace/shared-types (database-events.ts).\n// The SDK cannot import from shared-types since it ships as a standalone npm package.\n// Keep both definitions in sync when making changes.\nexport interface DatabaseChangeEvent {\n type: 'insert' | 'update' | 'delete';\n projectId: string;\n collectionName: string;\n documentId: string;\n document?: Record<string, unknown>;\n timestamp: number;\n}\n\nexport interface RealtimeConfig {\n baseUrl: string;\n getToken: () => Promise<string | null>;\n}\n\nexport class RealtimeClient {\n private socket: Socket | null = null;\n private config: RealtimeConfig;\n private subscriptions = new Map<string, Set<(event: DatabaseChangeEvent) => void>>();\n private connecting: Promise<void> | null = null;\n // Store original where objects per room for reconnect resubscription\n private roomWhereMap = new Map<string, Record<string, string | number | boolean>>();\n\n constructor(config: RealtimeConfig) {\n this.config = config;\n }\n\n async subscribe(\n projectId: string,\n collectionName: string,\n callback: (event: DatabaseChangeEvent) => void,\n onError?: (error: Error) => void,\n where?: Record<string, string | number | boolean>,\n ): Promise<() => void> {\n await this.ensureConnected();\n\n const room = this.buildRoomKey(projectId, collectionName, where);\n\n // Track the callback\n if (!this.subscriptions.has(room)) {\n this.subscriptions.set(room, new Set());\n }\n const callbacks = this.subscriptions.get(room);\n callbacks?.add(callback);\n\n // If this is the first subscription for this room, tell the server\n if (callbacks?.size === 1) {\n if (where && Object.keys(where).length > 0) {\n this.roomWhereMap.set(room, where);\n }\n const payload: Record<string, unknown> = { projectId, collectionName };\n if (where && Object.keys(where).length > 0) {\n payload['where'] = where;\n }\n this.socket?.emit(\n 'subscribe',\n payload,\n (response: { subscribed?: string; error?: string }) => {\n if (response.error && onError) {\n onError(new Error(response.error));\n }\n },\n );\n }\n\n // Return unsubscribe function\n return () => {\n const callbacks = this.subscriptions.get(room);\n if (callbacks) {\n callbacks.delete(callback);\n if (callbacks.size === 0) {\n this.subscriptions.delete(room);\n this.roomWhereMap.delete(room);\n const payload: Record<string, unknown> = { projectId, collectionName };\n if (where && Object.keys(where).length > 0) {\n payload['where'] = where;\n }\n this.socket?.emit('unsubscribe', payload);\n }\n }\n };\n }\n\n disconnect(): void {\n if (this.socket) {\n this.socket.disconnect();\n this.socket = null;\n }\n this.subscriptions.clear();\n this.roomWhereMap.clear();\n this.connecting = null;\n }\n\n private buildRoomKey(\n projectId: string,\n collectionName: string,\n where?: Record<string, string | number | boolean>,\n ): string {\n const base = `db:${projectId}:${collectionName}`;\n if (!where || Object.keys(where).length === 0) {\n return base;\n }\n const filterStr = Object.keys(where)\n .sort()\n .map((k) => `${k}=${String(where[k])}`)\n .join('&');\n return `${base}?${filterStr}`;\n }\n\n private async ensureConnected(): Promise<void> {\n if (this.socket?.connected) return;\n\n if (this.connecting) {\n return this.connecting;\n }\n\n this.connecting = this.connect();\n try {\n await this.connecting;\n } finally {\n this.connecting = null;\n }\n }\n\n private async connect(): Promise<void> {\n const token = await this.config.getToken();\n if (!token) {\n throw new Error('No authentication token available');\n }\n\n // Derive WebSocket URL from the API URL\n const wsUrl = this.config.baseUrl.replace(/\\/api\\/v\\d+\\/?$/, '');\n\n return new Promise<void>((resolve, reject) => {\n let initialConnect = true;\n\n this.socket = io(`${wsUrl}/database`, {\n auth: async (cb: (data: Record<string, unknown>) => void) => {\n try {\n const freshToken = await this.config.getToken();\n cb({ token: freshToken });\n } catch {\n cb({ token: null });\n }\n },\n transports: ['websocket'],\n reconnection: false, // Disabled until first successful connect\n });\n\n this.socket.on('authenticated', () => {\n if (initialConnect) {\n initialConnect = false;\n // Enable reconnection after first successful connect\n if (this.socket) {\n this.socket.io.opts.reconnection = true;\n this.socket.io.opts.reconnectionDelay = 1000;\n this.socket.io.opts.reconnectionDelayMax = 5000;\n this.socket.io.opts.reconnectionAttempts = 50;\n }\n resolve();\n }\n });\n\n this.socket.on('connect', () => {\n if (!initialConnect) {\n // Reconnect — server will re-authenticate and emit 'authenticated',\n // but we can already re-subscribe since the socket carries the auth callback\n this.resubscribeAll();\n }\n });\n\n this.socket.on('connect_error', (err) => {\n if (initialConnect) {\n reject(new Error(`WebSocket connection failed: ${err.message}`));\n }\n });\n\n this.socket.on('db:event', (event: DatabaseChangeEvent) => {\n const base = `db:${event.projectId}:${event.collectionName}`;\n\n // Dispatch to whole-collection subscribers via direct Map lookup\n const baseCallbacks = this.subscriptions.get(base);\n if (baseCallbacks) {\n for (const cb of baseCallbacks) {\n try { cb(event); } catch { /* ignore callback errors */ }\n }\n }\n\n // Dispatch to matching filtered subscriptions\n for (const [room, callbacks] of this.subscriptions) {\n if (room.startsWith(`${base}?`) && this.eventMatchesRoom(event, room)) {\n for (const cb of callbacks) {\n try { cb(event); } catch { /* ignore callback errors */ }\n }\n }\n }\n });\n\n this.socket.on('disconnect', () => {\n // socket.io handles reconnection automatically\n });\n });\n }\n\n private eventMatchesRoom(event: DatabaseChangeEvent, room: string): boolean {\n const base = `db:${event.projectId}:${event.collectionName}`;\n if (room === base) return true;\n if (!room.startsWith(`${base}?`)) return false;\n\n // For filtered rooms, check the document against the where filter\n const where = this.roomWhereMap.get(room);\n if (!where) return true; // No filter stored for this room\n if (!event.document) return false; // Delete events without document can't match a filter\n\n for (const [key, value] of Object.entries(where)) {\n const docValue = event.document[key];\n if (Array.isArray(docValue)) {\n if (!docValue.some((item) => String(item) === String(value))) {\n return false;\n }\n } else if (String(docValue) !== String(value)) {\n return false;\n }\n }\n return true;\n }\n\n private resubscribeAll(): void {\n for (const [room] of this.subscriptions) {\n // Parse room format: db:{projectId}:{collectionName} or db:{projectId}:{collectionName}?filter\n const queryIdx = room.indexOf('?');\n const basePart = queryIdx >= 0 ? room.substring(0, queryIdx) : room;\n const parts = basePart.split(':');\n if (parts.length >= 3) {\n const projectId = parts[1];\n const collectionName = parts.slice(2).join(':');\n const payload: Record<string, unknown> = { projectId, collectionName };\n\n // Use stored where object to preserve original types (number, boolean)\n const where = this.roomWhereMap.get(room);\n if (where) {\n payload['where'] = where;\n }\n\n this.socket?.emit(\n 'subscribe',\n payload,\n (response: { subscribed?: string; error?: string }) => {\n if (response?.error) {\n // Server rejected resubscription — clean up the stale subscription\n this.subscriptions.delete(room);\n this.roomWhereMap.delete(room);\n }\n },\n );\n }\n }\n }\n}\n","import { HttpClient, TokenManager, generatePKCEChallenge } from '../core';\nimport {\n SpacelrClientConfig,\n GrantType,\n LoginParams,\n LoginResponse,\n RegisterParams,\n RegisterResponse,\n TokenResponse,\n UserInfo,\n UserProfile,\n AuthorizationUrlParams,\n ExchangeCodeParams,\n PKCEChallenge,\n OpenIDConfiguration,\n JWKSResponse,\n TwoFactorVerifyParams,\n} from '../types';\n\nexport class AuthModule {\n private http: HttpClient;\n private tokenManager: TokenManager;\n private config: SpacelrClientConfig;\n\n constructor(\n http: HttpClient,\n tokenManager: TokenManager,\n config: SpacelrClientConfig\n ) {\n this.http = http;\n this.tokenManager = tokenManager;\n this.config = config;\n\n // Wire up refresh callback to avoid circular deps\n this.tokenManager.setRefreshCallback(async (refreshToken: string) => {\n const result = await this.refresh(refreshToken);\n const expiresAt = result.expires_in\n ? Math.floor(Date.now() / 1000) + result.expires_in\n : undefined;\n return {\n accessToken: result.access_token,\n refreshToken: result.refresh_token,\n expiresAt,\n };\n });\n }\n\n async login(params: LoginParams): Promise<LoginResponse> {\n const response = await this.http.request<LoginResponse>({\n method: 'POST',\n path: '/auth/login',\n body: params,\n });\n\n await this.storeTokensFromLogin(response);\n return response;\n }\n\n async register(params: RegisterParams): Promise<RegisterResponse> {\n const response = await this.http.request<RegisterResponse>({\n method: 'POST',\n path: '/auth/register',\n body: params,\n });\n\n // Only store tokens if they were returned (not when email verification is required)\n if (response.access_token) {\n await this.storeTokensFromRegister(response);\n }\n return response;\n }\n\n async refresh(refreshToken: string): Promise<TokenResponse> {\n return this.http.request<TokenResponse>({\n method: 'POST',\n path: '/auth/refresh',\n body: { refreshToken },\n });\n }\n\n async getProfile(): Promise<UserProfile> {\n return this.http.request<UserProfile>({\n method: 'GET',\n path: '/auth/me',\n authenticated: true,\n });\n }\n\n async logout(): Promise<void> {\n await this.http.request<void>({\n method: 'POST',\n path: '/auth/logout',\n authenticated: true,\n });\n await this.tokenManager.clearTokens();\n }\n\n async verifyEmail(token: string): Promise<{ message: string }> {\n return this.http.request<{ message: string }>({\n method: 'GET',\n path: '/auth/verify-email',\n query: { token },\n });\n }\n\n async resendVerification(email: string): Promise<{ message: string }> {\n return this.http.request<{ message: string }>({\n method: 'POST',\n path: '/auth/resend-verification',\n body: { email },\n });\n }\n\n async getUserInfo(): Promise<UserInfo> {\n return this.http.request<UserInfo>({\n method: 'GET',\n path: this.config.userInfoEndpoint ?? '/auth/userinfo',\n authenticated: true,\n });\n }\n\n getAuthorizationUrl(params: AuthorizationUrlParams): string {\n const baseUrl = this.config.apiUrl.replace(/\\/+$/, '');\n const endpoint =\n this.config.authorizationEndpoint ?? '/auth/authorize';\n const url = new URL(`${baseUrl}${endpoint}`);\n\n url.searchParams.set('client_id', this.config.clientId);\n url.searchParams.set('redirect_uri', params.redirectUri);\n url.searchParams.set('response_type', params.responseType ?? 'code');\n\n const scope =\n params.scope ?? this.config.scopes?.join(' ') ?? 'openid';\n url.searchParams.set('scope', scope);\n\n if (params.state) {\n url.searchParams.set('state', params.state);\n }\n if (params.codeChallenge) {\n url.searchParams.set('code_challenge', params.codeChallenge);\n }\n if (params.codeChallengeMethod) {\n url.searchParams.set(\n 'code_challenge_method',\n params.codeChallengeMethod\n );\n }\n\n return url.toString();\n }\n\n async exchangeCode(params: ExchangeCodeParams): Promise<TokenResponse> {\n const body: Record<string, string> = {\n grant_type: params.grantType ?? GrantType.AUTHORIZATION_CODE,\n code: params.code,\n redirect_uri: params.redirectUri,\n client_id: this.config.clientId,\n };\n\n if (params.clientSecret) {\n body['client_secret'] = params.clientSecret;\n }\n if (params.codeVerifier) {\n body['code_verifier'] = params.codeVerifier;\n }\n\n const tokenEndpoint =\n this.config.tokenEndpoint ?? '/auth/token';\n\n const response = await this.http.request<TokenResponse>({\n method: 'POST',\n path: tokenEndpoint,\n body,\n });\n\n const expiresAt = response.expires_in\n ? Math.floor(Date.now() / 1000) + response.expires_in\n : undefined;\n\n await this.tokenManager.setTokens({\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n expiresAt,\n });\n\n return response;\n }\n\n async generatePKCE(): Promise<PKCEChallenge> {\n return generatePKCEChallenge();\n }\n\n async getOpenIDConfiguration(): Promise<OpenIDConfiguration> {\n return this.http.request<OpenIDConfiguration>({\n method: 'GET',\n path: '/.well-known/openid-configuration',\n });\n }\n\n async getJWKS(): Promise<JWKSResponse> {\n return this.http.request<JWKSResponse>({\n method: 'GET',\n path: '/.well-known/jwks.json',\n });\n }\n\n /**\n * Request a password reset email.\n * Always returns a generic message regardless of whether the email exists.\n */\n async requestPasswordReset(email: string): Promise<{ message: string }> {\n return this.http.request<{ message: string }>({\n method: 'POST',\n path: '/auth/request-password-reset',\n body: { email },\n });\n }\n\n /**\n * Reset password using a token received via email.\n */\n async resetPassword(\n token: string,\n password: string,\n ): Promise<{ message: string }> {\n return this.http.request<{ message: string }>({\n method: 'POST',\n path: '/auth/reset-password',\n body: { token, password },\n });\n }\n\n /**\n * Exchange a one-time verification code for tokens.\n * Use this after email verification redirects the user with a ?loginCode= parameter.\n */\n async exchangeVerificationCode(code: string): Promise<LoginResponse> {\n const response = await this.http.request<LoginResponse>({\n method: 'POST',\n path: '/auth/exchange-code',\n body: { code },\n });\n\n await this.storeTokensFromLogin(response);\n return response;\n }\n\n /**\n * Resend a two-factor authentication code email.\n * Call this when the user hasn't received the code or it expired.\n */\n async resendTwoFactorCode(token: string): Promise<{ message: string }> {\n return this.http.request<{ message: string }>({\n method: 'POST',\n path: '/auth/resend-two-factor-code',\n body: { token },\n });\n }\n\n /**\n * Verify a two-factor authentication code.\n * Call this after catching SpacelrTwoFactorRequiredError from login().\n */\n async verifyTwoFactor(params: TwoFactorVerifyParams): Promise<LoginResponse> {\n const response = await this.http.request<LoginResponse>({\n method: 'POST',\n path: '/auth/verify-two-factor',\n body: { token: params.token, code: params.code },\n });\n\n await this.storeTokensFromLogin(response);\n return response;\n }\n\n private async storeTokensFromLogin(response: LoginResponse): Promise<void> {\n const expiresAt = response.expires_in\n ? Math.floor(Date.now() / 1000) + response.expires_in\n : undefined;\n\n await this.tokenManager.setTokens({\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n expiresAt,\n });\n }\n\n private async storeTokensFromRegister(\n response: RegisterResponse\n ): Promise<void> {\n if (!response.access_token) return;\n await this.tokenManager.setTokens({\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n });\n }\n}\n","import { HttpClient, TokenManager } from '../core';\nimport {\n SpacelrClientConfig,\n FileVisibility,\n FileInfo,\n FileListResponse,\n ListFilesParams,\n InitMultipartUploadParams,\n InitMultipartUploadResponse,\n PartEtag,\n ShareFileParams,\n UnshareFileParams,\n QuotaInfo,\n UploadFileParams,\n UploadLargeFileParams,\n UploadProgress,\n DownloadUrlResponse,\n} from '../types';\n\nexport class StorageModule {\n private http: HttpClient;\n private tokenManager: TokenManager;\n private config: SpacelrClientConfig;\n\n constructor(\n http: HttpClient,\n tokenManager: TokenManager,\n config: SpacelrClientConfig\n ) {\n this.http = http;\n this.tokenManager = tokenManager;\n this.config = config;\n }\n\n /**\n * Upload a file through the gateway (no direct S3 access).\n * Accepts a Blob/File (browser) or ArrayBuffer/Uint8Array (Node).\n */\n async uploadFile(\n file: Blob | ArrayBuffer | Uint8Array,\n params: UploadFileParams,\n onProgress?: (progress: UploadProgress) => void,\n ): Promise<FileInfo> {\n const formData = new FormData();\n const blob =\n file instanceof Blob\n ? file\n : new Blob([file as BlobPart], { type: params.mimeType });\n formData.append('file', blob, params.filename);\n if (params.visibility) formData.append('visibility', params.visibility);\n if (params.description) formData.append('description', params.description);\n\n const progressHandler = onProgress\n ? (e: { loaded: number; total: number }) => {\n onProgress({\n loaded: e.loaded,\n total: e.total,\n percentage: e.total > 0 ? Math.round((e.loaded / e.total) * 100) : 0,\n });\n }\n : undefined;\n\n return this.http.uploadForm<FileInfo>('/storage/files', formData, progressHandler);\n }\n\n /**\n * Upload a large file using multipart upload through the gateway.\n * Splits into parts, uploads concurrently, and completes.\n */\n async uploadLargeFile(\n file: Blob | ArrayBuffer | Uint8Array,\n params: UploadLargeFileParams,\n onProgress?: (progress: UploadProgress) => void\n ): Promise<FileInfo> {\n const fileSize = file instanceof Blob ? file.size : file.byteLength;\n const concurrency = params.concurrency ?? 3;\n\n const init = await this.initMultipartUpload({\n filename: params.filename,\n mimeType: params.mimeType,\n totalSizeBytes: fileSize,\n visibility: params.visibility,\n description: params.description,\n });\n\n const { partSize, totalParts, fileId } = init;\n const completedParts: PartEtag[] = [];\n let completedBytes = 0;\n const partProgressMap = new Map<number, number>();\n\n try {\n const allPartNumbers = Array.from(\n { length: totalParts },\n (_, i) => i + 1\n );\n\n for (let i = 0; i < allPartNumbers.length; i += concurrency) {\n const batch = allPartNumbers.slice(i, i + concurrency);\n\n const uploads = batch.map(async (partNumber) => {\n const start = (partNumber - 1) * partSize;\n const end = Math.min(start + partSize, fileSize);\n const chunkSize = end - start;\n const chunk =\n file instanceof Blob\n ? file.slice(start, end)\n : new Blob([file.slice(start, end)]);\n\n const formData = new FormData();\n formData.append('file', chunk, `part-${partNumber}`);\n formData.append('partNumber', String(partNumber));\n\n const partProgress = onProgress && typeof XMLHttpRequest !== 'undefined'\n ? (e: { loaded: number; total: number }) => {\n const ratio = e.total > 0 ? e.loaded / e.total : 0;\n partProgressMap.set(partNumber, Math.min(ratio * chunkSize, chunkSize));\n const inFlightBytes = Array.from(partProgressMap.values())\n .reduce((sum, v) => sum + v, 0);\n const totalLoaded = Math.min(completedBytes + inFlightBytes, fileSize);\n onProgress({\n loaded: totalLoaded,\n total: fileSize,\n percentage: fileSize > 0\n ? Math.min(100, Math.round((totalLoaded / fileSize) * 100))\n : 0,\n });\n }\n : undefined;\n\n const result = await this.http.uploadForm<{ etag: string }>(\n `/storage/files/${fileId}/multipart/upload-part`,\n formData,\n partProgress,\n );\n\n partProgressMap.delete(partNumber);\n completedBytes += chunkSize;\n\n if (onProgress) {\n onProgress({\n loaded: Math.min(completedBytes, fileSize),\n total: fileSize,\n percentage: fileSize > 0\n ? Math.min(100, Math.round((completedBytes / fileSize) * 100))\n : 0,\n });\n }\n\n completedParts.push({\n partNumber,\n etag: result.etag,\n });\n });\n\n await Promise.all(uploads);\n partProgressMap.clear();\n }\n\n // Sort parts by number before completing\n completedParts.sort((a, b) => a.partNumber - b.partNumber);\n\n await this.completeMultipartUpload(fileId, completedParts);\n return this.getFileInfo(fileId);\n } catch (error) {\n // Clean up the incomplete multipart upload on failure\n try {\n await this.abortMultipartUpload(fileId);\n } catch {\n // Abort is best-effort; ignore cleanup failures\n }\n throw error;\n }\n }\n\n async initMultipartUpload(\n params: InitMultipartUploadParams\n ): Promise<InitMultipartUploadResponse> {\n return this.http.request<InitMultipartUploadResponse>({\n method: 'POST',\n path: '/storage/files/multipart/init',\n body: params,\n authenticated: true,\n });\n }\n\n async completeMultipartUpload(\n fileId: string,\n parts: PartEtag[]\n ): Promise<FileInfo> {\n return this.http.request<FileInfo>({\n method: 'POST',\n path: `/storage/files/${fileId}/multipart/complete`,\n body: { parts },\n authenticated: true,\n });\n }\n\n async abortMultipartUpload(fileId: string): Promise<void> {\n await this.http.request<void>({\n method: 'POST',\n path: `/storage/files/${fileId}/multipart/abort`,\n authenticated: true,\n });\n }\n\n async listFiles(params?: ListFilesParams): Promise<FileListResponse> {\n return this.http.request<FileListResponse>({\n method: 'GET',\n path: '/storage/files',\n query: params as Record<string, string | number | undefined>,\n authenticated: true,\n });\n }\n\n async listSharedFiles(params?: ListFilesParams): Promise<FileListResponse> {\n return this.http.request<FileListResponse>({\n method: 'GET',\n path: '/storage/shared',\n query: params as Record<string, string | number | undefined>,\n authenticated: true,\n });\n }\n\n async getFileInfo(fileId: string): Promise<FileInfo> {\n return this.http.request<FileInfo>({\n method: 'GET',\n path: `/storage/files/${fileId}`,\n authenticated: true,\n });\n }\n\n async downloadFile(fileId: string): Promise<Blob> {\n const baseUrl = this.config.apiUrl.replace(/\\/+$/, '');\n const url = `${baseUrl}/storage/files/${fileId}/download`;\n\n const headers: Record<string, string> = {\n 'x-client-id': this.config.clientId,\n 'x-project-id': this.config.projectId,\n };\n\n const token = await this.tokenManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n const response = await fetch(url, { headers });\n\n if (!response.ok) {\n throw new Error(`Download failed: ${response.status}`);\n }\n\n return response.blob();\n }\n\n async getDownloadUrl(fileId: string): Promise<DownloadUrlResponse> {\n return this.http.request<DownloadUrlResponse>({\n method: 'GET',\n path: `/storage/files/${fileId}/download-url`,\n authenticated: true,\n });\n }\n\n async deleteFile(fileId: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `/storage/files/${fileId}`,\n authenticated: true,\n });\n }\n\n async shareFile(\n fileId: string,\n params: ShareFileParams\n ): Promise<void> {\n await this.http.request<void>({\n method: 'POST',\n path: `/storage/files/${fileId}/share`,\n body: params,\n authenticated: true,\n });\n }\n\n async unshareFile(\n fileId: string,\n params: UnshareFileParams\n ): Promise<void> {\n await this.http.request<void>({\n method: 'POST',\n path: `/storage/files/${fileId}/unshare`,\n body: params,\n authenticated: true,\n });\n }\n\n async updateVisibility(\n fileId: string,\n visibility: FileVisibility\n ): Promise<FileInfo> {\n return this.http.request<FileInfo>({\n method: 'PATCH',\n path: `/storage/files/${fileId}/visibility`,\n body: { visibility },\n authenticated: true,\n });\n }\n\n async getQuota(): Promise<QuotaInfo> {\n return this.http.request<QuotaInfo>({\n method: 'GET',\n path: '/storage/quota',\n authenticated: true,\n });\n }\n\n async getPublicFileUrl(fileId: string, projectId?: string): Promise<string> {\n const baseUrl = this.config.apiUrl.replace(/\\/+$/, '');\n const resolvedProjectId = projectId ?? this.config.projectId;\n return `${baseUrl}/public/files/${resolvedProjectId}/${fileId}`;\n }\n}\n","import { HttpClient } from '../core';\nimport { RealtimeClient, DatabaseChangeEvent } from '../core';\n\nexport interface PopulateOption {\n field: string;\n collection: string;\n foreignField?: string;\n}\n\nexport interface DocumentResult {\n _id: string;\n [key: string]: unknown;\n}\n\nexport interface FindResult<T = Record<string, unknown>> {\n documents: (T & { _id: string })[];\n total: number;\n limit: number;\n offset: number;\n}\n\nexport interface InsertResult {\n insertedCount: number;\n insertedIds: string[];\n}\n\nexport interface UpdateResult {\n matchedCount: number;\n modifiedCount: number;\n}\n\nexport interface DeleteResult {\n deletedCount: number;\n}\n\nexport interface CountResult {\n count: number;\n}\n\nexport interface FindByIdOptions {\n populate?: PopulateOption[];\n}\n\nexport interface SubscribeHandlers<T = Record<string, unknown>> {\n where?: Record<string, string | number | boolean>;\n onInsert?: (doc: T & { _id: string }) => void;\n onUpdate?: (doc: T & { _id: string }) => void;\n onDelete?: (documentId: string) => void;\n onError?: (error: Error) => void;\n}\n\nclass QueryBuilder<T = Record<string, unknown>> {\n private _filter?: Record<string, unknown>;\n private _sort?: Record<string, 1 | -1>;\n private _limit?: number;\n private _offset?: number;\n private _fields?: string[];\n private _populate: PopulateOption[] = [];\n\n constructor(\n private http: HttpClient,\n private basePath: string,\n filter?: Record<string, unknown>,\n ) {\n this._filter = filter;\n }\n\n sort(sort: Record<string, 1 | -1>): QueryBuilder<T> {\n this._sort = sort;\n return this;\n }\n\n limit(limit: number): QueryBuilder<T> {\n this._limit = limit;\n return this;\n }\n\n offset(offset: number): QueryBuilder<T> {\n this._offset = offset;\n return this;\n }\n\n select(fields: string[]): QueryBuilder<T> {\n this._fields = fields;\n return this;\n }\n\n populate(\n field: string,\n collection: string,\n foreignField?: string,\n ): QueryBuilder<T> {\n this._populate.push({ field, collection, foreignField });\n return this;\n }\n\n async execute(): Promise<FindResult<T>> {\n const query: Record<string, string | number | undefined> = {};\n if (this._filter) query['filter'] = JSON.stringify(this._filter);\n if (this._sort) query['sort'] = JSON.stringify(this._sort);\n if (this._limit !== undefined) query['limit'] = this._limit;\n if (this._offset !== undefined) query['offset'] = this._offset;\n if (this._fields) query['fields'] = this._fields.join(',');\n if (this._populate.length) {\n query['populate'] = this._populate\n .map((p) =>\n p.foreignField\n ? `${p.field}:${p.collection}:${p.foreignField}`\n : `${p.field}:${p.collection}`,\n )\n .join(',');\n }\n\n return this.http.request<FindResult<T>>({\n method: 'GET',\n path: this.basePath,\n query,\n authenticated: true,\n });\n }\n}\n\nclass CollectionRef<T = Record<string, unknown>> {\n private basePath: string;\n private collectionName: string;\n\n constructor(\n private http: HttpClient,\n private realtime: RealtimeClient | null,\n private projectId: string,\n collectionName: string,\n ) {\n this.collectionName = collectionName;\n this.basePath = `/db/${collectionName}`;\n }\n\n async insert(\n document: Partial<T> & { _id?: string },\n ): Promise<InsertResult> {\n return this.http.request<InsertResult>({\n method: 'POST',\n path: this.basePath,\n body: { documents: [document] },\n authenticated: true,\n });\n }\n\n async insertMany(\n documents: (Partial<T> & { _id?: string })[],\n ): Promise<InsertResult> {\n return this.http.request<InsertResult>({\n method: 'POST',\n path: this.basePath,\n body: { documents },\n authenticated: true,\n });\n }\n\n find(filter?: Record<string, unknown>): QueryBuilder<T> {\n return new QueryBuilder<T>(this.http, this.basePath, filter);\n }\n\n async findById(\n id: string,\n options?: FindByIdOptions,\n ): Promise<T & { _id: string }> {\n const query: Record<string, string> = {};\n if (options?.populate?.length) {\n query['populate'] = options.populate\n .map((p) =>\n p.foreignField\n ? `${p.field}:${p.collection}:${p.foreignField}`\n : `${p.field}:${p.collection}`,\n )\n .join(',');\n }\n\n return this.http.request<T & { _id: string }>({\n method: 'GET',\n path: `${this.basePath}/${id}`,\n query,\n authenticated: true,\n });\n }\n\n async update(id: string, update: Partial<T>): Promise<UpdateResult> {\n return this.http.request<UpdateResult>({\n method: 'PATCH',\n path: `${this.basePath}/${id}`,\n body: { update },\n authenticated: true,\n });\n }\n\n async delete(id: string): Promise<DeleteResult> {\n return this.http.request<DeleteResult>({\n method: 'DELETE',\n path: `${this.basePath}/${id}`,\n authenticated: true,\n });\n }\n\n async count(filter?: Record<string, unknown>): Promise<number> {\n const result = await this.http.request<CountResult>({\n method: 'POST',\n path: `${this.basePath}/count`,\n body: { filter },\n authenticated: true,\n });\n return result.count;\n }\n\n subscribe(handlers: SubscribeHandlers<T>): () => void {\n if (!this.realtime) {\n throw new Error('Realtime not available: no RealtimeClient configured');\n }\n\n let unsubscribeFn: (() => void) | null = null;\n let pendingUnsub = false;\n\n const callback = (event: DatabaseChangeEvent) => {\n switch (event.type) {\n case 'insert':\n if (handlers.onInsert && event.document) {\n handlers.onInsert(event.document as T & { _id: string });\n }\n break;\n case 'update':\n if (handlers.onUpdate && event.document) {\n handlers.onUpdate(event.document as T & { _id: string });\n }\n break;\n case 'delete':\n if (handlers.onDelete) {\n handlers.onDelete(event.documentId);\n }\n break;\n }\n };\n\n this.realtime\n .subscribe(this.projectId, this.collectionName, callback, handlers.onError, handlers.where)\n .then((unsub) => {\n if (pendingUnsub) {\n // Unsubscribe was called before async subscribe resolved\n unsub();\n } else {\n unsubscribeFn = unsub;\n }\n })\n .catch((err) => {\n if (handlers.onError) {\n handlers.onError(err instanceof Error ? err : new Error(String(err)));\n }\n });\n\n // Return synchronous unsubscribe\n return () => {\n if (unsubscribeFn) {\n unsubscribeFn();\n } else {\n pendingUnsub = true;\n }\n };\n }\n}\n\nexport class DatabaseModule {\n private http: HttpClient;\n private realtime: RealtimeClient | null;\n private projectId: string;\n\n constructor(http: HttpClient, projectId: string, realtime?: RealtimeClient) {\n this.http = http;\n this.projectId = projectId;\n this.realtime = realtime ?? null;\n }\n\n collection<T = Record<string, unknown>>(name: string): CollectionRef<T> {\n return new CollectionRef<T>(this.http, this.realtime, this.projectId, name);\n }\n}\n","import { HttpClient } from '../core';\n\nexport interface PushSubscriptionInfo {\n id: string;\n platform: 'web' | 'android' | 'ios';\n endpoint?: string;\n deviceToken?: string;\n deviceId?: string;\n deviceName?: string;\n userAgent?: string;\n isActive: boolean;\n lastUsedAt?: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface VapidKeyResponse {\n publicKey: string;\n}\n\nconst DEVICE_ID_KEY = 'spacelr_device_id';\n\nexport class NotificationsModule {\n private customDeviceId: string | null = null;\n private customDeviceName: string | null = null;\n\n constructor(private http: HttpClient) {}\n\n /** Set a custom device ID (e.g. from Capacitor Preferences for persistence beyond localStorage) */\n setDeviceId(id: string): void {\n this.customDeviceId = id;\n }\n\n /** Set a custom device name (e.g. \"iOS App\", \"macOS - Chrome\") */\n setDeviceName(name: string): void {\n this.customDeviceName = name;\n }\n\n /** Get or generate a stable device identifier. Custom ID takes priority over localStorage. */\n private getDeviceId(): string | undefined {\n if (this.customDeviceId) return this.customDeviceId;\n if (typeof localStorage === 'undefined') return undefined;\n let id = localStorage.getItem(DEVICE_ID_KEY);\n if (!id) {\n id = crypto.randomUUID();\n localStorage.setItem(DEVICE_ID_KEY, id);\n }\n return id;\n }\n\n /** Get device name: custom name > auto-detected from user agent > undefined */\n private getDeviceName(): string | undefined {\n if (this.customDeviceName) return this.customDeviceName;\n return this.detectDeviceName();\n }\n\n /** Auto-detect a short device label from navigator.userAgent (e.g. \"macOS - Chrome\") */\n private detectDeviceName(): string | undefined {\n if (typeof navigator === 'undefined' || !navigator.userAgent) return undefined;\n const ua = navigator.userAgent;\n\n let os = 'Unknown';\n if (/iPad|iPhone|iPod/.test(ua)) os = 'iOS';\n else if (/Android/.test(ua)) os = 'Android';\n else if (/Mac OS X/.test(ua)) os = 'macOS';\n else if (/Windows/.test(ua)) os = 'Windows';\n else if (/Linux/.test(ua)) os = 'Linux';\n\n let browser = 'Unknown';\n if (/Edg\\//.test(ua)) browser = 'Edge';\n else if (/OPR\\/|Opera/.test(ua)) browser = 'Opera';\n else if (/Chrome\\//.test(ua)) browser = 'Chrome';\n else if (/Safari\\//.test(ua) && !/Chrome/.test(ua)) browser = 'Safari';\n else if (/Firefox\\//.test(ua)) browser = 'Firefox';\n\n return `${os} - ${browser}`;\n }\n\n /** Get the VAPID public key for Web Push setup */\n async getVapidPublicKey(): Promise<VapidKeyResponse> {\n return this.http.request<VapidKeyResponse>({\n method: 'GET',\n path: '/notifications/vapid-key',\n authenticated: true,\n });\n }\n\n /** Register a push subscription (web, android, or ios) */\n async subscribe(\n subscription: {\n platform: 'web' | 'android' | 'ios';\n endpoint?: string;\n keys?: { p256dh: string; auth: string };\n deviceToken?: string;\n },\n deviceName?: string,\n ): Promise<PushSubscriptionInfo> {\n return this.http.request<PushSubscriptionInfo>({\n method: 'POST',\n path: '/notifications/subscribe',\n body: {\n ...subscription,\n deviceName: deviceName ?? this.getDeviceName(),\n deviceId: this.getDeviceId(),\n },\n authenticated: true,\n });\n }\n\n /** Unregister a push subscription */\n async unsubscribe(\n platform: 'web' | 'android' | 'ios',\n identifier: string,\n ): Promise<{ deleted: boolean }> {\n const body: Record<string, string> = { platform };\n if (platform === 'web') {\n body['endpoint'] = identifier;\n } else {\n body['deviceToken'] = identifier;\n }\n\n return this.http.request<{ deleted: boolean }>({\n method: 'DELETE',\n path: '/notifications/subscribe',\n body,\n authenticated: true,\n });\n }\n\n /** Get all subscriptions for the current user */\n async getSubscriptions(): Promise<PushSubscriptionInfo[]> {\n return this.http.request<PushSubscriptionInfo[]>({\n method: 'GET',\n path: '/notifications/subscriptions',\n authenticated: true,\n });\n }\n\n /**\n * Helper: Register browser Web Push subscription.\n * Requests notification permission, subscribes via Push API,\n * and registers the subscription with the server.\n */\n async registerBrowserPush(\n serviceWorkerRegistration: ServiceWorkerRegistration,\n deviceName?: string,\n ): Promise<PushSubscriptionInfo> {\n // Get VAPID key\n const { publicKey } = await this.getVapidPublicKey();\n\n if (!publicKey) {\n throw new Error(\n 'VAPID public key not configured on the server',\n );\n }\n\n // Request notification permission\n const permission = await Notification.requestPermission();\n if (permission !== 'granted') {\n throw new Error(\n `Notification permission denied: ${permission}`,\n );\n }\n\n // Convert VAPID key to Uint8Array\n const applicationServerKey = this.urlBase64ToUint8Array(publicKey);\n\n // Subscribe via Push API\n const pushSubscription =\n await serviceWorkerRegistration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey,\n });\n\n const subJson = pushSubscription.toJSON();\n const p256dh = subJson.keys?.['p256dh'];\n const auth = subJson.keys?.['auth'];\n\n if (!subJson.endpoint || !p256dh || !auth) {\n throw new Error('Invalid push subscription: missing endpoint or keys');\n }\n\n // Register with server\n return this.subscribe(\n {\n platform: 'web',\n endpoint: subJson.endpoint,\n keys: { p256dh, auth },\n },\n deviceName,\n );\n }\n\n /**\n * Helper: Register a mobile device push token (FCM or APNs).\n */\n async registerDevicePush(\n deviceToken: string,\n platform: 'android' | 'ios',\n deviceName?: string,\n ): Promise<PushSubscriptionInfo> {\n return this.subscribe(\n {\n platform,\n deviceToken,\n },\n deviceName,\n );\n }\n\n private urlBase64ToUint8Array(base64String: string): ArrayBuffer {\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding)\n .replace(/-/g, '+')\n .replace(/_/g, '/');\n\n const rawData = atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n return outputArray.buffer as ArrayBuffer;\n }\n}\n","import { SpacelrClientConfig } from './types';\nimport { HttpClient, TokenManager, MemoryTokenStorage, BrowserTokenStorage, RealtimeClient } from './core';\nimport { AuthModule, StorageModule, DatabaseModule, NotificationsModule } from './modules';\n\nexport interface SpacelrClient {\n readonly auth: AuthModule;\n readonly storage: StorageModule;\n readonly db: DatabaseModule;\n readonly notifications: NotificationsModule;\n /** Disconnect realtime WebSocket (if connected) */\n disconnect(): void;\n}\n\nexport function createClient(config: SpacelrClientConfig): SpacelrClient {\n const tokenStorage = config.tokenStorage\n ?? (typeof window !== 'undefined' && typeof window.localStorage !== 'undefined'\n ? new BrowserTokenStorage()\n : new MemoryTokenStorage());\n const tokenManager = new TokenManager(\n tokenStorage,\n config.refreshBufferSeconds ?? 60\n );\n const httpClient = new HttpClient(config, tokenManager);\n\n const realtime = new RealtimeClient({\n baseUrl: config.apiUrl,\n getToken: () => tokenManager.getAccessToken(),\n });\n\n const auth = new AuthModule(httpClient, tokenManager, config);\n const storage = new StorageModule(httpClient, tokenManager, config);\n const db = new DatabaseModule(httpClient, config.projectId, realtime);\n const notifications = new NotificationsModule(httpClient);\n\n return {\n auth,\n storage,\n db,\n notifications,\n disconnect() {\n realtime.disconnect();\n },\n };\n}\n"],"mappings":"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","names":["FileVisibility","GrantType","CodeChallengeMethod","SharePermission","hash","callbacks"]}
1
+ {"version":3,"sources":["../../../../libs/sdk/src/core/errors.ts","../../../../libs/sdk/src/core/http-client.ts","../../../../libs/sdk/src/core/token-storage.ts","../../../../libs/sdk/src/core/token-manager.ts","../../../../libs/sdk/src/types/common.ts","../../../../libs/sdk/src/core/pkce.ts","../../../../libs/sdk/src/core/realtime.ts","../../../../libs/sdk/src/modules/auth.module.ts","../../../../libs/sdk/src/modules/storage.module.ts","../../../../libs/sdk/src/modules/database.module.ts","../../../../libs/sdk/src/modules/notifications.module.ts","../../../../libs/sdk/src/modules/functions.module.ts","../../../../libs/sdk/src/client.ts"],"sourcesContent":["export class SpacelrError extends Error {\n readonly code: string;\n readonly statusCode?: number;\n readonly details?: Record<string, unknown>;\n\n constructor(\n message: string,\n code: string,\n statusCode?: number,\n details?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'SpacelrError';\n this.code = code;\n this.statusCode = statusCode;\n this.details = details;\n }\n}\n\nexport class SpacelrAuthError extends SpacelrError {\n constructor(\n message: string,\n statusCode = 401,\n details?: Record<string, unknown>\n ) {\n super(message, 'AUTH_ERROR', statusCode, details);\n this.name = 'SpacelrAuthError';\n }\n}\n\nexport class SpacelrNetworkError extends SpacelrError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'NETWORK_ERROR', undefined, details);\n this.name = 'SpacelrNetworkError';\n }\n}\n\nexport class SpacelrTimeoutError extends SpacelrError {\n constructor(timeoutMs: number) {\n super(\n `Request timed out after ${timeoutMs}ms`,\n 'TIMEOUT_ERROR',\n undefined,\n { timeoutMs }\n );\n this.name = 'SpacelrTimeoutError';\n }\n}\n\nexport class SpacelrTwoFactorRequiredError extends SpacelrError {\n readonly twoFactorToken: string;\n\n constructor(twoFactorToken: string, details?: Record<string, unknown>) {\n super(\n 'Two-factor authentication required',\n 'TWO_FACTOR_REQUIRED',\n 200,\n details\n );\n this.name = 'SpacelrTwoFactorRequiredError';\n this.twoFactorToken = twoFactorToken;\n }\n}\n\nexport class SpacelrEmailVerificationRequiredError extends SpacelrError {\n readonly emailSent: boolean;\n\n constructor(emailSent: boolean, details?: Record<string, unknown>) {\n super(\n emailSent\n ? 'Email verification required. A new verification email has been sent.'\n : 'Email verification required. Please check your inbox for the verification email.',\n 'EMAIL_VERIFICATION_REQUIRED',\n 200,\n details\n );\n this.name = 'SpacelrEmailVerificationRequiredError';\n this.emailSent = emailSent;\n }\n}\n","import { SpacelrClientConfig, ApiResponse } from '../types';\nimport {\n SpacelrError,\n SpacelrAuthError,\n SpacelrNetworkError,\n SpacelrTimeoutError,\n SpacelrTwoFactorRequiredError,\n SpacelrEmailVerificationRequiredError,\n} from './errors';\nimport { TokenManager } from './token-manager';\n\nexport interface HttpRequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n path: string;\n body?: unknown;\n authenticated?: boolean;\n headers?: Record<string, string>;\n query?: Record<string, string | number | undefined>;\n /** Send cookies cross-origin (credentials: 'include'). Defaults to true for /auth/ paths. */\n withCredentials?: boolean;\n}\n\nexport class HttpClient {\n private config: SpacelrClientConfig;\n private tokenManager: TokenManager;\n\n constructor(config: SpacelrClientConfig, tokenManager: TokenManager) {\n this.config = config;\n this.tokenManager = tokenManager;\n }\n\n async request<T>(options: HttpRequestOptions): Promise<T> {\n return this.requestWithRetry<T>(options, false);\n }\n\n private async requestWithRetry<T>(\n options: HttpRequestOptions,\n isRetry: boolean,\n ): Promise<T> {\n const url = this.buildUrl(options.path, options.query);\n const headers = await this.buildHeaders(options);\n const timeout = this.config.timeout ?? 30000;\n const includeCredentials = options.withCredentials ?? options.path.startsWith('/auth/');\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method: options.method,\n headers,\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n ...(includeCredentials && { credentials: 'include' as RequestCredentials }),\n });\n\n const responseBody = await this.parseResponse(response);\n\n if (!response.ok) {\n if (options.authenticated && response.status === 401) {\n if (await this.recoverFromAuthFailure(isRetry)) {\n return this.requestWithRetry<T>(options, true);\n }\n }\n this.throwHttpError(response.status, responseBody);\n }\n\n return this.extractData<T>(responseBody);\n } catch (error) {\n if (error instanceof SpacelrError) throw error;\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n throw new SpacelrTimeoutError(timeout);\n }\n\n throw new SpacelrNetworkError(\n error instanceof Error ? error.message : 'Network request failed'\n );\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async uploadForm<T>(\n path: string,\n formData: FormData,\n onProgress?: (event: { loaded: number; total: number }) => void,\n ): Promise<T> {\n return this.uploadFormWithRetry<T>(path, formData, onProgress, false);\n }\n\n private async uploadFormWithRetry<T>(\n path: string,\n formData: FormData,\n onProgress: ((event: { loaded: number; total: number }) => void) | undefined,\n isRetry: boolean,\n ): Promise<T> {\n const url = this.buildUrl(path);\n const headers = await this.buildFormHeaders();\n const timeoutMs = this.config.timeout ?? 30000;\n\n try {\n if (onProgress) {\n return await this.uploadFormWithProgress<T>(url, headers, formData, onProgress, timeoutMs);\n }\n return await this.uploadFormOnce<T>(url, headers, formData, timeoutMs);\n } catch (error) {\n if (error instanceof SpacelrAuthError && error.statusCode === 401) {\n if (await this.recoverFromAuthFailure(isRetry)) {\n return this.uploadFormWithRetry<T>(path, formData, onProgress, true);\n }\n }\n throw error;\n }\n }\n\n /**\n * Shared handler for a 401 on an authenticated request. Returns true if the\n * caller should retry with a refreshed token. 403 is never routed here —\n * it means \"forbidden for this action\" and must not trigger logout.\n *\n * `emitAuthLost('unauthorized')` is deduped inside TokenManager, so it's a\n * no-op if `executeRefresh` already emitted 'refresh-failed'.\n */\n private async recoverFromAuthFailure(isRetry: boolean): Promise<boolean> {\n if (isRetry) {\n this.tokenManager.emitAuthLost('unauthorized');\n return false;\n }\n const refreshed = await this.tokenManager.forceRefresh();\n if (refreshed) return true;\n this.tokenManager.emitAuthLost('unauthorized');\n return false;\n }\n\n private async uploadFormOnce<T>(\n url: string,\n headers: Record<string, string>,\n formData: FormData,\n timeoutMs: number,\n ): Promise<T> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: formData,\n signal: controller.signal,\n });\n\n const responseBody = await this.parseResponse(response);\n\n if (!response.ok) {\n this.throwHttpError(response.status, responseBody);\n }\n\n return this.extractData<T>(responseBody);\n } catch (error) {\n if (error instanceof SpacelrError) throw error;\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n throw new SpacelrTimeoutError(timeoutMs);\n }\n\n throw new SpacelrNetworkError(\n error instanceof Error ? error.message : 'Network request failed'\n );\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private uploadFormWithProgress<T>(\n url: string,\n headers: Record<string, string>,\n formData: FormData,\n onProgress: (event: { loaded: number; total: number }) => void,\n timeoutMs: number,\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open('POST', url);\n xhr.timeout = timeoutMs;\n\n for (const [key, value] of Object.entries(headers)) {\n xhr.setRequestHeader(key, value);\n }\n\n xhr.upload.addEventListener('progress', (e) => {\n if (e.lengthComputable) {\n onProgress({ loaded: e.loaded, total: e.total });\n }\n });\n\n xhr.addEventListener('load', () => {\n try {\n const contentType = xhr.getResponseHeader('content-type') ?? '';\n if (!contentType.includes('application/json')) {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve({ success: true, data: xhr.responseText } as unknown as T);\n } else if (xhr.status === 401 || xhr.status === 403) {\n // Surface as SpacelrAuthError for consistent typing with the\n // JSON path below. Only 401 actually triggers the upload retry\n // wrapper (uploadFormWithRetry filters on statusCode === 401);\n // 403 propagates unchanged as a permission denial.\n reject(new SpacelrAuthError(`HTTP ${xhr.status}`, xhr.status));\n } else {\n reject(new SpacelrNetworkError(`HTTP ${xhr.status}: ${xhr.responseText.slice(0, 200)}`));\n }\n return;\n }\n const body = JSON.parse(xhr.responseText);\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve(this.extractData<T>(body));\n } else {\n this.throwHttpError(xhr.status, body);\n }\n } catch (error) {\n if (error instanceof SpacelrError) {\n reject(error);\n } else {\n reject(new SpacelrNetworkError('Failed to parse response'));\n }\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new SpacelrNetworkError('Network request failed'));\n });\n\n xhr.addEventListener('timeout', () => {\n xhr.abort();\n reject(new SpacelrTimeoutError(timeoutMs));\n });\n\n xhr.send(formData);\n });\n }\n\n private buildUrl(\n path: string,\n query?: Record<string, string | number | undefined>\n ): string {\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n // .well-known endpoints are served at the origin without the API prefix\n const base = cleanPath.startsWith('/.well-known')\n ? new URL(this.config.apiUrl).origin\n : this.config.apiUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}${cleanPath}`);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n }\n\n private async buildHeaders(\n options: HttpRequestOptions\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'x-client-id': this.config.clientId,\n 'x-project-id': this.config.projectId,\n ...options.headers,\n };\n\n if (options.authenticated) {\n const token = await this.tokenManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n }\n\n return headers;\n }\n\n private async buildFormHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'x-client-id': this.config.clientId,\n 'x-project-id': this.config.projectId,\n };\n\n const token = await this.tokenManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n return headers;\n }\n\n private async parseResponse(\n response: Response\n ): Promise<ApiResponse | Record<string, unknown>> {\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('application/json')) {\n return response.json();\n }\n const text = await response.text();\n return { success: response.ok, data: text };\n }\n\n private throwHttpError(\n statusCode: number,\n body: ApiResponse | Record<string, unknown>\n ): never {\n const apiBody = body as ApiResponse;\n const message =\n apiBody.error?.message ?? `HTTP ${statusCode}`;\n const code = apiBody.error?.code ?? `HTTP_${statusCode}`;\n const details = apiBody.error?.details;\n\n if (statusCode === 401 || statusCode === 403) {\n throw new SpacelrAuthError(message, statusCode, details);\n }\n\n throw new SpacelrError(message, code, statusCode, details);\n }\n\n private extractData<T>(body: ApiResponse | Record<string, unknown>): T {\n const apiBody = body as ApiResponse;\n // If the response wraps data in a standard envelope, extract it\n if ('success' in apiBody && apiBody.data !== undefined) {\n const data = apiBody.data as Record<string, unknown>;\n // Check if the response indicates email verification is required\n if (data['emailVerificationRequired'] === true) {\n throw new SpacelrEmailVerificationRequiredError(data['emailSent'] === true);\n }\n // Check if the response indicates a 2FA challenge\n if (data['twoFactorRequired'] === true && typeof data['twoFactorToken'] === 'string') {\n throw new SpacelrTwoFactorRequiredError(data['twoFactorToken']);\n }\n return apiBody.data as T;\n }\n // Check unwrapped responses\n const rawBody = body as Record<string, unknown>;\n if (rawBody['emailVerificationRequired'] === true) {\n throw new SpacelrEmailVerificationRequiredError(rawBody['emailSent'] === true);\n }\n if (rawBody['twoFactorRequired'] === true && typeof rawBody['twoFactorToken'] === 'string') {\n throw new SpacelrTwoFactorRequiredError(rawBody['twoFactorToken']);\n }\n // Otherwise return the whole body (e.g. OIDC endpoints return raw objects)\n return body as T;\n }\n}\n","import { StoredTokens, TokenStorage } from '../types';\n\nexport type { TokenStorage };\n\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: StoredTokens | null = null;\n\n async getTokens(): Promise<StoredTokens | null> {\n return this.tokens;\n }\n\n async setTokens(tokens: StoredTokens): Promise<void> {\n this.tokens = tokens;\n }\n\n async clearTokens(): Promise<void> {\n this.tokens = null;\n }\n}\n\nexport class BrowserTokenStorage implements TokenStorage {\n private readonly storageKey: string;\n\n constructor(storageKey = 'spacelr_tokens') {\n this.storageKey = storageKey;\n }\n\n async getTokens(): Promise<StoredTokens | null> {\n try {\n const raw = localStorage.getItem(this.storageKey);\n if (!raw) return null;\n return JSON.parse(raw) as StoredTokens;\n } catch {\n return null;\n }\n }\n\n async setTokens(tokens: StoredTokens): Promise<void> {\n try {\n localStorage.setItem(this.storageKey, JSON.stringify(tokens));\n } catch {\n // Quota exceeded or private browsing — silently ignore\n }\n }\n\n async clearTokens(): Promise<void> {\n try {\n localStorage.removeItem(this.storageKey);\n } catch {\n // localStorage unavailable — silently ignore\n }\n }\n}\n","import { StoredTokens, TokenStorage } from '../types';\nimport { MemoryTokenStorage } from './token-storage';\n\nexport type RefreshCallback = (refreshToken: string) => Promise<StoredTokens>;\nexport type AuthLostReason = 'refresh-failed' | 'unauthorized';\nexport type TokenRefreshedListener = (tokens: StoredTokens) => void;\nexport type AuthLostListener = (reason: AuthLostReason) => void;\n\nexport class TokenManager {\n private storage: TokenStorage;\n private refreshBufferSeconds: number;\n private refreshCallback: RefreshCallback | null = null;\n private refreshPromise: Promise<StoredTokens> | null = null;\n private tokenRefreshedListeners = new Set<TokenRefreshedListener>();\n private authLostListeners = new Set<AuthLostListener>();\n // Guard so callbacks that run during auth-loss handling (e.g. a logout()\n // that hits `/auth/logout` with a dead token) don't re-emit and loop.\n // Cleared by setTokens() / clearTokens() to re-arm for the next session.\n private authLostEmitted = false;\n\n constructor(storage?: TokenStorage, refreshBufferSeconds = 60) {\n this.storage = storage ?? new MemoryTokenStorage();\n this.refreshBufferSeconds = refreshBufferSeconds;\n }\n\n setRefreshCallback(callback: RefreshCallback): void {\n this.refreshCallback = callback;\n }\n\n async getAccessToken(): Promise<string | null> {\n const tokens = await this.storage.getTokens();\n if (!tokens) return null;\n\n if (this.isTokenExpired(tokens)) {\n const refreshed = await this.tryRefresh(tokens);\n return refreshed?.accessToken ?? null;\n }\n\n if (this.shouldRefresh(tokens)) {\n // Trigger background refresh but return current token\n this.tryRefresh(tokens).catch(() => {\n // Ignore background refresh failures (onAuthLost will have fired)\n });\n }\n\n return tokens.accessToken;\n }\n\n async setTokens(tokens: StoredTokens): Promise<void> {\n await this.storage.setTokens(tokens);\n this.authLostEmitted = false;\n }\n\n async clearTokens(): Promise<void> {\n await this.storage.clearTokens();\n this.refreshPromise = null;\n this.authLostEmitted = false;\n }\n\n async getStoredTokens(): Promise<StoredTokens | null> {\n return this.storage.getTokens();\n }\n\n /**\n * Force a refresh using the current stored refresh token.\n * Returns the new access token, or null if refresh is not possible or fails.\n * A rejecting custom `TokenStorage.getTokens()` is treated as \"no tokens\"\n * so callers (notably `HttpClient`'s 401 retry path) see a clean `null`\n * rather than an exception that would get wrapped as a network error.\n */\n async forceRefresh(): Promise<string | null> {\n // Once auth is lost, further refresh attempts are pointless and would\n // spam the refresh endpoint while the consumer is handling the logout.\n if (this.authLostEmitted) return null;\n let tokens: StoredTokens | null;\n try {\n tokens = await this.storage.getTokens();\n } catch {\n return null;\n }\n if (!tokens) return null;\n const refreshed = await this.tryRefresh(tokens);\n return refreshed?.accessToken ?? null;\n }\n\n onTokenRefreshed(listener: TokenRefreshedListener): () => void {\n this.tokenRefreshedListeners.add(listener);\n return () => this.tokenRefreshedListeners.delete(listener);\n }\n\n onAuthLost(listener: AuthLostListener): () => void {\n this.authLostListeners.add(listener);\n return () => this.authLostListeners.delete(listener);\n }\n\n emitAuthLost(reason: AuthLostReason): void {\n if (this.authLostEmitted) return;\n this.authLostEmitted = true;\n for (const listener of this.authLostListeners) {\n try {\n listener(reason);\n } catch {\n // Ignore listener errors\n }\n }\n }\n\n private isTokenExpired(tokens: StoredTokens): boolean {\n if (!tokens.expiresAt) return false;\n return Date.now() >= tokens.expiresAt * 1000;\n }\n\n private shouldRefresh(tokens: StoredTokens): boolean {\n if (!tokens.expiresAt || !tokens.refreshToken) return false;\n const bufferMs = this.refreshBufferSeconds * 1000;\n return Date.now() >= tokens.expiresAt * 1000 - bufferMs;\n }\n\n private async tryRefresh(\n tokens: StoredTokens\n ): Promise<StoredTokens | null> {\n if (!tokens.refreshToken || !this.refreshCallback) return null;\n\n // Deduplicate concurrent refresh calls — both the leader and any followers\n // must get `null` on failure, not a rejected promise. Otherwise parallel\n // callers of getAccessToken/forceRefresh will throw unexpectedly while the\n // leader gracefully returns null.\n if (this.refreshPromise) {\n try {\n return await this.refreshPromise;\n } catch {\n return null;\n }\n }\n\n this.refreshPromise = this.executeRefresh(tokens.refreshToken);\n\n try {\n return await this.refreshPromise;\n } catch {\n return null;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async executeRefresh(refreshToken: string): Promise<StoredTokens> {\n // `tryRefresh` guards against a missing callback before reaching here.\n const callback = this.refreshCallback as RefreshCallback;\n try {\n const newTokens = await callback(refreshToken);\n await this.storage.setTokens(newTokens);\n this.emitTokenRefreshed(newTokens);\n return newTokens;\n } catch (error) {\n this.emitAuthLost('refresh-failed');\n throw error;\n }\n }\n\n private emitTokenRefreshed(tokens: StoredTokens): void {\n for (const listener of this.tokenRefreshedListeners) {\n try {\n listener(tokens);\n } catch {\n // Ignore listener errors\n }\n }\n }\n}\n","export enum FileVisibility {\n PRIVATE = 'private',\n SHARED = 'shared',\n PUBLIC = 'public',\n}\n\nexport enum GrantType {\n AUTHORIZATION_CODE = 'authorization_code',\n CLIENT_CREDENTIALS = 'client_credentials',\n REFRESH_TOKEN = 'refresh_token',\n}\n\nexport enum CodeChallengeMethod {\n PLAIN = 'plain',\n S256 = 'S256',\n}\n\nexport enum SharePermission {\n READ = 'read',\n WRITE = 'write',\n}\n\nexport interface ApiResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: {\n message: string;\n code: string;\n details?: Record<string, unknown>;\n };\n}\n","import { PKCEChallenge, CodeChallengeMethod } from '../types';\n\nfunction generateRandomBytes(length: number): Uint8Array {\n if (\n typeof globalThis.crypto !== 'undefined' &&\n globalThis.crypto.getRandomValues\n ) {\n const bytes = new Uint8Array(length);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n }\n\n // Node.js fallback\n const nodeCrypto = require('crypto') as typeof import('crypto');\n return new Uint8Array(nodeCrypto.randomBytes(length));\n}\n\nfunction base64UrlEncode(buffer: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < buffer.length; i++) {\n binary += String.fromCharCode(buffer[i]);\n }\n\n const base64 = btoa(binary);\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nasync function sha256(input: string): Promise<Uint8Array> {\n if (\n typeof globalThis.crypto !== 'undefined' &&\n globalThis.crypto.subtle\n ) {\n const encoder = new TextEncoder();\n const data = encoder.encode(input);\n const hash = await globalThis.crypto.subtle.digest('SHA-256', data);\n return new Uint8Array(hash);\n }\n\n // Node.js fallback\n const nodeCrypto = require('crypto') as typeof import('crypto');\n const hash = nodeCrypto.createHash('sha256').update(input).digest();\n return new Uint8Array(hash);\n}\n\nexport async function generatePKCEChallenge(): Promise<PKCEChallenge> {\n const randomBytes = generateRandomBytes(32);\n const codeVerifier = base64UrlEncode(randomBytes);\n\n const hashBytes = await sha256(codeVerifier);\n const codeChallenge = base64UrlEncode(hashBytes);\n\n return {\n codeVerifier,\n codeChallenge,\n codeChallengeMethod: CodeChallengeMethod.S256,\n };\n}\n","import { io, Socket } from 'socket.io-client';\n\n// Wait this long before retrying a failed socket rebuild. Short enough that\n// a transient network stumble heals without waiting for a user-visible wake\n// event, long enough not to hammer an unreachable server.\nconst REBUILD_RETRY_DELAY_MS = 5000;\n\nexport type ConnectionState = 'connected' | 'reconnecting' | 'disconnected';\n\n// NOTE: This interface is duplicated in @spacelr-workspace/shared-types (database-events.ts).\n// The SDK cannot import from shared-types since it ships as a standalone npm package.\n// Keep both definitions in sync when making changes.\nexport interface DatabaseChangeEvent {\n type: 'insert' | 'update' | 'delete';\n projectId: string;\n collectionName: string;\n documentId: string;\n document?: Record<string, unknown>;\n timestamp: number;\n}\n\nexport interface RealtimeConfig {\n baseUrl: string;\n getToken: () => Promise<string | null>;\n /**\n * Optional subscription to token-refresh events. When wired, the realtime\n * client will push the fresh access token to the server via a\n * `reauthenticate` message so the open socket stays authenticated without\n * dropping the connection.\n */\n onTokenRefreshed?: (listener: (accessToken: string) => void) => () => void;\n}\n\nexport class RealtimeClient {\n private socket: Socket | null = null;\n private config: RealtimeConfig;\n private subscriptions = new Map<string, Set<(event: DatabaseChangeEvent) => void>>();\n private connecting: Promise<void> | null = null;\n // Store original where objects per room for reconnect resubscription\n private roomWhereMap = new Map<string, Record<string, string | number | boolean>>();\n private unsubscribeFromTokenRefreshed: (() => void) | null = null;\n // Wake-up listeners (browser only) — recover from long OS suspends where\n // socket.io's internal reconnect loop may have already given up.\n private onVisibilityChange: (() => void) | null = null;\n private onOnline: (() => void) | null = null;\n // Set by `disconnect()`; checked by async paths (rebuildSocket, deferred\n // retries) to avoid reviving a client the consumer has torn down.\n private disposed = false;\n // Scheduled retry after a failed `rebuildSocket()`; cleared when it fires\n // or when `disconnect()` tears down.\n private rebuildRetryTimer: ReturnType<typeof setTimeout> | null = null;\n private connectionState: ConnectionState = 'disconnected';\n private connectionStateListeners = new Set<(state: ConnectionState) => void>();\n\n constructor(config: RealtimeConfig) {\n this.config = config;\n }\n\n async subscribe(\n projectId: string,\n collectionName: string,\n callback: (event: DatabaseChangeEvent) => void,\n onError?: (error: Error) => void,\n where?: Record<string, string | number | boolean>,\n ): Promise<() => void> {\n this.ensureWakeListeners();\n if (this.connectionState === 'disconnected') {\n this.setConnectionState('reconnecting');\n }\n await this.ensureConnected();\n\n const room = this.buildRoomKey(projectId, collectionName, where);\n\n // Track the callback\n if (!this.subscriptions.has(room)) {\n this.subscriptions.set(room, new Set());\n }\n const callbacks = this.subscriptions.get(room);\n callbacks?.add(callback);\n\n // If this is the first subscription for this room, tell the server\n if (callbacks?.size === 1) {\n if (where && Object.keys(where).length > 0) {\n this.roomWhereMap.set(room, where);\n }\n const payload: Record<string, unknown> = { projectId, collectionName };\n if (where && Object.keys(where).length > 0) {\n payload['where'] = where;\n }\n this.socket?.emit(\n 'subscribe',\n payload,\n (response: { subscribed?: string; error?: string }) => {\n if (response.error && onError) {\n onError(new Error(response.error));\n }\n },\n );\n }\n\n // Return unsubscribe function\n return () => {\n const callbacks = this.subscriptions.get(room);\n if (callbacks) {\n callbacks.delete(callback);\n if (callbacks.size === 0) {\n this.subscriptions.delete(room);\n this.roomWhereMap.delete(room);\n const payload: Record<string, unknown> = { projectId, collectionName };\n if (where && Object.keys(where).length > 0) {\n payload['where'] = where;\n }\n this.socket?.emit('unsubscribe', payload);\n }\n }\n };\n }\n\n /**\n * Tear down the realtime client permanently. After this call the instance\n * is disposed — subsequent `subscribe()` calls will not re-establish a\n * connection. Create a new `RealtimeClient` (via `createClient`) if you\n * need to reconnect after a logout.\n */\n disconnect(): void {\n this.setConnectionState('disconnected');\n this.disposed = true;\n if (this.rebuildRetryTimer) {\n clearTimeout(this.rebuildRetryTimer);\n this.rebuildRetryTimer = null;\n }\n if (this.unsubscribeFromTokenRefreshed) {\n this.unsubscribeFromTokenRefreshed();\n this.unsubscribeFromTokenRefreshed = null;\n }\n this.detachWakeListeners();\n if (this.socket) {\n this.socket.disconnect();\n this.socket = null;\n }\n this.subscriptions.clear();\n this.roomWhereMap.clear();\n this.connecting = null;\n }\n\n getConnectionState(): ConnectionState {\n return this.connectionState;\n }\n\n onConnectionStateChanged(listener: (state: ConnectionState) => void): () => void {\n this.connectionStateListeners.add(listener);\n return () => this.connectionStateListeners.delete(listener);\n }\n\n private setConnectionState(next: ConnectionState): void {\n if (this.connectionState === next) return;\n this.connectionState = next;\n for (const listener of this.connectionStateListeners) {\n try {\n listener(next);\n } catch {\n // Ignore listener errors, matching onTokenRefreshed / onAuthLost.\n }\n }\n }\n\n private buildRoomKey(\n projectId: string,\n collectionName: string,\n where?: Record<string, string | number | boolean>,\n ): string {\n const base = `db:${projectId}:${collectionName}`;\n if (!where || Object.keys(where).length === 0) {\n return base;\n }\n const filterStr = Object.keys(where)\n .sort()\n .map((k) => `${k}=${String(where[k])}`)\n .join('&');\n return `${base}?${filterStr}`;\n }\n\n private async ensureConnected(): Promise<void> {\n if (this.socket?.connected) return;\n\n if (this.connecting) {\n return this.connecting;\n }\n\n this.connecting = this.connect();\n try {\n await this.connecting;\n } finally {\n this.connecting = null;\n }\n }\n\n private async connect(): Promise<void> {\n const token = await this.config.getToken();\n if (!token) {\n throw new Error('No authentication token available');\n }\n\n // Derive WebSocket URL from the API URL\n const wsUrl = this.config.baseUrl.replace(/\\/api\\/v\\d+\\/?$/, '');\n\n return new Promise<void>((resolve, reject) => {\n let initialConnect = true;\n\n this.socket = io(`${wsUrl}/database`, {\n auth: async (cb: (data: Record<string, unknown>) => void) => {\n try {\n const freshToken = await this.config.getToken();\n cb({ token: freshToken });\n } catch {\n cb({ token: null });\n }\n },\n transports: ['websocket'],\n reconnection: false, // Disabled until first successful connect\n });\n\n this.socket.on('authenticated', () => {\n // If the consumer called disconnect() while an in-flight authenticate\n // was in progress, don't emit a spurious `connected` on a disposed\n // client. socket.io can deliver a queued event after the socket was\n // detached — the `this.socket = null` and disposed flag are set but\n // the handler is still bound to the old socket instance.\n if (this.disposed) return;\n this.setConnectionState('connected');\n if (initialConnect) {\n initialConnect = false;\n // Enable reconnection after first successful connect\n if (this.socket) {\n this.socket.io.opts.reconnection = true;\n this.socket.io.opts.reconnectionDelay = 1000;\n this.socket.io.opts.reconnectionDelayMax = 5000;\n this.socket.io.opts.reconnectionAttempts = 50;\n }\n // Wire up token-refresh → reauthenticate so the open socket picks up\n // new JWTs without disconnecting.\n if (this.config.onTokenRefreshed && !this.unsubscribeFromTokenRefreshed) {\n this.unsubscribeFromTokenRefreshed = this.config.onTokenRefreshed(\n (accessToken) => {\n this.socket?.emit('reauthenticate', { token: accessToken });\n },\n );\n }\n resolve();\n } else {\n // Reconnect path — server has now set socket.data.userId. It is\n // safe to re-subscribe. (Doing this on 'connect' races the async\n // handleConnection and produces silent \"Not authenticated\" errors.)\n this.resubscribeAll();\n }\n });\n\n this.socket.on('connect_error', (err) => {\n if (initialConnect) {\n reject(new Error(`WebSocket connection failed: ${err.message}`));\n }\n });\n\n // socket.io has exhausted its reconnection attempts (e.g. after a long\n // laptop suspend where all retries burned in a few seconds on resume).\n // Build a fresh socket so the session can recover.\n this.socket.io.on('reconnect_failed', () => {\n void this.rebuildSocket();\n });\n\n this.socket.on('db:event', (event: DatabaseChangeEvent) => {\n const base = `db:${event.projectId}:${event.collectionName}`;\n\n // Dispatch to whole-collection subscribers via direct Map lookup\n const baseCallbacks = this.subscriptions.get(base);\n if (baseCallbacks) {\n for (const cb of baseCallbacks) {\n try { cb(event); } catch { /* ignore callback errors */ }\n }\n }\n\n // Dispatch to matching filtered subscriptions\n for (const [room, callbacks] of this.subscriptions) {\n if (room.startsWith(`${base}?`) && this.eventMatchesRoom(event, room)) {\n for (const cb of callbacks) {\n try { cb(event); } catch { /* ignore callback errors */ }\n }\n }\n }\n });\n\n this.socket.on('disconnect', () => {\n if (!this.disposed) {\n this.setConnectionState('reconnecting');\n }\n });\n });\n }\n\n private eventMatchesRoom(event: DatabaseChangeEvent, room: string): boolean {\n const base = `db:${event.projectId}:${event.collectionName}`;\n if (room === base) return true;\n if (!room.startsWith(`${base}?`)) return false;\n\n // For filtered rooms, check the document against the where filter\n const where = this.roomWhereMap.get(room);\n if (!where) return true; // No filter stored for this room\n if (!event.document) return false; // Delete events without document can't match a filter\n\n for (const [key, value] of Object.entries(where)) {\n const docValue = event.document[key];\n if (Array.isArray(docValue)) {\n if (!docValue.some((item) => String(item) === String(value))) {\n return false;\n }\n } else if (String(docValue) !== String(value)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Tear down the current socket and create a fresh one. Used by both\n * `reconnect_failed` (socket.io gave up) and the visibility/online wake-up\n * listeners. Preserves the `subscriptions` map so existing rooms can be\n * replayed to the server on the new connection. If the rebuild itself\n * fails it schedules one retry after 5 s — beyond that we rely on the\n * next wake-up event (visibility/online) to try again.\n */\n private async rebuildSocket(): Promise<void> {\n if (this.disposed || this.connecting) return;\n\n const previous = this.socket;\n this.socket = null;\n if (previous) previous.disconnect();\n\n this.setConnectionState('reconnecting');\n\n try {\n await this.ensureConnected();\n this.handlePostRebuild();\n } catch {\n this.scheduleRebuildRetry();\n }\n }\n\n private handlePostRebuild(): void {\n // Capture into a local first — TS narrows `this.socket` to `null` after\n // the earlier assignment and doesn't re-widen through the await above.\n const newSocket: Socket | null = this.socket;\n if (this.disposed) {\n this.socket = null;\n newSocket?.disconnect();\n return;\n }\n // The server has no record of the subscriptions we held on the old\n // (dead) socket, so replay them here. The new socket's `authenticated`\n // handler took the `initialConnect = true` branch and doesn't resubscribe.\n if (this.subscriptions.size > 0) {\n this.resubscribeAll();\n }\n }\n\n private scheduleRebuildRetry(): void {\n // A transient rebuild failure (token fetch rejected, connect_error) —\n // retry once so recovery doesn't stall waiting for a wake event.\n if (this.disposed || this.rebuildRetryTimer) return;\n this.rebuildRetryTimer = setTimeout(() => {\n this.rebuildRetryTimer = null;\n void this.rebuildSocket();\n }, REBUILD_RETRY_DELAY_MS);\n (this.rebuildRetryTimer as unknown as { unref?: () => void }).unref?.();\n }\n\n private ensureWakeListeners(): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return;\n if (this.onVisibilityChange !== null) return;\n\n const wakeIfUnhealthy = () => {\n if (typeof document !== 'undefined' && document.visibilityState !== 'visible') return;\n if (this.socket?.connected) return;\n if (this.connecting) return;\n void this.rebuildSocket();\n };\n\n this.onVisibilityChange = wakeIfUnhealthy;\n this.onOnline = wakeIfUnhealthy;\n document.addEventListener('visibilitychange', this.onVisibilityChange);\n window.addEventListener('online', this.onOnline);\n }\n\n private detachWakeListeners(): void {\n if (typeof document !== 'undefined' && this.onVisibilityChange) {\n document.removeEventListener('visibilitychange', this.onVisibilityChange);\n }\n if (typeof window !== 'undefined' && this.onOnline) {\n window.removeEventListener('online', this.onOnline);\n }\n this.onVisibilityChange = null;\n this.onOnline = null;\n }\n\n private resubscribeAll(): void {\n for (const [room] of this.subscriptions) {\n // Parse room format: db:{projectId}:{collectionName} or db:{projectId}:{collectionName}?filter\n const queryIdx = room.indexOf('?');\n const basePart = queryIdx >= 0 ? room.substring(0, queryIdx) : room;\n const parts = basePart.split(':');\n if (parts.length >= 3) {\n const projectId = parts[1];\n const collectionName = parts.slice(2).join(':');\n const payload: Record<string, unknown> = { projectId, collectionName };\n\n // Use stored where object to preserve original types (number, boolean)\n const where = this.roomWhereMap.get(room);\n if (where) {\n payload['where'] = where;\n }\n\n this.socket?.emit(\n 'subscribe',\n payload,\n (response: { subscribed?: string; error?: string }) => {\n if (response?.error) {\n // Server rejected resubscription — clean up the stale subscription\n this.subscriptions.delete(room);\n this.roomWhereMap.delete(room);\n }\n },\n );\n }\n }\n }\n}\n","import { HttpClient, TokenManager, generatePKCEChallenge } from '../core';\nimport {\n SpacelrClientConfig,\n GrantType,\n LoginParams,\n LoginResponse,\n RegisterParams,\n RegisterResponse,\n TokenResponse,\n UserInfo,\n UserProfile,\n AuthorizationUrlParams,\n ExchangeCodeParams,\n PKCEChallenge,\n OpenIDConfiguration,\n JWKSResponse,\n TwoFactorVerifyParams,\n} from '../types';\n\nexport class AuthModule {\n private http: HttpClient;\n private tokenManager: TokenManager;\n private config: SpacelrClientConfig;\n\n constructor(\n http: HttpClient,\n tokenManager: TokenManager,\n config: SpacelrClientConfig\n ) {\n this.http = http;\n this.tokenManager = tokenManager;\n this.config = config;\n\n // Wire up refresh callback to avoid circular deps\n this.tokenManager.setRefreshCallback(async (refreshToken: string) => {\n const result = await this.refresh(refreshToken);\n const expiresAt = result.expires_in\n ? Math.floor(Date.now() / 1000) + result.expires_in\n : undefined;\n return {\n accessToken: result.access_token,\n refreshToken: result.refresh_token,\n expiresAt,\n };\n });\n }\n\n async login(params: LoginParams): Promise<LoginResponse> {\n const response = await this.http.request<LoginResponse>({\n method: 'POST',\n path: '/auth/login',\n body: params,\n });\n\n await this.storeTokensFromLogin(response);\n return response;\n }\n\n async register(params: RegisterParams): Promise<RegisterResponse> {\n const response = await this.http.request<RegisterResponse>({\n method: 'POST',\n path: '/auth/register',\n body: params,\n });\n\n // Only store tokens if they were returned (not when email verification is required)\n if (response.access_token) {\n await this.storeTokensFromRegister(response);\n }\n return response;\n }\n\n async refresh(refreshToken: string): Promise<TokenResponse> {\n return this.http.request<TokenResponse>({\n method: 'POST',\n path: '/auth/refresh',\n body: { refreshToken },\n });\n }\n\n async getProfile(): Promise<UserProfile> {\n return this.http.request<UserProfile>({\n method: 'GET',\n path: '/auth/me',\n authenticated: true,\n });\n }\n\n async logout(): Promise<void> {\n try {\n await this.http.request<void>({\n method: 'POST',\n path: '/auth/logout',\n authenticated: true,\n });\n } catch {\n // Server-side revocation is best-effort. If the token is already invalid\n // the server has effectively revoked the session anyway — always clear\n // the local state so the UI doesn't get stuck.\n }\n await this.tokenManager.clearTokens();\n }\n\n async verifyEmail(token: string): Promise<{ message: string }> {\n return this.http.request<{ message: string }>({\n method: 'GET',\n path: '/auth/verify-email',\n query: { token },\n });\n }\n\n async resendVerification(email: string): Promise<{ message: string }> {\n return this.http.request<{ message: string }>({\n method: 'POST',\n path: '/auth/resend-verification',\n body: { email },\n });\n }\n\n async getUserInfo(): Promise<UserInfo> {\n return this.http.request<UserInfo>({\n method: 'GET',\n path: this.config.userInfoEndpoint ?? '/auth/userinfo',\n authenticated: true,\n });\n }\n\n getAuthorizationUrl(params: AuthorizationUrlParams): string {\n const baseUrl = this.config.apiUrl.replace(/\\/+$/, '');\n const endpoint =\n this.config.authorizationEndpoint ?? '/auth/authorize';\n const url = new URL(`${baseUrl}${endpoint}`);\n\n url.searchParams.set('client_id', this.config.clientId);\n url.searchParams.set('redirect_uri', params.redirectUri);\n url.searchParams.set('response_type', params.responseType ?? 'code');\n\n const scope =\n params.scope ?? this.config.scopes?.join(' ') ?? 'openid';\n url.searchParams.set('scope', scope);\n\n if (params.state) {\n url.searchParams.set('state', params.state);\n }\n if (params.codeChallenge) {\n url.searchParams.set('code_challenge', params.codeChallenge);\n }\n if (params.codeChallengeMethod) {\n url.searchParams.set(\n 'code_challenge_method',\n params.codeChallengeMethod\n );\n }\n\n return url.toString();\n }\n\n async exchangeCode(params: ExchangeCodeParams): Promise<TokenResponse> {\n const body: Record<string, string> = {\n grant_type: params.grantType ?? GrantType.AUTHORIZATION_CODE,\n code: params.code,\n redirect_uri: params.redirectUri,\n client_id: this.config.clientId,\n };\n\n if (params.clientSecret) {\n body['client_secret'] = params.clientSecret;\n }\n if (params.codeVerifier) {\n body['code_verifier'] = params.codeVerifier;\n }\n\n const tokenEndpoint =\n this.config.tokenEndpoint ?? '/auth/token';\n\n const response = await this.http.request<TokenResponse>({\n method: 'POST',\n path: tokenEndpoint,\n body,\n });\n\n const expiresAt = response.expires_in\n ? Math.floor(Date.now() / 1000) + response.expires_in\n : undefined;\n\n await this.tokenManager.setTokens({\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n expiresAt,\n });\n\n return response;\n }\n\n async generatePKCE(): Promise<PKCEChallenge> {\n return generatePKCEChallenge();\n }\n\n async getOpenIDConfiguration(): Promise<OpenIDConfiguration> {\n return this.http.request<OpenIDConfiguration>({\n method: 'GET',\n path: '/.well-known/openid-configuration',\n });\n }\n\n async getJWKS(): Promise<JWKSResponse> {\n return this.http.request<JWKSResponse>({\n method: 'GET',\n path: '/.well-known/jwks.json',\n });\n }\n\n /**\n * Request a password reset email.\n * Always returns a generic message regardless of whether the email exists.\n */\n async requestPasswordReset(email: string): Promise<{ message: string }> {\n return this.http.request<{ message: string }>({\n method: 'POST',\n path: '/auth/request-password-reset',\n body: { email },\n });\n }\n\n /**\n * Reset password using a token received via email.\n */\n async resetPassword(\n token: string,\n password: string,\n ): Promise<{ message: string }> {\n return this.http.request<{ message: string }>({\n method: 'POST',\n path: '/auth/reset-password',\n body: { token, password },\n });\n }\n\n /**\n * Exchange a one-time verification code for tokens.\n * Use this after email verification redirects the user with a ?loginCode= parameter.\n */\n async exchangeVerificationCode(code: string): Promise<LoginResponse> {\n const response = await this.http.request<LoginResponse>({\n method: 'POST',\n path: '/auth/exchange-code',\n body: { code },\n });\n\n await this.storeTokensFromLogin(response);\n return response;\n }\n\n /**\n * Resend a two-factor authentication code email.\n * Call this when the user hasn't received the code or it expired.\n */\n async resendTwoFactorCode(token: string): Promise<{ message: string }> {\n return this.http.request<{ message: string }>({\n method: 'POST',\n path: '/auth/resend-two-factor-code',\n body: { token },\n });\n }\n\n /**\n * Verify a two-factor authentication code.\n * Call this after catching SpacelrTwoFactorRequiredError from login().\n */\n async verifyTwoFactor(params: TwoFactorVerifyParams): Promise<LoginResponse> {\n const response = await this.http.request<LoginResponse>({\n method: 'POST',\n path: '/auth/verify-two-factor',\n body: { token: params.token, code: params.code },\n });\n\n await this.storeTokensFromLogin(response);\n return response;\n }\n\n private async storeTokensFromLogin(response: LoginResponse): Promise<void> {\n const expiresAt = response.expires_in\n ? Math.floor(Date.now() / 1000) + response.expires_in\n : undefined;\n\n await this.tokenManager.setTokens({\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n expiresAt,\n });\n }\n\n private async storeTokensFromRegister(\n response: RegisterResponse\n ): Promise<void> {\n if (!response.access_token) return;\n await this.tokenManager.setTokens({\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n });\n }\n}\n","import { HttpClient, TokenManager } from '../core';\nimport {\n SpacelrClientConfig,\n FileVisibility,\n FileInfo,\n FileListResponse,\n ListFilesParams,\n InitMultipartUploadParams,\n InitMultipartUploadResponse,\n PartEtag,\n ShareFileParams,\n UnshareFileParams,\n QuotaInfo,\n UploadFileParams,\n UploadLargeFileParams,\n UploadProgress,\n DownloadUrlResponse,\n} from '../types';\n\nexport class StorageModule {\n private http: HttpClient;\n private tokenManager: TokenManager;\n private config: SpacelrClientConfig;\n\n constructor(\n http: HttpClient,\n tokenManager: TokenManager,\n config: SpacelrClientConfig\n ) {\n this.http = http;\n this.tokenManager = tokenManager;\n this.config = config;\n }\n\n /**\n * Upload a file through the gateway (no direct S3 access).\n * Accepts a Blob/File (browser) or ArrayBuffer/Uint8Array (Node).\n */\n async uploadFile(\n file: Blob | ArrayBuffer | Uint8Array,\n params: UploadFileParams,\n onProgress?: (progress: UploadProgress) => void,\n ): Promise<FileInfo> {\n const formData = new FormData();\n const blob =\n file instanceof Blob\n ? file\n : new Blob([file as BlobPart], { type: params.mimeType });\n formData.append('file', blob, params.filename);\n if (params.visibility) formData.append('visibility', params.visibility);\n if (params.description) formData.append('description', params.description);\n\n const progressHandler = onProgress\n ? (e: { loaded: number; total: number }) => {\n onProgress({\n loaded: e.loaded,\n total: e.total,\n percentage: e.total > 0 ? Math.round((e.loaded / e.total) * 100) : 0,\n });\n }\n : undefined;\n\n return this.http.uploadForm<FileInfo>('/storage/files', formData, progressHandler);\n }\n\n /**\n * Upload a large file using multipart upload through the gateway.\n * Splits into parts, uploads concurrently, and completes.\n */\n async uploadLargeFile(\n file: Blob | ArrayBuffer | Uint8Array,\n params: UploadLargeFileParams,\n onProgress?: (progress: UploadProgress) => void\n ): Promise<FileInfo> {\n const fileSize = file instanceof Blob ? file.size : file.byteLength;\n const concurrency = params.concurrency ?? 3;\n\n const init = await this.initMultipartUpload({\n filename: params.filename,\n mimeType: params.mimeType,\n totalSizeBytes: fileSize,\n visibility: params.visibility,\n description: params.description,\n });\n\n const { partSize, totalParts, fileId } = init;\n const completedParts: PartEtag[] = [];\n let completedBytes = 0;\n const partProgressMap = new Map<number, number>();\n\n try {\n const allPartNumbers = Array.from(\n { length: totalParts },\n (_, i) => i + 1\n );\n\n for (let i = 0; i < allPartNumbers.length; i += concurrency) {\n const batch = allPartNumbers.slice(i, i + concurrency);\n\n const uploads = batch.map(async (partNumber) => {\n const start = (partNumber - 1) * partSize;\n const end = Math.min(start + partSize, fileSize);\n const chunkSize = end - start;\n const chunk =\n file instanceof Blob\n ? file.slice(start, end)\n : new Blob([file.slice(start, end)]);\n\n const formData = new FormData();\n formData.append('file', chunk, `part-${partNumber}`);\n formData.append('partNumber', String(partNumber));\n\n const partProgress = onProgress && typeof XMLHttpRequest !== 'undefined'\n ? (e: { loaded: number; total: number }) => {\n const ratio = e.total > 0 ? e.loaded / e.total : 0;\n partProgressMap.set(partNumber, Math.min(ratio * chunkSize, chunkSize));\n const inFlightBytes = Array.from(partProgressMap.values())\n .reduce((sum, v) => sum + v, 0);\n const totalLoaded = Math.min(completedBytes + inFlightBytes, fileSize);\n onProgress({\n loaded: totalLoaded,\n total: fileSize,\n percentage: fileSize > 0\n ? Math.min(100, Math.round((totalLoaded / fileSize) * 100))\n : 0,\n });\n }\n : undefined;\n\n const result = await this.http.uploadForm<{ etag: string }>(\n `/storage/files/${fileId}/multipart/upload-part`,\n formData,\n partProgress,\n );\n\n partProgressMap.delete(partNumber);\n completedBytes += chunkSize;\n\n if (onProgress) {\n onProgress({\n loaded: Math.min(completedBytes, fileSize),\n total: fileSize,\n percentage: fileSize > 0\n ? Math.min(100, Math.round((completedBytes / fileSize) * 100))\n : 0,\n });\n }\n\n completedParts.push({\n partNumber,\n etag: result.etag,\n });\n });\n\n await Promise.all(uploads);\n partProgressMap.clear();\n }\n\n // Sort parts by number before completing\n completedParts.sort((a, b) => a.partNumber - b.partNumber);\n\n await this.completeMultipartUpload(fileId, completedParts);\n return this.getFileInfo(fileId);\n } catch (error) {\n // Clean up the incomplete multipart upload on failure\n try {\n await this.abortMultipartUpload(fileId);\n } catch {\n // Abort is best-effort; ignore cleanup failures\n }\n throw error;\n }\n }\n\n async initMultipartUpload(\n params: InitMultipartUploadParams\n ): Promise<InitMultipartUploadResponse> {\n return this.http.request<InitMultipartUploadResponse>({\n method: 'POST',\n path: '/storage/files/multipart/init',\n body: params,\n authenticated: true,\n });\n }\n\n async completeMultipartUpload(\n fileId: string,\n parts: PartEtag[]\n ): Promise<FileInfo> {\n return this.http.request<FileInfo>({\n method: 'POST',\n path: `/storage/files/${fileId}/multipart/complete`,\n body: { parts },\n authenticated: true,\n });\n }\n\n async abortMultipartUpload(fileId: string): Promise<void> {\n await this.http.request<void>({\n method: 'POST',\n path: `/storage/files/${fileId}/multipart/abort`,\n authenticated: true,\n });\n }\n\n async listFiles(params?: ListFilesParams): Promise<FileListResponse> {\n return this.http.request<FileListResponse>({\n method: 'GET',\n path: '/storage/files',\n query: params as Record<string, string | number | undefined>,\n authenticated: true,\n });\n }\n\n async listSharedFiles(params?: ListFilesParams): Promise<FileListResponse> {\n return this.http.request<FileListResponse>({\n method: 'GET',\n path: '/storage/shared',\n query: params as Record<string, string | number | undefined>,\n authenticated: true,\n });\n }\n\n async getFileInfo(fileId: string): Promise<FileInfo> {\n return this.http.request<FileInfo>({\n method: 'GET',\n path: `/storage/files/${fileId}`,\n authenticated: true,\n });\n }\n\n async downloadFile(fileId: string): Promise<Blob> {\n const baseUrl = this.config.apiUrl.replace(/\\/+$/, '');\n const url = `${baseUrl}/storage/files/${fileId}/download`;\n\n const headers: Record<string, string> = {\n 'x-client-id': this.config.clientId,\n 'x-project-id': this.config.projectId,\n };\n\n const token = await this.tokenManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n const response = await fetch(url, { headers });\n\n if (!response.ok) {\n throw new Error(`Download failed: ${response.status}`);\n }\n\n return response.blob();\n }\n\n async getDownloadUrl(fileId: string): Promise<DownloadUrlResponse> {\n return this.http.request<DownloadUrlResponse>({\n method: 'GET',\n path: `/storage/files/${fileId}/download-url`,\n authenticated: true,\n });\n }\n\n async deleteFile(fileId: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `/storage/files/${fileId}`,\n authenticated: true,\n });\n }\n\n async shareFile(\n fileId: string,\n params: ShareFileParams\n ): Promise<void> {\n await this.http.request<void>({\n method: 'POST',\n path: `/storage/files/${fileId}/share`,\n body: params,\n authenticated: true,\n });\n }\n\n async unshareFile(\n fileId: string,\n params: UnshareFileParams\n ): Promise<void> {\n await this.http.request<void>({\n method: 'POST',\n path: `/storage/files/${fileId}/unshare`,\n body: params,\n authenticated: true,\n });\n }\n\n async updateVisibility(\n fileId: string,\n visibility: FileVisibility\n ): Promise<FileInfo> {\n return this.http.request<FileInfo>({\n method: 'PATCH',\n path: `/storage/files/${fileId}/visibility`,\n body: { visibility },\n authenticated: true,\n });\n }\n\n async getQuota(): Promise<QuotaInfo> {\n return this.http.request<QuotaInfo>({\n method: 'GET',\n path: '/storage/quota',\n authenticated: true,\n });\n }\n\n async getPublicFileUrl(fileId: string, projectId?: string): Promise<string> {\n const baseUrl = this.config.apiUrl.replace(/\\/+$/, '');\n const resolvedProjectId = projectId ?? this.config.projectId;\n return `${baseUrl}/public/files/${resolvedProjectId}/${fileId}`;\n }\n}\n","import { HttpClient } from '../core';\nimport { RealtimeClient, DatabaseChangeEvent } from '../core';\n\nexport interface PopulateOption {\n field: string;\n collection: string;\n foreignField?: string;\n}\n\nexport interface DocumentResult {\n _id: string;\n [key: string]: unknown;\n}\n\nexport interface FindResult<T = Record<string, unknown>> {\n documents: (T & { _id: string })[];\n total: number;\n limit: number;\n offset: number;\n}\n\nexport interface InsertResult {\n insertedCount: number;\n insertedIds: string[];\n}\n\nexport interface UpdateResult {\n matchedCount: number;\n modifiedCount: number;\n}\n\nexport interface DeleteResult {\n deletedCount: number;\n}\n\nexport interface CountResult {\n count: number;\n}\n\nexport interface FindByIdOptions {\n populate?: PopulateOption[];\n}\n\nexport interface SubscribeHandlers<T = Record<string, unknown>> {\n where?: Record<string, string | number | boolean>;\n onInsert?: (doc: T & { _id: string }) => void;\n onUpdate?: (doc: T & { _id: string }) => void;\n onDelete?: (documentId: string) => void;\n onError?: (error: Error) => void;\n}\n\nclass QueryBuilder<T = Record<string, unknown>> {\n private _filter?: Record<string, unknown>;\n private _sort?: Record<string, 1 | -1>;\n private _limit?: number;\n private _offset?: number;\n private _fields?: string[];\n private _populate: PopulateOption[] = [];\n\n constructor(\n private http: HttpClient,\n private basePath: string,\n filter?: Record<string, unknown>,\n ) {\n this._filter = filter;\n }\n\n sort(sort: Record<string, 1 | -1>): QueryBuilder<T> {\n this._sort = sort;\n return this;\n }\n\n limit(limit: number): QueryBuilder<T> {\n this._limit = limit;\n return this;\n }\n\n offset(offset: number): QueryBuilder<T> {\n this._offset = offset;\n return this;\n }\n\n select(fields: string[]): QueryBuilder<T> {\n this._fields = fields;\n return this;\n }\n\n populate(\n field: string,\n collection: string,\n foreignField?: string,\n ): QueryBuilder<T> {\n this._populate.push({ field, collection, foreignField });\n return this;\n }\n\n async execute(): Promise<FindResult<T>> {\n const query: Record<string, string | number | undefined> = {};\n if (this._filter) query['filter'] = JSON.stringify(this._filter);\n if (this._sort) query['sort'] = JSON.stringify(this._sort);\n if (this._limit !== undefined) query['limit'] = this._limit;\n if (this._offset !== undefined) query['offset'] = this._offset;\n if (this._fields) query['fields'] = this._fields.join(',');\n if (this._populate.length) {\n query['populate'] = this._populate\n .map((p) =>\n p.foreignField\n ? `${p.field}:${p.collection}:${p.foreignField}`\n : `${p.field}:${p.collection}`,\n )\n .join(',');\n }\n\n return this.http.request<FindResult<T>>({\n method: 'GET',\n path: this.basePath,\n query,\n authenticated: true,\n });\n }\n}\n\nclass CollectionRef<T = Record<string, unknown>> {\n private basePath: string;\n private collectionName: string;\n\n constructor(\n private http: HttpClient,\n private realtime: RealtimeClient | null,\n private projectId: string,\n collectionName: string,\n ) {\n this.collectionName = collectionName;\n this.basePath = `/db/${collectionName}`;\n }\n\n async insert(\n document: Partial<T> & { _id?: string },\n ): Promise<InsertResult> {\n return this.http.request<InsertResult>({\n method: 'POST',\n path: this.basePath,\n body: { documents: [document] },\n authenticated: true,\n });\n }\n\n async insertMany(\n documents: (Partial<T> & { _id?: string })[],\n ): Promise<InsertResult> {\n return this.http.request<InsertResult>({\n method: 'POST',\n path: this.basePath,\n body: { documents },\n authenticated: true,\n });\n }\n\n find(filter?: Record<string, unknown>): QueryBuilder<T> {\n return new QueryBuilder<T>(this.http, this.basePath, filter);\n }\n\n async findById(\n id: string,\n options?: FindByIdOptions,\n ): Promise<T & { _id: string }> {\n const query: Record<string, string> = {};\n if (options?.populate?.length) {\n query['populate'] = options.populate\n .map((p) =>\n p.foreignField\n ? `${p.field}:${p.collection}:${p.foreignField}`\n : `${p.field}:${p.collection}`,\n )\n .join(',');\n }\n\n return this.http.request<T & { _id: string }>({\n method: 'GET',\n path: `${this.basePath}/${id}`,\n query,\n authenticated: true,\n });\n }\n\n async update(id: string, update: Partial<T>): Promise<UpdateResult> {\n return this.http.request<UpdateResult>({\n method: 'PATCH',\n path: `${this.basePath}/${id}`,\n body: { update },\n authenticated: true,\n });\n }\n\n async delete(id: string): Promise<DeleteResult> {\n return this.http.request<DeleteResult>({\n method: 'DELETE',\n path: `${this.basePath}/${id}`,\n authenticated: true,\n });\n }\n\n async count(filter?: Record<string, unknown>): Promise<number> {\n const result = await this.http.request<CountResult>({\n method: 'POST',\n path: `${this.basePath}/count`,\n body: { filter },\n authenticated: true,\n });\n return result.count;\n }\n\n subscribe(handlers: SubscribeHandlers<T>): () => void {\n if (!this.realtime) {\n throw new Error('Realtime not available: no RealtimeClient configured');\n }\n\n let unsubscribeFn: (() => void) | null = null;\n let pendingUnsub = false;\n\n const callback = (event: DatabaseChangeEvent) => {\n switch (event.type) {\n case 'insert':\n if (handlers.onInsert && event.document) {\n handlers.onInsert(event.document as T & { _id: string });\n }\n break;\n case 'update':\n if (handlers.onUpdate && event.document) {\n handlers.onUpdate(event.document as T & { _id: string });\n }\n break;\n case 'delete':\n if (handlers.onDelete) {\n handlers.onDelete(event.documentId);\n }\n break;\n }\n };\n\n this.realtime\n .subscribe(this.projectId, this.collectionName, callback, handlers.onError, handlers.where)\n .then((unsub) => {\n if (pendingUnsub) {\n // Unsubscribe was called before async subscribe resolved\n unsub();\n } else {\n unsubscribeFn = unsub;\n }\n })\n .catch((err) => {\n if (handlers.onError) {\n handlers.onError(err instanceof Error ? err : new Error(String(err)));\n }\n });\n\n // Return synchronous unsubscribe\n return () => {\n if (unsubscribeFn) {\n unsubscribeFn();\n } else {\n pendingUnsub = true;\n }\n };\n }\n}\n\nexport class DatabaseModule {\n private http: HttpClient;\n private realtime: RealtimeClient | null;\n private projectId: string;\n\n constructor(http: HttpClient, projectId: string, realtime?: RealtimeClient) {\n this.http = http;\n this.projectId = projectId;\n this.realtime = realtime ?? null;\n }\n\n collection<T = Record<string, unknown>>(name: string): CollectionRef<T> {\n return new CollectionRef<T>(this.http, this.realtime, this.projectId, name);\n }\n}\n","import { HttpClient } from '../core';\n\nexport interface PushSubscriptionInfo {\n id: string;\n platform: 'web' | 'android' | 'ios';\n endpoint?: string;\n deviceToken?: string;\n deviceId?: string;\n deviceName?: string;\n userAgent?: string;\n isActive: boolean;\n lastUsedAt?: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface VapidKeyResponse {\n publicKey: string;\n}\n\nconst DEVICE_ID_KEY = 'spacelr_device_id';\n\nexport class NotificationsModule {\n private customDeviceId: string | null = null;\n private customDeviceName: string | null = null;\n\n constructor(private http: HttpClient) {}\n\n /** Set a custom device ID (e.g. from Capacitor Preferences for persistence beyond localStorage) */\n setDeviceId(id: string): void {\n this.customDeviceId = id;\n }\n\n /** Set a custom device name (e.g. \"iOS App\", \"macOS - Chrome\") */\n setDeviceName(name: string): void {\n this.customDeviceName = name;\n }\n\n /** Get or generate a stable device identifier. Custom ID takes priority over localStorage. */\n private getDeviceId(): string | undefined {\n if (this.customDeviceId) return this.customDeviceId;\n if (typeof localStorage === 'undefined') return undefined;\n let id = localStorage.getItem(DEVICE_ID_KEY);\n if (!id) {\n id = crypto.randomUUID();\n localStorage.setItem(DEVICE_ID_KEY, id);\n }\n return id;\n }\n\n /** Get device name: custom name > auto-detected from user agent > undefined */\n private getDeviceName(): string | undefined {\n if (this.customDeviceName) return this.customDeviceName;\n return this.detectDeviceName();\n }\n\n /** Auto-detect a short device label from navigator.userAgent (e.g. \"macOS - Chrome\") */\n private detectDeviceName(): string | undefined {\n if (typeof navigator === 'undefined' || !navigator.userAgent) return undefined;\n const ua = navigator.userAgent;\n\n let os = 'Unknown';\n if (/iPad|iPhone|iPod/.test(ua)) os = 'iOS';\n else if (/Android/.test(ua)) os = 'Android';\n else if (/Mac OS X/.test(ua)) os = 'macOS';\n else if (/Windows/.test(ua)) os = 'Windows';\n else if (/Linux/.test(ua)) os = 'Linux';\n\n let browser = 'Unknown';\n if (/Edg\\//.test(ua)) browser = 'Edge';\n else if (/OPR\\/|Opera/.test(ua)) browser = 'Opera';\n else if (/Chrome\\//.test(ua)) browser = 'Chrome';\n else if (/Safari\\//.test(ua) && !/Chrome/.test(ua)) browser = 'Safari';\n else if (/Firefox\\//.test(ua)) browser = 'Firefox';\n\n return `${os} - ${browser}`;\n }\n\n /** Get the VAPID public key for Web Push setup */\n async getVapidPublicKey(): Promise<VapidKeyResponse> {\n return this.http.request<VapidKeyResponse>({\n method: 'GET',\n path: '/notifications/vapid-key',\n authenticated: true,\n });\n }\n\n /** Register a push subscription (web, android, or ios) */\n async subscribe(\n subscription: {\n platform: 'web' | 'android' | 'ios';\n endpoint?: string;\n keys?: { p256dh: string; auth: string };\n deviceToken?: string;\n },\n deviceName?: string,\n ): Promise<PushSubscriptionInfo> {\n return this.http.request<PushSubscriptionInfo>({\n method: 'POST',\n path: '/notifications/subscribe',\n body: {\n ...subscription,\n deviceName: deviceName ?? this.getDeviceName(),\n deviceId: this.getDeviceId(),\n },\n authenticated: true,\n });\n }\n\n /** Unregister a push subscription */\n async unsubscribe(\n platform: 'web' | 'android' | 'ios',\n identifier: string,\n ): Promise<{ deleted: boolean }> {\n const body: Record<string, string> = { platform };\n if (platform === 'web') {\n body['endpoint'] = identifier;\n } else {\n body['deviceToken'] = identifier;\n }\n\n return this.http.request<{ deleted: boolean }>({\n method: 'DELETE',\n path: '/notifications/subscribe',\n body,\n authenticated: true,\n });\n }\n\n /** Get all subscriptions for the current user */\n async getSubscriptions(): Promise<PushSubscriptionInfo[]> {\n return this.http.request<PushSubscriptionInfo[]>({\n method: 'GET',\n path: '/notifications/subscriptions',\n authenticated: true,\n });\n }\n\n /**\n * Helper: Register browser Web Push subscription.\n * Requests notification permission, subscribes via Push API,\n * and registers the subscription with the server.\n */\n async registerBrowserPush(\n serviceWorkerRegistration: ServiceWorkerRegistration,\n deviceName?: string,\n ): Promise<PushSubscriptionInfo> {\n // Get VAPID key\n const { publicKey } = await this.getVapidPublicKey();\n\n if (!publicKey) {\n throw new Error(\n 'VAPID public key not configured on the server',\n );\n }\n\n // Request notification permission\n const permission = await Notification.requestPermission();\n if (permission !== 'granted') {\n throw new Error(\n `Notification permission denied: ${permission}`,\n );\n }\n\n // Convert VAPID key to Uint8Array\n const applicationServerKey = this.urlBase64ToUint8Array(publicKey);\n\n // Subscribe via Push API\n const pushSubscription =\n await serviceWorkerRegistration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey,\n });\n\n const subJson = pushSubscription.toJSON();\n const p256dh = subJson.keys?.['p256dh'];\n const auth = subJson.keys?.['auth'];\n\n if (!subJson.endpoint || !p256dh || !auth) {\n throw new Error('Invalid push subscription: missing endpoint or keys');\n }\n\n // Register with server\n return this.subscribe(\n {\n platform: 'web',\n endpoint: subJson.endpoint,\n keys: { p256dh, auth },\n },\n deviceName,\n );\n }\n\n /**\n * Helper: Register a mobile device push token (FCM or APNs).\n */\n async registerDevicePush(\n deviceToken: string,\n platform: 'android' | 'ios',\n deviceName?: string,\n ): Promise<PushSubscriptionInfo> {\n return this.subscribe(\n {\n platform,\n deviceToken,\n },\n deviceName,\n );\n }\n\n private urlBase64ToUint8Array(base64String: string): ArrayBuffer {\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding)\n .replace(/-/g, '+')\n .replace(/_/g, '/');\n\n const rawData = atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n return outputArray.buffer as ArrayBuffer;\n }\n}\n","import { HttpClient } from '../core';\n\nexport interface FunctionInvokeOptions {\n secret: string;\n payload?: Record<string, unknown>;\n}\n\nexport interface FunctionInvokeResult {\n success: boolean;\n executionId?: string;\n error?: string;\n}\n\nexport class FunctionsModule {\n private http: HttpClient;\n\n constructor(http: HttpClient) {\n this.http = http;\n }\n\n /**\n * Invoke a function via webhook trigger.\n * Calls POST /api/v1/functions/:projectId/:functionId/invoke\n * with X-Webhook-Secret header.\n */\n async invoke(\n projectId: string,\n functionId: string,\n options: FunctionInvokeOptions,\n ): Promise<FunctionInvokeResult> {\n return this.http.request<FunctionInvokeResult>({\n method: 'POST',\n path: `/api/v1/functions/${encodeURIComponent(projectId)}/${encodeURIComponent(functionId)}/invoke`,\n headers: {\n 'X-Webhook-Secret': options.secret,\n },\n body: options.payload ?? {},\n authenticated: false,\n });\n }\n}\n","import { SpacelrClientConfig, StoredTokens } from './types';\nimport { HttpClient, TokenManager, MemoryTokenStorage, BrowserTokenStorage, RealtimeClient, ConnectionState } from './core';\nimport { AuthLostReason } from './core/token-manager';\nimport { AuthModule, StorageModule, DatabaseModule, NotificationsModule, FunctionsModule } from './modules';\n\nexport interface SpacelrClient {\n readonly auth: AuthModule;\n readonly storage: StorageModule;\n readonly db: DatabaseModule;\n readonly notifications: NotificationsModule;\n readonly functions: FunctionsModule;\n /**\n * Store auth tokens externally (e.g. obtained via the auth-components\n * library that bypasses the SDK). Makes the tokens available to HTTP +\n * realtime clients. Refresh is on-demand: the SDK will refresh the\n * access token when it next makes a request and the token is within the\n * expiry buffer (or on a 401 retry).\n */\n setTokens(tokens: StoredTokens): Promise<void>;\n /** Clear stored tokens and reset auth-loss state. */\n clearTokens(): Promise<void>;\n /** Disconnect realtime WebSocket (if connected) */\n disconnect(): void;\n /**\n * Subscribe to auth loss. Fires in three situations:\n * - `'refresh-failed'` — a refresh call was rejected by the server\n * (refresh token revoked or expired). Note: this can also fire from a\n * *background* refresh triggered inside the expiry buffer window, at\n * which point the current access token may still be valid for up to\n * `refreshBufferSeconds` (default 60). Treat the session as over anyway;\n * the refresh token is dead.\n * - `'unauthorized'` — an authenticated request came back 401 and no\n * fresh token could be produced (no refresh token, or the retry after\n * a successful refresh was itself 401).\n *\n * Typical consumer reaction: clear UI state and show the login screen.\n * The event is deduped until `setTokens`/`clearTokens` is called again.\n */\n onAuthLost(listener: (reason: AuthLostReason) => void): () => void;\n /** Subscribe to successful token refreshes. */\n onTokenRefreshed(listener: (tokens: StoredTokens) => void): () => void;\n /**\n * Subscribe to realtime socket connection-state changes. Fires on every\n * transition (dedup: identical consecutive states are skipped). Returns\n * an unsubscribe function.\n *\n * States:\n * - `'disconnected'` — initial state before the first subscribe, or after\n * the SDK has torn the realtime socket down (e.g. on logout via the\n * internal `realtime.disconnect()`). This is a terminal state for the\n * current realtime client; reconnect happens on the next `subscribe()`.\n * - `'reconnecting'` — connecting or rebuilding the socket (initial\n * connect, socket drop, `reconnect_failed` rebuild, or a wake-up event\n * after suspend).\n * - `'connected'` — authenticated and actively listening for events.\n */\n onConnectionStateChanged(listener: (state: ConnectionState) => void): () => void;\n /** Read the current realtime socket state without waiting for an event. */\n getConnectionState(): ConnectionState;\n}\n\nexport function createClient(config: SpacelrClientConfig): SpacelrClient {\n const tokenStorage = config.tokenStorage\n ?? (typeof window !== 'undefined' && typeof window.localStorage !== 'undefined'\n ? new BrowserTokenStorage()\n : new MemoryTokenStorage());\n const tokenManager = new TokenManager(\n tokenStorage,\n config.refreshBufferSeconds ?? 60\n );\n const httpClient = new HttpClient(config, tokenManager);\n\n const realtime = new RealtimeClient({\n baseUrl: config.apiUrl,\n getToken: () => tokenManager.getAccessToken(),\n onTokenRefreshed: (listener) =>\n tokenManager.onTokenRefreshed((tokens) => listener(tokens.accessToken)),\n });\n\n const auth = new AuthModule(httpClient, tokenManager, config);\n const storage = new StorageModule(httpClient, tokenManager, config);\n const db = new DatabaseModule(httpClient, config.projectId, realtime);\n const notifications = new NotificationsModule(httpClient);\n const functions = new FunctionsModule(httpClient);\n\n return {\n auth,\n storage,\n db,\n notifications,\n functions,\n setTokens(tokens) {\n return tokenManager.setTokens(tokens);\n },\n clearTokens() {\n return tokenManager.clearTokens();\n },\n disconnect() {\n realtime.disconnect();\n },\n onAuthLost(listener) {\n return tokenManager.onAuthLost(listener);\n },\n onTokenRefreshed(listener) {\n return tokenManager.onTokenRefreshed(listener);\n },\n onConnectionStateChanged(listener) {\n return realtime.onConnectionStateChanged(listener);\n },\n getConnectionState() {\n return realtime.getConnectionState();\n },\n };\n}\n"],"mappings":"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","names":["FileVisibility","GrantType","CodeChallengeMethod","SharePermission","hash","callbacks","document"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spacelr/sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "TypeScript SDK for the Spacelr API - auth and storage",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "repository": {
33
33
  "type": "git",
34
- "url": "https://github.com/Scan0815/spacelr-workspace",
34
+ "url": "git+https://github.com/Scan0815/spacelr-workspace.git",
35
35
  "directory": "libs/sdk"
36
36
  },
37
37
  "dependencies": {