@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
TypeScript
/**
* 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