UNPKG

@blinkdotnew/sdk

Version:

Blink TypeScript SDK for client-side applications - Zero-boilerplate CRUD + auth + AI + analytics + notifications for modern SaaS/AI apps

1,624 lines (1,616 loc) 120 kB
/** * Storage adapter for cross-platform compatibility * Allows using AsyncStorage on React Native and localStorage on web */ /** * Storage adapter interface * Supports both synchronous (web localStorage) and asynchronous (React Native AsyncStorage) implementations */ interface StorageAdapter { getItem(key: string): Promise<string | null> | string | null; setItem(key: string, value: string): Promise<void> | void; removeItem(key: string): Promise<void> | void; clear(): Promise<void> | void; } /** * Web localStorage adapter (synchronous) * Used automatically on web browsers */ declare class WebStorageAdapter implements StorageAdapter { getItem(key: string): string | null; setItem(key: string, value: string): void; removeItem(key: string): void; clear(): void; } /** * Async storage adapter wrapper (for React Native AsyncStorage) * Usage: * ```typescript * import AsyncStorage from '@react-native-async-storage/async-storage' * const storage = new AsyncStorageAdapter(AsyncStorage) * ``` */ declare class AsyncStorageAdapter implements StorageAdapter { private asyncStorage; constructor(asyncStorage: any); getItem(key: string): Promise<string | null>; setItem(key: string, value: string): Promise<void>; removeItem(key: string): Promise<void>; clear(): Promise<void>; } /** * No-op storage adapter (for server-side or when storage is disabled) * Used automatically on Node.js or when no storage is available */ declare class NoOpStorageAdapter implements StorageAdapter { getItem(_key: string): null; setItem(_key: string, _value: string): void; removeItem(_key: string): void; clear(): void; } /** * Get default storage adapter based on platform */ declare function getDefaultStorageAdapter(): StorageAdapter; /** * Core type definitions for Blink SDK */ interface BlinkClientConfig { projectId: string; authRequired?: boolean; auth?: BlinkAuthConfig; /** * Publishable key (client-safe). * * Used for **public endpoints** when no user JWT is present (e.g. analytics ingest, storage upload, * optional public DB reads). Never use for privileged operations. */ publishableKey?: string; /** * Secret key (server-only, privileged). Permanent, never expires. * * Used in **server runtimes** (Edge Functions, Workers) for privileged operations that require * service-role access (e.g. raw SQL, bypassing row-level security). * * Format: `blnk_sk_{projectId-last-8}_{random}` (similar to Stripe's `sk_live_...`) * * **Security**: Never expose this key in client-side code. It is injected by the platform * into edge function environments as `BLINK_SECRET_KEY`. * * When present, this key takes precedence over user JWTs for all requests. * * @example * // Edge function (Deno) * const blink = createClient({ * projectId: Deno.env.get('BLINK_PROJECT_ID')!, * secretKey: Deno.env.get('BLINK_SECRET_KEY'), * }) */ secretKey?: string; /** * @deprecated Use `secretKey` instead. Service tokens are JWT-based and expire after 365 days. * Secret keys are permanent and never expire. */ serviceToken?: string; /** * Storage adapter for cross-platform token persistence * * Web: Uses localStorage by default * React Native: Pass AsyncStorageAdapter(AsyncStorage) * Node.js: Uses NoOpStorageAdapter by default * * @example * // React Native * import AsyncStorage from '@react-native-async-storage/async-storage' * import { AsyncStorageAdapter } from '@blinkdotnew/sdk' * * const blink = createClient({ * projectId: 'your-project', * storage: new AsyncStorageAdapter(AsyncStorage) * }) */ storage?: StorageAdapter; } interface BlinkAuthConfig { mode?: 'managed' | 'headless'; /** * Automatically detect and extract auth tokens from URL parameters * * - Web: Set to `true` (default) to handle OAuth redirects * - React Native: Set to `false` to use deep links instead * * @default true * * @example * // React Native - disable URL detection, use deep links * { * auth: { * detectSessionInUrl: false, * storage: AsyncStorageAdapter(AsyncStorage) * } * } */ detectSessionInUrl?: boolean; /** * WebBrowser module for React Native OAuth (expo-web-browser) * * When provided, `signInWithGoogle()` and other OAuth methods will * automatically use the mobile session-based flow on React Native, * so you don't need platform-specific code. * * @example * // React Native setup (configure once) * import * as WebBrowser from 'expo-web-browser' * import AsyncStorage from '@react-native-async-storage/async-storage' * * const blink = createClient({ * projectId: 'your-project', * auth: { * mode: 'headless', * webBrowser: WebBrowser // Pass the module here * }, * storage: new AsyncStorageAdapter(AsyncStorage) * }) * * // Now this works on both web and mobile! * const user = await blink.auth.signInWithGoogle() */ webBrowser?: WebBrowserModule; email?: { requireVerification?: boolean; allowSignUp?: boolean; passwordRules?: { minLength?: number; requireUppercase?: boolean; requireLowercase?: boolean; requireNumbers?: boolean; requireSpecialChars?: boolean; }; }; roles?: { [role: string]: { permissions: string[]; inherit?: string[]; }; }; defaultRole?: string; session?: { duration?: number; refreshThreshold?: number; multiDevice?: boolean; }; redirectUrl?: string; authUrl?: string; coreUrl?: string; /** * Storage adapter for auth token persistence (overrides global storage) * If not provided, uses the global storage from BlinkClientConfig */ storage?: StorageAdapter; } type AuthProvider = 'email' | 'google' | 'github' | 'apple' | 'microsoft' | 'twitter' | 'linkedin' | 'discord'; interface AuthOptions { redirectUrl?: string; metadata?: Record<string, any>; useMobileSession?: boolean; } interface SignUpData { email: string; password: string; metadata?: { displayName?: string; avatar?: string; [key: string]: any; }; } interface MagicLinkOptions { redirectUrl?: string; } /** * WebBrowser interface for React Native OAuth * Compatible with expo-web-browser module * * Simply pass the expo-web-browser module directly: * ```typescript * import * as WebBrowser from 'expo-web-browser' * const blink = createClient({ auth: { webBrowser: WebBrowser } }) * ``` */ interface WebBrowserModule { openAuthSessionAsync(url: string, redirectUrl?: string, options?: unknown): Promise<{ type: string; url?: string; }>; } interface BlinkUser { id: string; email: string; displayName?: string; photoURL?: string; emailVerified?: boolean; createdAt?: string; lastSignInAt?: string; role?: string; } interface AuthTokens { access_token: string; refresh_token?: string; token_type: 'Bearer'; expires_in: number; refresh_expires_in?: number; issued_at?: number; } interface AuthState { user: BlinkUser | null; tokens: AuthTokens | null; isAuthenticated: boolean; isLoading: boolean; } interface FilterOperators { eq?: any; neq?: any; gt?: any; gte?: any; lt?: any; lte?: any; in?: any[]; not_in?: any[]; like?: string; ilike?: string; is?: null | boolean; not?: any; } interface LogicalOperators { AND?: FilterCondition[]; OR?: FilterCondition[]; } type FilterCondition = Record<string, any> | FilterOperators | LogicalOperators; interface QueryOptions { where?: FilterCondition; orderBy?: Record<string, 'asc' | 'desc'> | string; limit?: number; offset?: number; cursor?: string; select?: string[]; } interface CreateOptions { returning?: boolean; } interface UpdateOptions { returning?: boolean; } interface UpsertOptions { onConflict?: string; returning?: boolean; } interface TableOperations<T = any> { create(data: Partial<T>, options?: CreateOptions): Promise<T>; createMany(data: Partial<T>[], options?: CreateOptions): Promise<T[]>; upsert(data: Partial<T>, options?: UpsertOptions): Promise<T>; upsertMany(data: Partial<T>[], options?: UpsertOptions): Promise<T[]>; get(id: string): Promise<T | null>; list(options?: QueryOptions): Promise<T[]>; update(id: string, data: Partial<T>, options?: UpdateOptions): Promise<T>; updateMany(updates: Array<{ id: string; } & Partial<T>>, options?: UpdateOptions): Promise<T[]>; delete(id: string): Promise<void>; deleteMany(options: { where: FilterCondition; }): Promise<void>; count(options?: { where?: FilterCondition; }): Promise<number>; exists(options: { where: FilterCondition; }): Promise<boolean>; } declare class BlinkError extends Error { code?: string | undefined; status?: number | undefined; details?: any; constructor(message: string, code?: string | undefined, status?: number | undefined, details?: any); } interface StorageUploadOptions { /** * @deprecated Blink storage uploads are add-only by default. This option is ignored. */ upsert?: boolean; onProgress?: (percent: number) => void; } interface StorageUploadResponse { publicUrl: string; } interface FileObject { id: string; name: string; bucket_id: string; owner?: string | null; owner_id?: string | null; version?: string | null; created_at: string; updated_at: string; last_accessed_at: string; metadata: { size: number; mimetype: string; cacheControl?: string; }; user_metadata?: Record<string, any>; } interface BlinkStorage { upload(file: File | Blob | Buffer, path: string, options?: StorageUploadOptions): Promise<StorageUploadResponse>; download(path: string, options?: { filename?: string; }): Promise<StorageDownloadResponse>; remove(...paths: string[]): Promise<void>; } interface StorageDownloadResponse { downloadUrl: string; filename: string; contentType?: string; size?: number; } interface TokenUsage$1 { promptTokens: number; completionTokens: number; totalTokens: number; } interface TextContent { type: 'text'; text: string; } interface ImageContent { type: 'image'; image: string; } type MessageContent = TextContent | ImageContent; interface Message { role: 'system' | 'user' | 'assistant'; content: string | MessageContent[]; } interface TextGenerationRequest { model?: string; prompt?: string; messages?: Message[]; stream?: boolean; search?: boolean; maxSteps?: number; experimental_continueSteps?: boolean; maxTokens?: number; temperature?: number; signal?: AbortSignal; } interface TextGenerationResponse { text: string; finishReason?: 'stop' | 'length' | 'content_filter' | 'tool_calls'; usage?: TokenUsage$1; files?: any[]; reasoningDetails?: any[]; toolCalls?: any[]; toolResults?: any[]; warnings?: string[]; request?: { body?: string; }; response?: any; steps?: Array<{ stepType?: string; text?: string; finishReason?: string; usage?: TokenUsage$1; }>; sources?: any[]; providerMetadata?: any; experimental_providerMetadata?: any; } interface ObjectGenerationRequest { model?: string; prompt: string; output?: 'object' | 'array' | 'enum'; schema?: any; enum?: string[]; stream?: boolean; signal?: AbortSignal; } interface ObjectGenerationResponse { object: any; finishReason?: 'stop' | 'length' | 'content_filter'; usage?: TokenUsage$1; warnings?: string[]; providerMetadata?: { openai?: { reasoningTokens?: number; acceptedPredictionTokens?: number; rejectedPredictionTokens?: number; cachedPromptTokens?: number; }; }; experimental_providerMetadata?: any; response?: { id?: string; timestamp?: string; modelId?: string; headers?: any; body?: any; }; request?: { body?: string; }; } interface ImageGenerationRequest { model?: string; prompt: string; images?: string[]; size?: string; quality?: 'auto' | 'low' | 'medium' | 'high'; background?: 'auto' | 'transparent' | 'opaque'; n?: number; response_format?: 'url' | 'b64_json'; output_format?: 'png' | 'jpeg' | 'webp'; output_compression?: number; moderation?: 'auto' | 'low'; signal?: AbortSignal; } interface ImageGenerationResponse { data: Array<{ url?: string; b64_json?: string; }>; } interface SpeechGenerationRequest { model?: string; text: string; voice?: 'alloy' | 'echo' | 'fable' | 'onyx' | 'nova' | 'shimmer'; response_format?: 'mp3' | 'opus' | 'aac' | 'flac' | 'wav' | 'pcm'; speed?: number; signal?: AbortSignal; } interface SpeechGenerationResponse { url: string; voice: string; format: string; mimeType: string; } interface TranscriptionRequest { model?: string; audio: string | number[] | ArrayBuffer | Uint8Array; language?: string; response_format?: 'json' | 'text' | 'srt' | 'verbose_json' | 'vtt'; signal?: AbortSignal; } interface VideoGenerationRequest { prompt: string; model?: string; image_url?: string; duration?: string; aspect_ratio?: string; resolution?: string; negative_prompt?: string; generate_audio?: boolean; seed?: number; cfg_scale?: number; signal?: AbortSignal; } interface VideoGenerationResponse { result: { video: { url: string; content_type?: string; file_name?: string; file_size?: number; }; seed?: number; video_id?: string; thumbnail?: { url: string; }; }; metadata?: { projectId: string; timestamp: string; provider: string; model: string; }; usage?: { creditsCharged: number; costUSD: number; model: string; }; } interface TranscriptionResponse { text: string; transcript?: string; segments?: Array<{ id: number; seek: number; start: number; end: number; text: string; tokens: number[]; temperature: number; avg_logprob: number; compression_ratio: number; no_speech_prob: number; }>; language?: string; duration?: number; words?: Array<{ word: string; start: number; end: number; }>; } interface BlinkAI { generateText(options: TextGenerationRequest): Promise<TextGenerationResponse>; streamText(options: TextGenerationRequest, onChunk: (chunk: string) => void): Promise<TextGenerationResponse>; generateObject(options: ObjectGenerationRequest): Promise<ObjectGenerationResponse>; streamObject(options: ObjectGenerationRequest, onPartial: (partial: any) => void): Promise<ObjectGenerationResponse>; generateImage(options: ImageGenerationRequest): Promise<ImageGenerationResponse>; modifyImage(options: { images: string[]; prompt: string; size?: string; quality?: "auto" | "low" | "medium" | "high"; n?: number; background?: "auto" | "transparent" | "opaque"; signal?: AbortSignal; }): Promise<ImageGenerationResponse>; generateVideo(options: VideoGenerationRequest): Promise<VideoGenerationResponse>; generateSpeech(options: SpeechGenerationRequest): Promise<SpeechGenerationResponse>; transcribeAudio(options: TranscriptionRequest): Promise<TranscriptionResponse>; agent(options: any): Promise<any>; /** Creates a reusable Agent instance (Vercel AI SDK pattern) */ createAgent(options: any): any; /** Binds an existing Agent instance to this client's httpClient */ bindAgent(agent: any): any; } interface DataExtraction { chunks: string[]; } interface ExtractFromUrlRequest { url: string; chunking?: boolean; chunkSize?: number; } interface ExtractFromUrlResponse { chunks?: string[]; text?: string; } interface ExtractFromBlobResponse { chunks?: string[]; text?: string; } interface ScrapeRequest { url: string; formats?: ('markdown' | 'html' | 'rawHtml' | 'links' | 'extract' | 'metadata')[]; } interface ScrapeResponse { markdown?: string; html?: string; rawHtml?: string; links?: Array<{ text: string; url: string; type: string; }>; extract?: { title?: string; description?: string; headings?: string[]; text?: string; }; metadata?: { title?: string; description?: string; url?: string; domain?: string; favicon?: string; image?: string; author?: string; publishedTime?: string; modifiedTime?: string; type?: string; siteName?: string; locale?: string; keywords?: string[]; }; } interface ScrapeResult { markdown: string; html: string; metadata: { title: string; description: string; url: string; domain: string; favicon?: string; image?: string; author?: string; publishedTime?: string; modifiedTime?: string; type?: string; siteName?: string; locale?: string; keywords?: string[]; }; links: Array<{ text: string; url: string; type: string; }>; extract: { title: string; description: string; headings: string[]; text: string; }; } interface ScreenshotRequest { url: string; fullPage?: boolean; width?: number; height?: number; } interface ScreenshotResponse { url: string; } interface FetchRequest { url: string; method?: string; headers?: Record<string, string>; body?: any; query?: Record<string, string>; async?: boolean; } interface FetchResponse { status: number; headers: Record<string, string>; body: any; durationMs: number; } interface AsyncFetchResponse { status: 'triggered'; message: string; } interface SearchRequest { q: string; location?: string; hl?: string; tbm?: string; num?: number; } interface SearchResponse { organic_results: Array<{ position: number; title: string; link: string; snippet: string; }>; total_results?: string; related_searches?: string[]; people_also_ask?: Array<{ question: string; snippet: string; link: string; }>; local_results?: Array<{ title: string; address: string; rating: number; reviews: number; phone?: string; }>; ads?: Array<{ title: string; link: string; snippet: string; }>; shopping_results?: Array<{ title: string; price: string; source: string; link: string; }>; news_results?: Array<{ title: string; link: string; snippet: string; date: string; source: string; }>; image_results?: Array<{ title: string; link: string; original: string; thumbnail: string; }>; } interface RealtimeMessage { id: string; type: string; data: any; timestamp: number; userId?: string; metadata?: Record<string, any>; } interface PresenceUser { userId: string; metadata?: Record<string, any>; joinedAt: number; lastSeen: number; } interface RealtimeChannel { subscribe(options?: { userId?: string; metadata?: Record<string, any>; }): Promise<void>; unsubscribe(): Promise<void>; publish(type: string, data: any, options?: { userId?: string; metadata?: Record<string, any>; }): Promise<string>; onMessage(callback: (message: RealtimeMessage) => void): () => void; onPresence(callback: (users: PresenceUser[]) => void): () => void; getPresence(): Promise<PresenceUser[]>; getMessages(options?: { limit?: number; before?: string; after?: string; }): Promise<RealtimeMessage[]>; isReady(): boolean; } interface RealtimeSubscribeOptions { userId?: string; metadata?: Record<string, any>; } interface RealtimePublishOptions { userId?: string; metadata?: Record<string, any>; } interface RealtimeGetMessagesOptions { limit?: number; before?: string; after?: string; } interface BlinkRealtime { channel(name: string): RealtimeChannel; subscribe(channelName: string, callback: (message: RealtimeMessage) => void, options?: RealtimeSubscribeOptions): Promise<() => void>; publish(channelName: string, type: string, data: any, options?: RealtimePublishOptions): Promise<string>; presence(channelName: string): Promise<PresenceUser[]>; onPresence(channelName: string, callback: (users: PresenceUser[]) => void): () => void; } declare class BlinkRealtimeError extends BlinkError { constructor(message: string, status?: number, details?: any); } interface SendEmailAttachment { filename: string; url: string; type?: string; content?: string; disposition?: 'attachment' | 'inline'; cid?: string; } interface SendEmailRequest { to: string | string[]; subject: string; html?: string; text?: string; from?: string; replyTo?: string; cc?: string | string[]; bcc?: string | string[]; attachments?: SendEmailAttachment[]; } interface SendEmailResponse { success: boolean; messageId: string; } interface BlinkNotifications { email(params: SendEmailRequest): Promise<SendEmailResponse>; } /** * Token type in Blink Auth system * - `access`: Regular user access token (short-lived) * - `service`: Service token for server-side operations (permanent secret key) */ type BlinkTokenType = 'access' | 'service'; /** * Result of token introspection * Used by edge functions and server-side code to verify user tokens */ interface TokenIntrospectionResult { /** Whether the token is valid */ valid: boolean; /** Project ID from the token */ projectId?: string; /** User ID (Firebase UID) - only present for access tokens */ userId?: string; /** User's email - only present for access tokens */ email?: string; /** Token type: 'access' or 'service' */ tokenType?: BlinkTokenType; /** User's role in the app (if set via app_role claim) */ appRole?: string; /** Token expiration timestamp (Unix seconds) - not present for secret keys */ exp?: number; /** Legacy service key ID (for JWT-based service tokens) */ svcKeyId?: string; /** Error message if token is invalid */ error?: string; } type ConnectorProvider = 'discord' | 'notion' | 'google_drive' | 'google_calendar' | 'ai'; type ConnectorAuthMode = 'oauth' | 'api_key' | 'blink_managed' | 'hybrid'; interface ConnectorStatusData { connected: boolean; provider: ConnectorProvider; auth_mode?: ConnectorAuthMode; account_id?: string; metadata?: Record<string, unknown>; expires_at?: any; scopes?: string[]; } interface ConnectorStatusResponse { success: boolean; data: ConnectorStatusData; } interface ConnectorExecuteRequest<TParams = Record<string, unknown>> { method: string; params?: TParams; account_id?: string; http_method?: string; } interface ConnectorExecuteResponse<TData = any> { success: boolean; data: TData; } interface ConnectorApiKeyRequest<TMetadata = Record<string, unknown>> { api_key: string; account_id?: string; metadata?: TMetadata; } interface ConnectorApiKeyResponse { success: boolean; data: { id: string; account_id?: string; }; } interface BlinkConnectors { status(provider: ConnectorProvider, options?: { account_id?: string; }): Promise<ConnectorStatusResponse>; execute<TParams = Record<string, unknown>, TData = any>(provider: ConnectorProvider, request: ConnectorExecuteRequest<TParams>): Promise<ConnectorExecuteResponse<TData>>; saveApiKey<TMetadata = Record<string, unknown>>(provider: ConnectorProvider, request: ConnectorApiKeyRequest<TMetadata>): Promise<ConnectorApiKeyResponse>; } declare class BlinkConnectorError extends BlinkError { constructor(message: string, status?: number, details?: any); } /** * HTTP client for Blink API requests * Handles authentication, error handling, and request/response processing */ interface RequestOptions { method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; headers?: Record<string, string>; body?: any; searchParams?: Record<string, string>; signal?: AbortSignal; } interface BlinkResponse<T = any> { data: T; status: number; headers: Headers; } declare class HttpClient { private readonly authUrl; private readonly coreUrl; readonly projectId: string; private readonly publishableKey?; private readonly secretKey?; private getToken; private getValidToken?; constructor(config: BlinkClientConfig, getToken: () => string | null, getValidToken?: () => Promise<string | null>); private shouldAttachPublishableKey; private shouldSkipSecretKey; private getAuthorizationHeader; /** * Make an authenticated request to the Blink API */ request<T = any>(path: string, options?: RequestOptions): Promise<BlinkResponse<T>>; /** * GET request */ get<T = any>(path: string, searchParams?: Record<string, string>): Promise<BlinkResponse<T>>; /** * POST request */ post<T = any>(path: string, body?: any, headers?: Record<string, string>): Promise<BlinkResponse<T>>; /** * PATCH request */ patch<T = any>(path: string, body?: any, headers?: Record<string, string>): Promise<BlinkResponse<T>>; /** * DELETE request */ delete<T = any>(path: string, searchParams?: Record<string, string>): Promise<BlinkResponse<T>>; /** * Database-specific requests */ dbGet<T = any>(table: string, searchParams?: Record<string, string>): Promise<BlinkResponse<T[]>>; dbPost<T = any>(table: string, body: any, options?: { returning?: boolean; }): Promise<BlinkResponse<T | T[]>>; dbPatch<T = any>(table: string, body: any, searchParams?: Record<string, string>, options?: { returning?: boolean; }): Promise<BlinkResponse<T[]>>; dbDelete<T = any>(table: string, searchParams?: Record<string, string>, options?: { returning?: boolean; }): Promise<BlinkResponse<T[]>>; dbSql<T = any>(query: string, params?: any[]): Promise<BlinkResponse<{ rows: T[]; columns: string[]; rowCount: number; executionTime: number; }>>; dbBatch<T = any>(statements: Array<{ sql: string; args?: any[]; }>, mode?: 'read' | 'write'): Promise<BlinkResponse<{ results: Array<{ rows: T[]; columns: string[]; rowCount: number; }>; executionTime: number; success: boolean; }>>; /** * Upload file with progress tracking */ uploadFile(path: string, file: File | Blob | Buffer, filePath: string, options?: { upsert?: boolean; onProgress?: (percent: number) => void; contentType?: string; }): Promise<BlinkResponse<any>>; /** * Upload with progress tracking using XMLHttpRequest */ private uploadWithProgress; /** * AI-specific requests */ aiText(prompt: string, options?: { model?: string; messages?: Array<{ role: string; content: string | any[]; }>; stream?: boolean; search?: boolean; maxSteps?: number; experimental_continueSteps?: boolean; maxTokens?: number; temperature?: number; signal?: AbortSignal; }): Promise<BlinkResponse<any>>; /** * Stream AI text generation - uses Vercel AI SDK's pipeUIMessageStreamToResponse (Data Stream Protocol) */ streamAiText(prompt: string, options: { model?: string | undefined; messages?: { role: string; content: string | any[]; }[] | undefined; search?: boolean | undefined; maxSteps?: number | undefined; experimental_continueSteps?: boolean | undefined; maxTokens?: number | undefined; temperature?: number | undefined; signal?: AbortSignal | undefined; } | undefined, onChunk: (chunk: string) => void): Promise<any>; aiObject(prompt: string, options?: { model?: string; output?: 'object' | 'array' | 'enum'; schema?: any; enum?: string[]; stream?: boolean; signal?: AbortSignal; }): Promise<BlinkResponse<any>>; /** * Stream AI object generation - uses Vercel AI SDK's pipeTextStreamToResponse */ streamAiObject(prompt: string, options: { model?: string | undefined; output?: "object" | "array" | "enum" | undefined; schema?: any; enum?: string[] | undefined; signal?: AbortSignal | undefined; } | undefined, onPartial: (partial: any) => void): Promise<any>; aiImage(prompt: string, options?: { model?: string; images?: string[]; size?: string; quality?: 'auto' | 'low' | 'medium' | 'high'; background?: 'auto' | 'transparent' | 'opaque'; n?: number; response_format?: 'url' | 'b64_json'; output_format?: 'png' | 'jpeg' | 'webp'; output_compression?: number; moderation?: 'auto' | 'low'; signal?: AbortSignal; }): Promise<BlinkResponse<any>>; aiSpeech(text: string, options?: { model?: string; voice?: string; response_format?: string; speed?: number; signal?: AbortSignal; }): Promise<BlinkResponse<any>>; aiTranscribe(audio: string | number[] | ArrayBuffer | Uint8Array, options?: { model?: string; language?: string; response_format?: string; signal?: AbortSignal; }): Promise<BlinkResponse<any>>; aiVideo(prompt: string, options?: { model?: string; image_url?: string; duration?: string; aspect_ratio?: string; resolution?: string; negative_prompt?: string; generate_audio?: boolean; seed?: number; cfg_scale?: number; signal?: AbortSignal; }): Promise<BlinkResponse<any>>; /** * AI Agent request (non-streaming) * Returns JSON response with text, steps, usage, and billing */ aiAgent(requestBody: { stream: false; prompt?: string; messages?: Array<{ role: string; content: string | any[]; parts?: any[]; }>; agent: { model: string; system?: string; tools?: string[]; webhook_tools?: Array<{ name: string; description: string; input_schema: any; webhook_url: string; }>; client_tools?: Array<{ name: string; description: string; input_schema: any; }>; tool_choice?: 'auto' | 'required' | 'none'; stop_when?: Array<{ type: string; count: number; }>; prepare_step?: { context_policy: any; }; }; }, signal?: AbortSignal): Promise<BlinkResponse<any>>; /** * AI Agent streaming request * Returns raw Response for SSE streaming (compatible with AI SDK useChat) */ aiAgentStream(requestBody: { stream: true; prompt?: string; messages?: Array<{ role: string; content: string | any[]; parts?: any[]; }>; agent: { model: string; system?: string; tools?: string[]; webhook_tools?: Array<{ name: string; description: string; input_schema: any; webhook_url: string; }>; client_tools?: Array<{ name: string; description: string; input_schema: any; }>; tool_choice?: 'auto' | 'required' | 'none'; stop_when?: Array<{ type: string; count: number; }>; prepare_step?: { context_policy: any; }; }; }, signal?: AbortSignal): Promise<Response>; /** * RAG AI Search streaming request * Returns raw Response for SSE streaming */ ragAiSearchStream(body: { collection_id?: string; collection_name?: string; query: string; model?: string; max_context_chunks?: number; score_threshold?: number; system_prompt?: string; stream: true; }, signal?: AbortSignal): Promise<Response>; /** * Data-specific requests */ dataExtractFromUrl(projectId: string, request: ExtractFromUrlRequest): Promise<BlinkResponse<ExtractFromUrlResponse>>; dataExtractFromBlob(projectId: string, file: File, chunking?: boolean, chunkSize?: number): Promise<BlinkResponse<ExtractFromBlobResponse>>; dataScrape(projectId: string, request: ScrapeRequest): Promise<BlinkResponse<ScrapeResponse>>; dataScreenshot(projectId: string, request: ScreenshotRequest): Promise<BlinkResponse<ScreenshotResponse>>; dataFetch(projectId: string, request: FetchRequest): Promise<BlinkResponse<FetchResponse | AsyncFetchResponse>>; dataSearch(projectId: string, request: SearchRequest): Promise<BlinkResponse<SearchResponse>>; /** * Connector requests */ private formatProviderForPath; connectorStatus(provider: ConnectorProvider): Promise<BlinkResponse<ConnectorStatusResponse>>; connectorExecute<TParams = Record<string, unknown>, TData = any>(provider: ConnectorProvider, request: ConnectorExecuteRequest<TParams>): Promise<BlinkResponse<ConnectorExecuteResponse<TData>>>; connectorSaveApiKey<TMetadata = Record<string, unknown>>(provider: ConnectorProvider, request: ConnectorApiKeyRequest<TMetadata>): Promise<BlinkResponse<ConnectorApiKeyResponse>>; /** * Realtime-specific requests */ realtimePublish(projectId: string, request: { channel: string; type: string; data: any; userId?: string; metadata?: Record<string, any>; }): Promise<BlinkResponse<{ messageId: string; channel: string; timestamp: number; }>>; realtimeGetPresence(projectId: string, channel: string): Promise<BlinkResponse<{ channel: string; users: any[]; count: number; }>>; realtimeGetMessages(projectId: string, options: { channel: string; limit?: number; start?: string; end?: string; }): Promise<BlinkResponse<{ channel: string; messages: any[]; count: number; hasMore: boolean; }>>; /** * Private helper methods */ private buildUrl; private parseResponse; private handleErrorResponse; /** * Parse Vercel AI SDK v5 Data Stream Protocol (Server-Sent Events) * Supports all event types from the UI Message Stream protocol */ private parseDataStreamProtocol; } /** * Platform detection for cross-platform compatibility * Detects whether code is running on web, React Native, Node.js, or Deno */ type Platform = 'web' | 'react-native' | 'node' | 'deno'; /** * Current platform */ declare const platform: Platform; /** * Platform detection helpers */ declare const isWeb: boolean; declare const isReactNative: boolean; declare const isNode: boolean; declare const isDeno: boolean; declare const isBrowser: boolean; declare const isServer: boolean; /** * Blink Auth Module - Client-side authentication management * Handles token storage, user state, and authentication flows */ type AuthStateChangeCallback = (state: AuthState) => void; declare class BlinkAuth { private config; private authConfig; private authState; private listeners; private readonly authUrl; private readonly coreUrl; private parentWindowTokens; private isIframe; private initializationPromise; private isInitialized; private storage; constructor(config: BlinkClientConfig); /** * Generate project-scoped storage key */ private getStorageKey; /** * Migrate existing global tokens to project-scoped storage * DISABLED: We don't migrate global blink_tokens anymore because: * 1. Platform uses blink_tokens for platform auth (different user) * 2. Migrating platform tokens would cause project to show wrong user * 3. Projects should always authenticate fresh via their own flow */ private migrateExistingTokens; /** * Wait for authentication initialization to complete */ private waitForInitialization; /** * Setup listener for tokens from parent window */ private setupParentWindowListener; /** * Initialize authentication from stored tokens or URL fragments */ initialize(): Promise<void>; /** * Redirect to Blink auth page */ login(nextUrl?: string): void; /** * Logout and clear stored tokens */ logout(redirectUrl?: string): void; /** * Check if user is authenticated */ isAuthenticated(): boolean; /** * Get current user (sync) */ currentUser(): BlinkUser | null; /** * Get current access token */ getToken(): string | null; /** * Check if access token is expired based on timestamp */ private isAccessTokenExpired; /** * Check if refresh token is expired based on timestamp */ private isRefreshTokenExpired; /** * Get a valid access token, refreshing if necessary */ getValidToken(): Promise<string | null>; /** * Fetch current user profile from API * Gracefully waits for auth initialization to complete before throwing errors */ me(): Promise<BlinkUser>; /** * Sign up with email and password (headless mode) */ signUp(data: SignUpData): Promise<BlinkUser>; /** * Sign in with email and password (headless mode) */ signInWithEmail(email: string, password: string): Promise<BlinkUser>; /** * Sign in with Google (headless mode) * * **Universal OAuth** - Works on both Web and React Native! * * On React Native, requires `webBrowser` to be configured in client: * ```typescript * const blink = createClient({ * auth: { mode: 'headless', webBrowser: WebBrowser } * }) * await blink.auth.signInWithGoogle() // Works on both platforms! * ``` */ signInWithGoogle(options?: AuthOptions): Promise<BlinkUser>; /** * Sign in with GitHub (headless mode) * * **Universal OAuth** - Works on both Web and React Native! * See signInWithGoogle() for setup instructions. */ signInWithGitHub(options?: AuthOptions): Promise<BlinkUser>; /** * Sign in with Apple (headless mode) * * **Universal OAuth** - Works on both Web and React Native! * See signInWithGoogle() for setup instructions. */ signInWithApple(options?: AuthOptions): Promise<BlinkUser>; /** * Sign in with Microsoft (headless mode) * * **Universal OAuth** - Works on both Web and React Native! * See signInWithGoogle() for setup instructions. */ signInWithMicrosoft(options?: AuthOptions): Promise<BlinkUser>; /** * Initiate OAuth for mobile without deep linking (expo-web-browser pattern) * * This method: * 1. Generates a unique session ID * 2. Returns OAuth URL with session parameter * 3. App opens URL in expo-web-browser * 4. App polls checkMobileOAuthSession() until complete * * @param provider - OAuth provider (google, github, apple, etc.) * @param options - Optional metadata * @returns Session ID and OAuth URL * * @example * // React Native with expo-web-browser * import * as WebBrowser from 'expo-web-browser'; * * const { sessionId, authUrl } = await blink.auth.initiateMobileOAuth('google'); * * // Open browser * await WebBrowser.openAuthSessionAsync(authUrl); * * // Poll for completion * const user = await blink.auth.pollMobileOAuthSession(sessionId); * console.log('Authenticated:', user.email); */ initiateMobileOAuth(provider: AuthProvider, options?: Omit<AuthOptions, 'redirectUrl'>): Promise<{ sessionId: string; authUrl: string; }>; /** * Check mobile OAuth session status (single check) * * @param sessionId - Session ID from initiateMobileOAuth * @returns Tokens if session is complete, null if still pending */ checkMobileOAuthSession(sessionId: string): Promise<AuthTokens | null>; /** * Poll mobile OAuth session until complete (convenience method) * * @param sessionId - Session ID from initiateMobileOAuth * @param options - Polling options * @returns Authenticated user * * @example * const { sessionId, authUrl } = await blink.auth.initiateMobileOAuth('google'); * await WebBrowser.openAuthSessionAsync(authUrl); * const user = await blink.auth.pollMobileOAuthSession(sessionId, { * maxAttempts: 60, * intervalMs: 1000 * }); */ pollMobileOAuthSession(sessionId: string, options?: { maxAttempts?: number; intervalMs?: number; }): Promise<BlinkUser>; /** * Sign in with OAuth provider using expo-web-browser (React Native) * * This is a convenience method that handles the entire flow: * 1. Initiates mobile OAuth session * 2. Returns auth URL to open in WebBrowser * 3. Provides polling function to call after browser opens * * @param provider - OAuth provider * @returns Object with authUrl and authenticate function * * @example * import * as WebBrowser from 'expo-web-browser'; * * const { authUrl, authenticate } = await blink.auth.signInWithProviderMobile('google'); * * // Open browser * await WebBrowser.openAuthSessionAsync(authUrl); * * // Wait for authentication * const user = await authenticate(); */ signInWithProviderMobile(provider: AuthProvider, options?: Omit<AuthOptions, 'redirectUrl'>): Promise<{ authUrl: string; authenticate: () => Promise<BlinkUser>; }>; /** * Universal OAuth flow using session-based authentication (internal) * Works on ALL platforms: Web, iOS, Android * Uses expo-web-browser to open auth URL and polls for completion */ private signInWithProviderUniversal; /** * Generic provider sign-in method (headless mode) * * **Universal OAuth** - Works seamlessly on both Web and React Native! * * When `webBrowser` is configured in the client, this method automatically * uses the session-based OAuth flow that works on ALL platforms. * * **Universal Setup (configure once, works everywhere):** * ```typescript * import * as WebBrowser from 'expo-web-browser' * import AsyncStorage from '@react-native-async-storage/async-storage' * * const blink = createClient({ * projectId: 'your-project', * auth: { * mode: 'headless', * webBrowser: WebBrowser // Pass the module here * }, * storage: new AsyncStorageAdapter(AsyncStorage) * }) * * // Now this works on ALL platforms - no platform checks needed! * const user = await blink.auth.signInWithGoogle() * ``` * * @param provider - OAuth provider (google, github, apple, etc.) * @param options - Optional redirect URL and metadata * @returns Promise that resolves with authenticated user */ signInWithProvider(provider: AuthProvider, options?: AuthOptions): Promise<BlinkUser>; /** * Generate password reset token (for custom email delivery) */ generatePasswordResetToken(email: string): Promise<{ token: string; expiresAt: string; resetUrl: string; }>; /** * Send password reset email (using Blink default email service) */ sendPasswordResetEmail(email: string, options?: { redirectUrl?: string; }): Promise<void>; /** * Confirm password reset with token */ confirmPasswordReset(token: string, newPassword: string): Promise<void>; /** * Change password (requires current authentication) */ changePassword(oldPassword: string, newPassword: string): Promise<void>; /** * Generate email verification token (for custom email delivery) */ generateEmailVerificationToken(): Promise<{ token: string; expiresAt: string; verifyUrl: string; }>; /** * Send email verification (using Blink default email service) */ sendEmailVerification(): Promise<void>; /** * Verify email with token */ verifyEmail(token: string): Promise<void>; /** * Generate magic link token (for custom email delivery) */ generateMagicLinkToken(email: string, options?: MagicLinkOptions): Promise<{ token: string; expiresAt: string; magicUrl: string; }>; /** * Send magic link (using Blink default email service) */ sendMagicLink(email: string, options?: MagicLinkOptions): Promise<void>; /** * Verify magic link (automatic on redirect) */ verifyMagicLink(token?: string): Promise<BlinkUser>; /** * Get available providers for the current project */ getAvailableProviders(): Promise<AuthProvider[]>; /** * Check if user has a specific role */ hasRole(role: string | string[]): boolean; /** * Check if user can perform a specific action */ can(permission: string, resource?: string): boolean; /** * Sign out (clear local tokens) * Note: With stateless tokens, this only clears local storage */ signOut(): Promise<void>; /** * @deprecated Use signOut() instead. Kept for backward compatibility. */ revokeAllSessions(): Promise<void>; /** * Recover auth state (clear corrupted tokens and re-initialize) */ recoverAuthState(): Promise<void>; /** * Update user profile */ updateMe(updates: Partial<BlinkUser>): Promise<BlinkUser>; /** * Manually set tokens (for server-side usage) */ setToken(jwt: string, persist?: boolean): Promise<void>; /** * Manually set auth session from tokens (React Native deep link OAuth) * * Use this method to set the user session after receiving tokens from a deep link callback. * This is the React Native equivalent of automatic URL token detection on web. * * @param tokens - Auth tokens received from deep link or OAuth callback * @param persist - Whether to persist tokens to storage (default: true) * * @example * // React Native: Handle deep link OAuth callback * import * as Linking from 'expo-linking' * * Linking.addEventListener('url', async ({ url }) => { * const { queryParams } = Linking.parse(url) * * if (queryParams.access_token) { * await blink.auth.setSession({ * access_token: queryParams.access_token, * refresh_token: queryParams.refresh_token, * expires_in: parseInt(queryParams.expires_in) || 3600, * refresh_expires_in: parseInt(queryParams.refresh_expires_in) * }) * * console.log('User authenticated:', blink.auth.currentUser()) * } * }) */ setSession(tokens: { access_token: string; refresh_token?: string; expires_in?: number; refresh_expires_in?: number; }, persist?: boolean): Promise<BlinkUser>; /** * Verify a Blink Auth token using the introspection endpoint. * * **Server-side / Edge Function use only.** * * This is the recommended way to verify user tokens in Deno Edge Functions * and other server-side contexts. It calls the Blink API introspection * endpoint which validates the token without exposing the JWT secret. * * @param token - The raw JWT token (without "Bearer " prefix) or full Authorization header * @returns Token introspection result with validity and claims * * @example * // Deno Edge Function usage * import { createClient } from "npm:@blinkdotnew/sdk"; * * const blink = createClient({ * projectId: Deno.env.get("BLINK_PROJECT_ID")!, * secretKey: Deno.env.get("BLINK_SECRET_KEY"), * }); * * async function handler(req: Request): Promise