@nestcorehq/js 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/lib/client.ts","../src/lib/database.ts","../src/lib/functions.ts","../src/lib/jobs.ts","../src/lib/storage-adapters.ts","../src/lib/auth.ts","../src/lib/logging.ts","../src/lib/environment.ts","../src/lib/validation.ts","../src/lib/storage.ts","../src/lib/realtime.ts","../src/lib/analytics.ts","../src/lib/sse.ts","../src/lib/ai-data.ts","../src/lib/ai.ts","../src/lib/typegen.ts","../src/lib/errors.ts"],"sourcesContent":["import { HttpClient } from './lib/client';\nimport { DatabaseClient } from './lib/database';\nimport { FunctionsClient } from './lib/functions';\nimport { JobsClient } from './lib/jobs';\nimport { AuthClient } from './lib/auth';\nimport { LoggingClient } from './lib/logging';\nimport { EnvironmentClient } from './lib/environment';\nimport { StorageClient } from './lib/storage';\nimport { RealtimeClient } from './lib/realtime';\nimport { AnalyticsClient } from './lib/analytics';\nimport { AiClient } from './lib/ai';\nimport { MemoryClient, CollectionsClient } from './lib/ai-data';\nimport type { NestCoreConfig } from './types';\nimport type { RealtimeOptions } from './lib/realtime';\nimport { type StorageAdapter, localStorageAdapter, memoryAdapter, createLocalStorageAdapter, createMemoryAdapter } from './lib/storage-adapters';\n\nexport * from './types';\nexport * from './lib/logging';\nexport * from './lib/environment';\nexport * from './lib/storage';\nexport * from './lib/realtime';\nexport * from './lib/analytics';\nexport * from './lib/ai';\nexport * from './lib/ai-data';\nexport { generateTypes, type GenerateTypesOptions } from './lib/typegen';\nexport { validatePath, sanitizePath, validateRequired } from './lib/validation';\nexport { NestCoreError, OrbitNestError, unwrap, AuthLimitCodes } from './lib/errors';\nexport { isMfaChallenge, type MfaChallenge, type MfaFactor } from './lib/auth';\nexport {\n type StorageAdapter,\n localStorageAdapter,\n memoryAdapter,\n createLocalStorageAdapter,\n createMemoryAdapter,\n};\n\nconst DEFAULT_BASE_URL = 'https://api.nestcore.io';\n\n/**\n * Decode the `project_slug` claim from an NestCore API key (JWT).\n *\n * The signing key isn't needed — we only read the payload to look up which\n * project the key belongs to. Throws when the token isn't a JWT or doesn't\n * carry a `project_slug` claim, which signals a misconfigured key.\n */\nfunction decodeProjectSlugFromJwt(token: string): string {\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new Error('NestCore: apiKey is not a valid JWT (expected 3 segments)');\n }\n let payload = parts[1];\n // base64url → base64 padding\n payload = payload.replace(/-/g, '+').replace(/_/g, '/');\n switch (payload.length % 4) {\n case 2: payload += '=='; break;\n case 3: payload += '='; break;\n }\n let json: Record<string, unknown>;\n try {\n // Browsers expose `atob`; Node (for SSR/isomorphic use) exposes `Buffer`.\n const decoded = typeof atob !== 'undefined'\n ? atob(payload)\n : Buffer.from(payload, 'base64').toString('utf8');\n json = JSON.parse(decoded) as Record<string, unknown>;\n } catch {\n throw new Error('NestCore: could not decode apiKey JWT payload');\n }\n const slug = json['project_slug'];\n if (typeof slug !== 'string' || slug.length === 0) {\n throw new Error('NestCore: apiKey JWT does not contain a project_slug claim');\n }\n return slug;\n}\n\n/** @deprecated Renamed to {@link NestCoreClient}. */\nexport type OrbitNestClient = NestCoreClient;\n\nexport interface NestCoreClient {\n db: DatabaseClient;\n functions: FunctionsClient;\n /** @deprecated Needs a console admin token; every call returns an `ADMIN_ONLY` error. */\n jobs: JobsClient;\n auth: AuthClient;\n /** @deprecated Needs a console admin token; every call returns an `ADMIN_ONLY` error. */\n logs: LoggingClient;\n /** @deprecated Needs a console admin token; every call returns an `ADMIN_ONLY` error. */\n env: EnvironmentClient;\n storage: StorageClient;\n analytics: AnalyticsClient;\n /** AI Gateway: chat, streaming and embeddings with the project's own provider keys; `ai.threads` for conversations. */\n ai: AiClient;\n /** Memory API: facts about a user or agent, with semantic search. */\n memory: MemoryClient;\n /** Vector collections: ingest documents, hybrid search. */\n collections: CollectionsClient;\n /**\n * Create a realtime channel for live DB-change subscriptions and broadcasts.\n *\n * ```ts\n * const ch = client.channel('orders')\n * .on('postgres_changes',\n * { event: 'INSERT', table: 'orders', filter: { user_id: { eq: userId } } },\n * (e) => console.log('new order', e.new))\n * .subscribe();\n * ```\n *\n * In the browser this uses the native `WebSocket`. For isomorphic/SSR use on\n * Node.js < 22, pass a constructor: `realtimeOptions: { WebSocket }`.\n */\n channel: (name: string) => ReturnType<RealtimeClient['channel']>;\n /** Direct access to the underlying RealtimeClient. */\n realtime: RealtimeClient;\n}\n\n/**\n * Create an NestCore client for the **browser** (and other DOM/fetch\n * environments).\n *\n * Only `apiKey` is required — the project slug is decoded from the API key's\n * JWT payload and `baseUrl` defaults to `https://api.nestcore.io`.\n *\n * Pass a `storage` adapter to persist the auth session across page loads\n * (`localStorageAdapter` keeps users signed in after a refresh; the default is\n * in-memory). Use a **publishable anon key** in client-side code — never a\n * service-role key.\n *\n * ```ts\n * import { createClient, localStorageAdapter } from '@nestcorehq/js';\n *\n * const nestcore = createClient({\n * apiKey: import.meta.env.VITE_NESTCORE_ANON_KEY,\n * storage: localStorageAdapter, // keep the session after refresh\n * });\n *\n * const { data, error } = await nestcore.db.from('posts').eq('published', true).select();\n * ```\n */\nexport function createClient(\n config: NestCoreConfig & { realtimeOptions?: RealtimeOptions; storage?: StorageAdapter },\n): NestCoreClient {\n if (!config.apiKey) {\n throw new Error('NestCore: apiKey is required');\n }\n\n // Resolve projectSlug: caller override → JWT payload claim\n const projectSlug = config.projectSlug ?? decodeProjectSlugFromJwt(config.apiKey);\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n\n const httpClient = new HttpClient({ ...config, projectSlug, baseUrl });\n\n // The project slug is not a secret, so it stays in the URL query string.\n // The apiKey is a credential and must NOT appear in the URL, where it would\n // leak into proxy/CDN access logs, APM traces, and browser history. It is\n // passed to RealtimeClient and sent on the WS handshake via subprotocol (the\n // only credential channel a browser WebSocket allows).\n const wsUrl = `${baseUrl.replace(/^http/, 'ws')}/realtime/v1/ws?project=${encodeURIComponent(projectSlug)}`;\n const auth = new AuthClient(httpClient, { storage: config.storage });\n // Data, storage and function calls run as the signed-in user (RLS\n // `auth.uid()`), refreshing the access token shortly before it expires.\n httpClient.setAccessTokenProvider(() => auth.getAccessToken(), (stale) => auth.refreshAccessToken(stale));\n\n // An explicit realtimeOptions.userJwt wins; otherwise realtime follows auth.\n const followAuth = !config.realtimeOptions?.userJwt;\n const realtimeClient = new RealtimeClient(wsUrl, {\n ...config.realtimeOptions,\n apiKey: config.apiKey,\n ...(followAuth\n ? {\n accessToken: () => auth.getAccessToken(),\n // A refresh notifies onAuthStateChange → setAuth, which reconnects.\n onAuthRejected: (stale: string) => { void auth.refreshAccessToken(stale); },\n }\n : {}),\n });\n if (followAuth) auth.onAuthStateChange((s) => realtimeClient.setAuth(s?.access_token ?? null));\n\n const db = new DatabaseClient(httpClient);\n\n return {\n db,\n functions: new FunctionsClient(httpClient),\n jobs: new JobsClient(httpClient),\n auth,\n logs: new LoggingClient(httpClient),\n env: new EnvironmentClient(httpClient),\n storage: new StorageClient(httpClient),\n channel: (name: string) => realtimeClient.channel(name),\n realtime: realtimeClient,\n analytics: new AnalyticsClient(httpClient),\n ai: new AiClient(httpClient),\n memory: new MemoryClient(httpClient),\n collections: new CollectionsClient(httpClient),\n };\n}\n\nexport {\n HttpClient, DatabaseClient, FunctionsClient, JobsClient,\n AuthClient, LoggingClient, EnvironmentClient, StorageClient,\n RealtimeClient, AnalyticsClient,\n};\n","import type { NestCoreConfig, RequestOptions, ApiResult, ApiError } from '../types';\n\n// Internal config — by the time it reaches the HttpClient the slug has been\n// resolved (decoded from JWT or supplied by the caller), so it's required.\ntype ResolvedConfig = Omit<NestCoreConfig, 'projectSlug'> & { projectSlug: string };\n\nexport class HttpClient {\n private baseUrl: string;\n private apiKey: string;\n private projectSlug: string;\n private timeout: number;\n private analyticsToken: string | undefined;\n private clientIp: string | undefined;\n private accessTokenProvider?: () => Promise<string | null>;\n private tokenRefresher?: (staleToken: string) => Promise<string | null>;\n\n constructor(config: ResolvedConfig) {\n this.baseUrl = (config.baseUrl || 'https://api.nestcore.io').replace(/\\/$/, '');\n this.apiKey = config.apiKey;\n this.projectSlug = config.projectSlug;\n this.timeout = config.timeout || 30000;\n this.analyticsToken = config.analyticsToken;\n this.clientIp = config.clientIp;\n }\n\n getProjectSlug(): string {\n return this.projectSlug;\n }\n\n /** Configured API origin (e.g. `https://api.nestcore.io`) without trailing slash. */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n /** Default request timeout in ms. */\n getTimeout(): number {\n return this.timeout;\n }\n\n /** API key (used by storage methods that bypass the JSON request helper). */\n getApiKey(): string {\n return this.apiKey;\n }\n\n /**\n * Supply the signed-in user's access token (or null). `createClient` wires\n * this to the AuthClient so data, storage and function calls run as the user.\n * `refresh` is called once when the API rejects that token (401); it returns\n * a fresh token (or null when the session can't be renewed).\n */\n setAccessTokenProvider(\n fn: () => Promise<string | null>,\n refresh?: (staleToken: string) => Promise<string | null>,\n ): void {\n this.accessTokenProvider = fn;\n this.tokenRefresher = refresh;\n }\n\n /**\n * Credential headers for a project request. `apikey` always carries the\n * project key; `Authorization` carries the user's access token when signed\n * in (the project key otherwise). Matches the API's ApiKeyGuard precedence.\n */\n async authHeaders(): Promise<Record<string, string>> {\n return this.headersFor(this.accessTokenProvider ? await this.accessTokenProvider() : null);\n }\n\n /**\n * `fetch` with project credentials ahead of `init.headers`. As the user\n * (`asUser`), a 401 for the user token refreshes the session and retries\n * once; a second 401 is returned as-is (never a sign-out by itself).\n */\n async authFetch(\n url: string,\n init: RequestInit & { headers?: Record<string, string> },\n asUser = true,\n ): Promise<Response> {\n const token = asUser && this.accessTokenProvider ? await this.accessTokenProvider() : null;\n const send = (t: string | null) => fetch(url, { ...init, headers: { ...this.headersFor(t), ...init.headers } });\n const res = await send(token);\n if (res.status !== 401 || !token || !this.tokenRefresher) return res;\n const fresh = await this.tokenRefresher(token);\n if (!fresh || fresh === token) return res;\n await res.body?.cancel().catch(() => {});\n // FormData / string bodies are re-sendable; a one-shot stream would not be.\n return send(fresh);\n }\n\n async request<T>(path: string, options: RequestOptions = {}): Promise<ApiResult<T>> {\n return this._fetch<T>(path, options, true);\n }\n\n /**\n * Same as `request()` but never attaches the user session, only the project\n * key. Auth endpoints (sign-in, refresh, …) use it so refreshing a session\n * can't recurse into itself.\n */\n async requestPublic<T>(path: string, options: RequestOptions = {}): Promise<ApiResult<T>> {\n return this._fetch<T>(path, options, false);\n }\n\n /** Analytics-specific request — adds `x-analytics-token` header when configured. */\n async requestForAnalytics<T>(path: string, options: RequestOptions = {}): Promise<ApiResult<T>> {\n const extraHeaders = this.analyticsToken\n ? { 'x-analytics-token': this.analyticsToken }\n : undefined;\n return this._fetch<T>(path, { ...options, headers: { ...extraHeaders, ...options.headers } }, false);\n }\n\n /**\n * Same as `request()` but sends the supplied user access token as the\n * Bearer auth header. Used for endpoints that authenticate as a project\n * user (e.g. passkey device management).\n */\n async requestWithToken<T>(\n path: string,\n accessToken: string,\n options: RequestOptions = {},\n ): Promise<ApiResult<T>> {\n return this._fetch<T>(path, { ...options, headers: { Authorization: `Bearer ${accessToken}`, ...options.headers } }, false);\n }\n\n private headersFor(token: string | null): Record<string, string> {\n return {\n apikey: this.apiKey,\n Authorization: `Bearer ${token || this.apiKey}`,\n ...(this.clientIp ? { 'X-NestCore-Client-IP': this.clientIp } : {}),\n };\n }\n\n private async _fetch<T>(\n path: string,\n options: RequestOptions,\n asUser: boolean,\n ): Promise<ApiResult<T>> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), options.timeout || this.timeout);\n\n try {\n const response = await this.authFetch(url, {\n method: options.method || 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n keepalive: options.keepalive,\n // Fail loudly on unexpected redirects rather than silently forwarding\n // the Bearer credential to the redirect target.\n redirect: 'error',\n }, asUser);\n\n clearTimeout(timeoutId);\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n return {\n data: null,\n error: {\n message: errorMessage(data, response.status),\n code: data?.code || data?.statusCode?.toString(),\n status: response.status,\n // Structured fields, e.g. QUOTA_EXCEEDED's quota/plan/limit/usage.\n details: data ?? undefined,\n },\n };\n }\n\n return { data: data as T, error: null };\n } catch (err) {\n clearTimeout(timeoutId);\n\n if (err instanceof Error && err.name === 'AbortError') {\n return {\n data: null,\n error: { message: 'Request timeout', code: 'TIMEOUT' },\n };\n }\n\n return {\n data: null,\n error: {\n message: err instanceof Error ? err.message : 'Unknown error',\n code: 'NETWORK_ERROR',\n },\n };\n }\n }\n}\n\n/** Readable message from an API error body (NestJS validation sends `message: string[]`). */\nexport function errorMessage(body: unknown, status: number, fallback?: string): string {\n const b = body as { message?: unknown; error?: unknown } | null;\n const m = b?.message ?? b?.error;\n if (Array.isArray(m)) return m.join('; ');\n if (typeof m === 'string' && m) return m;\n const nested = (m as { message?: unknown } | null)?.message;\n if (typeof nested === 'string' && nested) return nested;\n return fallback ?? `Request failed with status ${status}`;\n}\n\n/**\n * `jobs`, `env` and `logs` map to console-admin routes (admin JWT + project\n * UUID, owner only). A project anon/service key can never call them, so these\n * clients fail fast instead of sending a request that always 401s.\n */\nexport function adminOnlyError(feature: string): ApiError {\n return {\n data: null,\n error: {\n message:\n `NestCore: ${feature} requires a NestCore console admin token and is not available with a project API key. ` +\n 'Manage it in the NestCore console, the NestCore CLI or the MCP server.',\n code: 'ADMIN_ONLY',\n },\n };\n}\n","import type { HttpClient } from './client';\r\nimport type {\r\n QueryResult,\r\n TableMetadata,\r\n PaginationOptions,\r\n RlsPolicy,\r\n ApiResult,\r\n} from '../types';\r\n\r\nexport class DatabaseClient {\r\n constructor(private client: HttpClient) {}\r\n\r\n /**\r\n * Get the base path for client database operations.\r\n *\r\n * Note: this path is intentionally **singular** (`/api/project/:slug/...`).\r\n * The plural form (`/api/projects/:projectId/...`) is reserved for the\r\n * admin/console routes which authenticate with the global JWT_SECRET. The\r\n * client SDK uses a per-project anon/service key, so it must hit the\r\n * singular endpoint where `ApiKeyGuard` validates with the project's own\r\n * `jwt_secret`.\r\n */\r\n private get basePath(): string {\r\n return `/api/project/${this.client.getProjectSlug()}/database`;\r\n }\r\n\r\n /**\r\n * Execute a raw SQL query with optional parameter binding.\r\n *\r\n * Params are bound on the backend via `pg`'s native $1/$2/... placeholders\r\n * (see studio_api `ProjectDatabaseUtil.query`). Always prefer this over\r\n * string interpolation when you have untrusted input:\r\n *\r\n * db.query('SELECT * FROM users WHERE email = $1', [email])\r\n */\r\n /**\r\n * Rows nearest to `query` by meaning, from a column embedded with automatic\r\n * embeddings (Console → AI → Embeddings). Runs under RLS as the signed-in user.\r\n * Each row carries `_similarity` (cosine) and `_score`. With `hybridColumn`,\r\n * keyword matches on that text column are blended in (reciprocal rank fusion).\r\n * Usually reached through `db.from(table).eq(...).semantic(column, query)`.\r\n */\r\n async semantic<T = Record<string, unknown>>(\r\n table: string,\r\n column: string,\r\n query: string,\r\n opts: SemanticOptions & { filters?: SemanticFilter[] } = {},\r\n ): Promise<ApiResult<{ rows: Array<T & { _similarity: number | null; _score: number }>; model: string | null }>> {\r\n return this.client.request(`${this.basePath}/semantic`, { method: 'POST', body: { table, column, query, ...opts } });\r\n }\r\n\r\n async query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<ApiResult<QueryResult<T>>> {\r\n const result = await this.client.request<{\r\n success: boolean;\r\n data: T[];\r\n rows_affected: number;\r\n columns?: Array<{ name: string; type: string }>;\r\n }>(`${this.basePath}/sql`, {\r\n method: 'POST',\r\n body: params && params.length > 0 ? { sql, params } : { sql },\r\n });\r\n\r\n if (result.error) {\r\n return result as ApiResult<QueryResult<T>>;\r\n }\r\n\r\n // Transform API response to QueryResult format\r\n return {\r\n data: {\r\n rows: result.data.data || [],\r\n rowCount: result.data.rows_affected || 0,\r\n fields: result.data.columns?.map(col => ({ name: col.name, dataType: col.type })),\r\n },\r\n error: null,\r\n };\r\n }\r\n\r\n /**\r\n * Get all tables in the database\r\n */\r\n async listTables(): Promise<ApiResult<string[]>> {\r\n return this.client.request<string[]>(`${this.basePath}/tables/list`);\r\n }\r\n\r\n /**\r\n * Get table metadata including columns. The API returns one entry per\r\n * matching table (an empty array when the table doesn't exist).\r\n */\r\n async getTableMetadata(tableName: string): Promise<ApiResult<TableMetadata[]>> {\r\n const query = new URLSearchParams({ table: tableName });\r\n return this.client.request<TableMetadata[]>(`${this.basePath}/tables?${query.toString()}`);\r\n }\r\n\r\n /**\r\n * Get table data with pagination\r\n */\r\n async getTableData<T = Record<string, unknown>>(\r\n tableName: string,\r\n options: PaginationOptions = {}\r\n ): Promise<ApiResult<{ rows: T[]; total: number }>> {\r\n const params = new URLSearchParams();\r\n if (options.page) params.set('page', options.page.toString());\r\n if (options.limit) params.set('limit', options.limit.toString());\r\n if (options.sortBy) params.set('sortBy', options.sortBy);\r\n if (options.sortOrder) params.set('sortOrder', options.sortOrder);\r\n\r\n const query = params.toString() ? `?${params.toString()}` : '';\r\n const result = await this.client.request<{ data: T[]; total_rows: number }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/data${query}`\r\n );\r\n if (result.error) return result;\r\n return { data: { rows: result.data.data ?? [], total: result.data.total_rows ?? 0 }, error: null };\r\n }\r\n\r\n /**\r\n * Insert a row into a table\r\n * Returns the inserted row data\r\n */\r\n async insert<T = Record<string, unknown>>(\r\n tableName: string,\r\n data: Record<string, unknown>\r\n ): Promise<ApiResult<T>> {\r\n const result = await this.client.request<{\r\n success: boolean;\r\n data: T[];\r\n rows_affected: number;\r\n message: string;\r\n }>(`${this.basePath}/tables/${encodeURIComponent(tableName)}/rows`, {\r\n method: 'POST',\r\n body: data,\r\n });\r\n\r\n if (result.error) {\r\n return result as ApiResult<T>;\r\n }\r\n\r\n // Return the first inserted row\r\n return {\r\n data: result.data.data?.[0] || (result.data as unknown as T),\r\n error: null,\r\n };\r\n }\r\n\r\n /**\r\n * Update a row by ID\r\n * Note: Requires service_role_key for authentication\r\n */\r\n async update<T = Record<string, unknown>>(\r\n tableName: string,\r\n rowId: string | number,\r\n data: Record<string, unknown>\r\n ): Promise<ApiResult<T>> {\r\n const result = await this.client.request<{\r\n success: boolean;\r\n data: T[];\r\n message: string;\r\n }>(`${this.basePath}/tables/${encodeURIComponent(tableName)}/rows/${encodeURIComponent(String(rowId))}`, {\r\n method: 'PUT',\r\n body: data,\r\n });\r\n\r\n if (result.error) {\r\n return result as ApiResult<T>;\r\n }\r\n\r\n // The API returns the updated rows as an array; return the row.\r\n return {\r\n data: result.data.data?.[0] ?? (result.data as unknown as T),\r\n error: null,\r\n };\r\n }\r\n\r\n /**\r\n * Delete a row by ID\r\n * Note: Requires service_role_key for authentication\r\n */\r\n async delete(tableName: string, rowId: string | number): Promise<ApiResult<{ success: boolean }>> {\r\n return this.client.request<{ success: boolean }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/rows/${encodeURIComponent(String(rowId))}`,\r\n { method: 'DELETE' }\r\n );\r\n }\r\n\r\n /**\r\n * Bulk insert rows. Returns the inserted rows.\r\n * Note: Requires service_role_key for authentication\r\n */\r\n async bulkInsert<T = Record<string, unknown>>(\r\n tableName: string,\r\n rows: Record<string, unknown>[]\r\n ): Promise<ApiResult<T[]>> {\r\n const result = await this.client.request<{ data?: T[] }>(`${this.basePath}/tables/${encodeURIComponent(tableName)}/rows/bulk`, {\r\n method: 'POST',\r\n body: rows,\r\n });\r\n if (result.error) return result;\r\n return { data: result.data.data ?? [], error: null };\r\n }\r\n\r\n /**\r\n * Bulk update rows. Returns the number of rows updated.\r\n * Note: Requires a service_role key or a signed-in user (RLS applies)\r\n */\r\n async bulkUpdate(\r\n tableName: string,\r\n updates: Array<{ where: Record<string, unknown>; data: Record<string, unknown> }>\r\n ): Promise<ApiResult<{ updated: number }>> {\r\n const result = await this.client.request<{ rows_affected?: number }>(`${this.basePath}/tables/${encodeURIComponent(tableName)}/rows/bulk`, {\r\n method: 'PUT',\r\n body: updates,\r\n });\r\n if (result.error) return result;\r\n return { data: { updated: result.data.rows_affected ?? 0 }, error: null };\r\n }\r\n\r\n /**\r\n * Bulk delete rows. Returns the number of rows deleted.\r\n * Note: Requires a service_role key or a signed-in user (RLS applies)\r\n */\r\n async bulkDelete(\r\n tableName: string,\r\n conditions: Record<string, unknown>[]\r\n ): Promise<ApiResult<{ deleted: number }>> {\r\n const result = await this.client.request<{ rows_affected?: number }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/rows/bulk`,\r\n {\r\n method: 'DELETE',\r\n body: conditions,\r\n }\r\n );\r\n if (result.error) return result;\r\n return { data: { deleted: result.data.rows_affected ?? 0 }, error: null };\r\n }\r\n\r\n /**\r\n * Enable Row Level Security on a table\r\n */\r\n async enableRls(tableName: string): Promise<ApiResult<{ success: boolean }>> {\r\n return this.client.request<{ success: boolean }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/rls/enable`,\r\n { method: 'POST' }\r\n );\r\n }\r\n\r\n /**\r\n * Disable Row Level Security on a table\r\n */\r\n async disableRls(tableName: string): Promise<ApiResult<{ success: boolean }>> {\r\n return this.client.request<{ success: boolean }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/rls/disable`,\r\n { method: 'POST' }\r\n );\r\n }\r\n\r\n /**\r\n * Create an RLS policy\r\n */\r\n async createPolicy(tableName: string, policy: RlsPolicy): Promise<ApiResult<{ success: boolean }>> {\r\n return this.client.request<{ success: boolean }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/policies`,\r\n {\r\n method: 'POST',\r\n body: policy,\r\n }\r\n );\r\n }\r\n\r\n /**\r\n * List RLS policies for a table\r\n */\r\n async listPolicies(tableName: string): Promise<ApiResult<RlsPolicy[]>> {\r\n const result = await this.client.request<{\r\n policies?: Array<{ name: string; command: RlsPolicy['command']; role?: string; using?: string | null; with_check?: string | null }>;\r\n }>(`${this.basePath}/tables/${encodeURIComponent(tableName)}/policies`);\r\n if (result.error) return result;\r\n // Map the API's pg_policies shape to the RlsPolicy fields createPolicy takes.\r\n const policies = (result.data.policies ?? []).map((p) => ({\r\n name: p.name,\r\n command: p.command,\r\n definition: p.using ?? '',\r\n ...(p.with_check ? { check: p.with_check } : {}),\r\n roles: String(p.role ?? '').replace(/[{}]/g, '').split(',').map((r) => r.trim()).filter(Boolean),\r\n }));\r\n return { data: policies, error: null };\r\n }\r\n\r\n /**\r\n * Delete an RLS policy\r\n */\r\n async deletePolicy(tableName: string, policyName: string): Promise<ApiResult<{ success: boolean }>> {\r\n return this.client.request<{ success: boolean }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/policies/${encodeURIComponent(policyName)}`,\r\n { method: 'DELETE' }\r\n );\r\n }\r\n\r\n /**\r\n * Create a fluent query builder for a table\r\n */\r\n from(tableName: string): TableQueryBuilder {\r\n return new TableQueryBuilder(this, tableName);\r\n }\r\n}\r\n\r\nconst SAFE_IDENT = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\r\n\r\ntype QueryFilter = { col: string; op: string; value: unknown; ftsType?: string; config?: string };\r\n\r\nexport interface SemanticOptions {\r\n /** Rows to return (default 10, max 100; a builder `.limit()` sets it too). */\r\n k?: number;\r\n /** A text column to also rank by keyword match. */\r\n hybridColumn?: string;\r\n /** Drop rows below this cosine similarity (0–1). */\r\n minSimilarity?: number;\r\n}\r\n\r\nexport type SemanticFilter = { column: string; op: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'in' | 'is'; value?: unknown };\r\n\r\n/**\r\n * Fluent, Supabase-style query builder. Filters/ordering/pagination compose into\r\n * a single parameterized SELECT executed via the SQL endpoint — so values are\r\n * always bound ($1, $2, …), never interpolated, and RLS still applies. Mirrors\r\n * the Flutter SDK's builder so the two SDKs feel the same.\r\n *\r\n * ```ts\r\n * const { data } = await client.db.from('posts')\r\n * .eq('status', 'published')\r\n * .gt('views', 100)\r\n * .order('created_at', { ascending: false })\r\n * .limit(10)\r\n * .select('id, title, views');\r\n * ```\r\n */\r\nclass TableQueryBuilder {\r\n private _filters: QueryFilter[] = [];\r\n private _orders: Array<{ col: string; asc: boolean }> = [];\r\n private _limit?: number;\r\n private _offset?: number;\r\n private _columns = '*';\r\n private _vectorOrder?: { col: string; vec: string; op: string };\r\n\r\n constructor(\r\n private db: DatabaseClient,\r\n private tableName: string,\r\n ) {\r\n this.ident(tableName);\r\n }\r\n\r\n private ident(name: string): string {\r\n if (!SAFE_IDENT.test(name)) {\r\n throw new Error(`NestCore: unsafe SQL identifier \"${name}\"`);\r\n }\r\n return `\"${name}\"`;\r\n }\r\n\r\n // ── Filter operators (match the backend's supported set) ──────────────────\r\n eq(column: string, value: unknown): this { return this.push(column, 'eq', value); }\r\n neq(column: string, value: unknown): this { return this.push(column, 'neq', value); }\r\n gt(column: string, value: unknown): this { return this.push(column, 'gt', value); }\r\n gte(column: string, value: unknown): this { return this.push(column, 'gte', value); }\r\n lt(column: string, value: unknown): this { return this.push(column, 'lt', value); }\r\n lte(column: string, value: unknown): this { return this.push(column, 'lte', value); }\r\n like(column: string, pattern: string): this { return this.push(column, 'like', pattern); }\r\n ilike(column: string, pattern: string): this { return this.push(column, 'ilike', pattern); }\r\n in(column: string, values: unknown[]): this { return this.push(column, 'in', values); }\r\n is(column: string, value: null | boolean): this { return this.push(column, 'is', value); }\r\n\r\n /**\r\n * Full-text search on a column using PostgreSQL text search.\r\n *\r\n * ```ts\r\n * await client.db.from('articles').textSearch('body', 'postgres & search').select();\r\n * ```\r\n *\r\n * @param type `websearch` (default, Google-style operators), `plain`, or `phrase`.\r\n * @param config text-search configuration / language (default `english`).\r\n */\r\n textSearch(\r\n column: string,\r\n query: string,\r\n opts?: { type?: 'websearch' | 'plain' | 'phrase'; config?: string },\r\n ): this {\r\n this.ident(column);\r\n const config = opts?.config ?? 'english';\r\n if (!SAFE_IDENT.test(config)) {\r\n throw new Error(`NestCore: unsafe text-search config \"${config}\"`);\r\n }\r\n this._filters.push({ col: column, op: 'fts', value: query, ftsType: opts?.type ?? 'websearch', config });\r\n return this;\r\n }\r\n\r\n /**\r\n * Order by vector similarity (pgvector). Returns the nearest rows to\r\n * `vector` by the chosen distance metric, closest first.\r\n *\r\n * ```ts\r\n * await client.db.from('documents').nearest('embedding', queryEmbedding, { metric: 'cosine', limit: 5 }).select();\r\n * ```\r\n *\r\n * Requires the `vector` extension (`CREATE EXTENSION IF NOT EXISTS vector`).\r\n * @param metric `l2` (default, `<->`), `cosine` (`<=>`), or `ip` (`<#>`).\r\n */\r\n nearest(column: string, vector: number[], opts?: { metric?: 'l2' | 'cosine' | 'ip'; limit?: number }): this {\r\n this.ident(column);\r\n if (!Array.isArray(vector) || !vector.every((n) => typeof n === 'number' && Number.isFinite(n))) {\r\n throw new Error('NestCore: nearest() vector must be an array of finite numbers');\r\n }\r\n const op = opts?.metric === 'cosine' ? '<=>' : opts?.metric === 'ip' ? '<#>' : '<->';\r\n this._vectorOrder = { col: column, vec: `[${vector.join(',')}]`, op };\r\n if (opts?.limit != null) this._limit = opts.limit;\r\n return this;\r\n }\r\n\r\n private push(column: string, op: string, value: unknown): this {\r\n this.ident(column);\r\n this._filters.push({ col: column, op, value });\r\n return this;\r\n }\r\n\r\n // ── Ordering / pagination ─────────────────────────────────────────────────\r\n order(column: string, opts?: { ascending?: boolean }): this {\r\n this.ident(column);\r\n this._orders.push({ col: column, asc: opts?.ascending !== false });\r\n return this;\r\n }\r\n /** @deprecated use {@link order} */\r\n orderBy(column: string, dir: 'ASC' | 'DESC' = 'ASC'): this {\r\n return this.order(column, { ascending: dir === 'ASC' });\r\n }\r\n limit(n: number): this { this._limit = n; return this; }\r\n /** Inclusive range, like Supabase: `.range(0, 9)` → first 10 rows. */\r\n range(from: number, to: number): this {\r\n this._offset = from;\r\n this._limit = to - from + 1;\r\n return this;\r\n }\r\n page(n: number, size = 20): this {\r\n this._limit = size;\r\n this._offset = Math.max(0, (n - 1) * size);\r\n return this;\r\n }\r\n\r\n private build(): { sql: string; params: unknown[] } {\r\n const params: unknown[] = [];\r\n const cols =\r\n this._columns === '*'\r\n ? '*'\r\n : this._columns.split(',').map((c) => this.ident(c.trim())).join(', ');\r\n let sql = `SELECT ${cols} FROM ${this.ident(this.tableName)}`;\r\n\r\n const where: string[] = [];\r\n for (const f of this._filters) {\r\n const col = this.ident(f.col);\r\n switch (f.op) {\r\n case 'eq': where.push(`${col} = $${params.push(f.value)}`); break;\r\n case 'neq': where.push(`${col} != $${params.push(f.value)}`); break;\r\n case 'gt': where.push(`${col} > $${params.push(f.value)}`); break;\r\n case 'gte': where.push(`${col} >= $${params.push(f.value)}`); break;\r\n case 'lt': where.push(`${col} < $${params.push(f.value)}`); break;\r\n case 'lte': where.push(`${col} <= $${params.push(f.value)}`); break;\r\n case 'like': where.push(`${col} LIKE $${params.push(f.value)}`); break;\r\n case 'ilike': where.push(`${col} ILIKE $${params.push(f.value)}`); break;\r\n case 'in': {\r\n const vals = (f.value as unknown[]) ?? [];\r\n if (vals.length === 0) { where.push('FALSE'); break; }\r\n where.push(`${col} IN (${vals.map((v) => `$${params.push(v)}`).join(', ')})`);\r\n break;\r\n }\r\n case 'is':\r\n if (f.value === null) where.push(`${col} IS NULL`);\r\n else where.push(`${col} = $${params.push(f.value)}`);\r\n break;\r\n case 'fts': {\r\n // config is validated as a safe identifier in textSearch(); the query\r\n // value is always bound as a parameter.\r\n const cfg = f.config ?? 'english';\r\n const fn =\r\n f.ftsType === 'plain' ? 'plainto_tsquery'\r\n : f.ftsType === 'phrase' ? 'phraseto_tsquery'\r\n : 'websearch_to_tsquery';\r\n where.push(`to_tsvector('${cfg}', ${col}::text) @@ ${fn}('${cfg}', $${params.push(f.value)})`);\r\n break;\r\n }\r\n }\r\n }\r\n if (where.length) sql += ` WHERE ${where.join(' AND ')}`;\r\n const orderClauses: string[] = [];\r\n if (this._vectorOrder) {\r\n // vector bound as a param, cast to ::vector — values are never interpolated.\r\n orderClauses.push(`${this.ident(this._vectorOrder.col)} ${this._vectorOrder.op} $${params.push(this._vectorOrder.vec)}::vector`);\r\n }\r\n for (const o of this._orders) orderClauses.push(`${this.ident(o.col)} ${o.asc ? 'ASC' : 'DESC'}`);\r\n if (orderClauses.length) sql += ` ORDER BY ${orderClauses.join(', ')}`;\r\n if (this._limit != null) sql += ` LIMIT $${params.push(this._limit)}`;\r\n if (this._offset != null) sql += ` OFFSET $${params.push(this._offset)}`;\r\n return { sql, params };\r\n }\r\n\r\n /** Run the built SELECT. Returns `{ rows, total }` (total = rows returned). */\r\n async select<T = Record<string, unknown>>(columns = '*'): Promise<ApiResult<{ rows: T[]; total: number }>> {\r\n this._columns = columns;\r\n const { sql, params } = this.build();\r\n const res = await this.db.query<T>(sql, params);\r\n if (res.error) return res as ApiResult<{ rows: T[]; total: number }>;\r\n return { data: { rows: res.data.rows, total: res.data.rows.length }, error: null };\r\n }\r\n\r\n /** Expect exactly one row; errors if zero rows. */\r\n async single<T = Record<string, unknown>>(columns = '*'): Promise<ApiResult<T>> {\r\n this._limit = 1;\r\n const res = await this.select<T>(columns);\r\n if (res.error) return res as ApiResult<T>;\r\n const row = res.data.rows[0];\r\n if (!row) return { data: null, error: { message: 'No rows found', code: 'NOT_FOUND' } };\r\n return { data: row, error: null };\r\n }\r\n\r\n /** Return the first row or null (no error when empty). */\r\n async maybeSingle<T = Record<string, unknown>>(columns = '*'): Promise<ApiResult<T | null>> {\r\n this._limit = 1;\r\n const res = await this.select<T>(columns);\r\n if (res.error) return res as ApiResult<T | null>;\r\n return { data: res.data.rows[0] ?? null, error: null };\r\n }\r\n\r\n /**\r\n * Semantic search over an embedded column, keeping this builder's filters\r\n * (`eq`, `gt`, `in`, …; not `textSearch` or `nearest`).\r\n *\r\n * ```ts\r\n * const { data } = await client.db.from('articles').eq('published', true)\r\n * .semantic('body_embedding', 'how do I reset my password?', { k: 5 });\r\n * ```\r\n */\r\n async semantic<T = Record<string, unknown>>(column: string, query: string, opts: SemanticOptions = {}): Promise<ApiResult<{ rows: Array<T & { _similarity: number | null; _score: number }>; model: string | null }>> {\r\n this.ident(column);\r\n if (this._vectorOrder || this._filters.some((f) => f.op === 'fts')) {\r\n return { data: null, error: { message: 'semantic() cannot be combined with textSearch() or nearest(); use hybridColumn for keyword ranking', code: 'INVALID_QUERY' } };\r\n }\r\n const filters = this._filters.map((f) => ({ column: f.col, op: f.op as SemanticFilter['op'], value: f.value }));\r\n return this.db.semantic<T>(this.tableName, column, query, { k: this._limit, ...opts, ...(filters.length ? { filters } : {}) });\r\n }\r\n\r\n // ── Writes (delegate to the row endpoints) ────────────────────────────────\r\n async insert<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<ApiResult<T>> {\r\n return this.db.insert<T>(this.tableName, data);\r\n }\r\n async update<T = Record<string, unknown>>(id: string | number, data: Record<string, unknown>): Promise<ApiResult<T>> {\r\n return this.db.update<T>(this.tableName, id, data);\r\n }\r\n async delete(id: string | number): Promise<ApiResult<{ success: boolean }>> {\r\n return this.db.delete(this.tableName, id);\r\n }\r\n}\r\n","import { errorMessage, type HttpClient } from './client';\nimport type { FunctionInvokeOptions, FunctionResponse, ApiResult } from '../types';\n\nexport class FunctionsClient {\n constructor(private client: HttpClient) {}\n\n /**\n * Invoke an edge function. Runs as the signed-in user when there is a\n * session (fills `ctx.user`), otherwise with the project key.\n *\n * Returns the function's real `status` and `headers`. The body is parsed by\n * content type: JSON → object, `text/*` → string, anything else → Blob.\n * A non-2xx response is an error whose `details` holds the parsed body.\n */\n async invoke<T = unknown>(\n functionName: string,\n options: FunctionInvokeOptions = {}\n ): Promise<ApiResult<FunctionResponse<T>>> {\n const url = `${this.client.getBaseUrl()}/api/projects/${this.client.getProjectSlug()}/functions/v1/${encodeURIComponent(functionName)}`;\n const method = options.method || 'POST';\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.client.getTimeout());\n\n try {\n const response = await this.client.authFetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: options.body !== undefined && method !== 'GET' ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n // Never forward credentials to a redirect target.\n redirect: 'error',\n });\n clearTimeout(timeoutId);\n\n const contentType = response.headers.get('content-type') ?? '';\n const data: unknown = contentType.includes('json')\n ? await response.json().catch(() => null)\n : contentType.startsWith('text/')\n ? await response.text()\n : await response.blob();\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => { headers[key] = value; });\n\n if (!response.ok) {\n return {\n data: null,\n error: {\n message: errorMessage(data, response.status),\n code: (data as { code?: string } | null)?.code,\n status: response.status,\n details: data,\n },\n };\n }\n return { data: { data: data as T, status: response.status, headers }, error: null };\n } catch (err) {\n clearTimeout(timeoutId);\n if (err instanceof Error && err.name === 'AbortError') {\n return { data: null, error: { message: 'Request timeout', code: 'TIMEOUT' } };\n }\n return {\n data: null,\n error: { message: err instanceof Error ? err.message : 'Unknown error', code: 'NETWORK_ERROR' },\n };\n }\n }\n}\n","import { adminOnlyError, type HttpClient } from './client';\nimport type {\n BackgroundJob,\n BackgroundJobRun,\n CreateJobOptions,\n UpdateJobOptions,\n ApiResult,\n} from '../types';\n\n/**\n * @deprecated Not usable with a project API key: these routes need a NestCore console\n * admin token. Every method resolves to an `ADMIN_ONLY` error without calling\n * the API. Use the NestCore console, the CLI or the MCP server instead.\n */\nexport class JobsClient {\n constructor(private client: HttpClient) {}\n\n /**\n * Create a background job with a cron schedule.\n */\n async create(options: CreateJobOptions): Promise<ApiResult<BackgroundJob>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * List all background jobs for the current project.\n */\n async list(): Promise<ApiResult<BackgroundJob[]>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * Get a single background job by name.\n */\n async get(name: string): Promise<ApiResult<BackgroundJob>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * Update a background job's code, schedule, or configuration.\n */\n async update(\n name: string,\n options: UpdateJobOptions,\n ): Promise<ApiResult<BackgroundJob>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * Delete a background job.\n */\n async delete(name: string): Promise<ApiResult<{ success: boolean }>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * Manually trigger a background job to run immediately.\n */\n async trigger(name: string): Promise<ApiResult<BackgroundJobRun>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * Get execution history for a background job.\n */\n async getRuns(\n name: string,\n options?: { limit?: number },\n ): Promise<ApiResult<BackgroundJobRun[]>> {\n return adminOnlyError('client.jobs');\n }\n}\n","/**\n * Pluggable key/value storage for persisting the auth session across page\n * loads. A browser app typically wants `localStorageAdapter` so a signed-in\n * user stays signed in after a refresh. Without an adapter the session lives\n * only in that client instance — the right choice for SSR (one client per\n * request) or when you manage tokens yourself.\n */\nexport interface StorageAdapter {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/** In-memory storage — sessions live only for the lifetime of the tab/process. */\nexport function createMemoryAdapter(): StorageAdapter {\n const store = new Map<string, string>();\n return {\n getItem: (k) => (store.has(k) ? store.get(k)! : null),\n setItem: (k, v) => void store.set(k, v),\n removeItem: (k) => void store.delete(k),\n };\n}\n\n/**\n * Browser `localStorage`-backed storage. Falls back to in-memory automatically\n * when `localStorage` is unavailable or throws (SSR, private-mode, quota), so\n * it's always safe to pass.\n */\nexport function createLocalStorageAdapter(): StorageAdapter {\n let ls: Storage | null = null;\n try {\n ls = typeof globalThis !== 'undefined' ? (globalThis as { localStorage?: Storage }).localStorage ?? null : null;\n // Probe — Safari private mode exposes localStorage but throws on write.\n if (ls) {\n const probe = '__nestcore_probe__';\n ls.setItem(probe, '1');\n ls.removeItem(probe);\n }\n } catch {\n ls = null;\n }\n if (!ls) return createMemoryAdapter();\n\n const storage = ls;\n return {\n getItem: (k) => {\n try {\n return storage.getItem(k);\n } catch {\n return null;\n }\n },\n setItem: (k, v) => {\n try {\n storage.setItem(k, v);\n } catch {\n /* ignore quota / private-mode errors */\n }\n },\n removeItem: (k) => {\n try {\n storage.removeItem(k);\n } catch {\n /* ignore */\n }\n },\n };\n}\n\n/**\n * Non-persisting adapter: the session lives only in the client instance that\n * holds it. Shared module-level state here would leak one user's session into\n * every other client in an SSR process, so this stores nothing.\n */\nexport const memoryAdapter: StorageAdapter = {\n getItem: () => null,\n setItem: () => undefined,\n removeItem: () => undefined,\n};\n\n/**\n * Browser `localStorage`, resolved on each call. Where `localStorage` is\n * unavailable (SSR, private mode) it persists nothing, so on a server every\n * client keeps its session to itself instead of sharing one in-memory store.\n */\nexport const localStorageAdapter: StorageAdapter = {\n getItem: (k) => {\n try { return usableLocalStorage()?.getItem(k) ?? null; } catch { return null; }\n },\n setItem: (k, v) => {\n try { usableLocalStorage()?.setItem(k, v); } catch { /* quota / private mode */ }\n },\n removeItem: (k) => {\n try { usableLocalStorage()?.removeItem(k); } catch { /* ignore */ }\n },\n};\n\nfunction usableLocalStorage(): Storage | null {\n try {\n return (globalThis as { localStorage?: Storage }).localStorage ?? null;\n } catch {\n return null;\n }\n}\n","import type { HttpClient } from './client';\nimport type {\n AuthSession,\n AuthUser,\n SignUpCredentials,\n SignInCredentials,\n PasswordRecoveryOptions,\n PasswordResetOptions,\n UpdateUserOptions,\n ApiResult,\n PasskeyDevice,\n PasskeyRegistrationOptions,\n PasskeyAuthenticationOptions,\n PasskeyRegisterVerifyOptions,\n PasskeyAuthenticateVerifyOptions,\n PasskeySignUpOptions,\n PasskeySignUpVerifyOptions,\n} from '../types';\nimport type { StorageAdapter } from './storage-adapters';\nimport { memoryAdapter } from './storage-adapters';\n\n/** Returned by `signIn` when the account has MFA enrolled — complete with `verifyMfa`. */\nexport interface MfaChallenge {\n mfa_required: true;\n challenge_token: string;\n factors: Array<{ id: string; type: string }>;\n}\n\n/** An enrolled MFA factor. */\nexport interface MfaFactor {\n id: string;\n factor_type: string;\n friendly_name?: string | null;\n verified: boolean;\n created_at: string;\n last_used_at?: string | null;\n}\n\n/** Type guard: did `signIn` return an MFA challenge instead of a session? */\nexport function isMfaChallenge(value: unknown): value is MfaChallenge {\n return !!value && typeof value === 'object' && (value as MfaChallenge).mfa_required === true;\n}\n\nconst SESSION_KEY = 'nestcore.session';\n/** Pre-rename key prefix; a session stored under it is migrated on first read. */\nconst LEGACY_SESSION_KEY = 'orbitnest.session';\n/** Refresh the access token this many seconds before it expires. */\nconst REFRESH_MARGIN_S = 30;\nconst NO_SESSION = { data: null, error: { message: 'No active session', code: 'NO_SESSION' } } as const;\n\nexport class AuthClient {\n private _session: AuthSession | null = null;\n private readonly storage: StorageAdapter;\n private readonly storageKey: string;\n private refreshing: Promise<ApiResult<AuthSession>> | null = null;\n private readonly listeners = new Set<(session: AuthSession | null) => void>();\n\n constructor(private client: HttpClient, options: { storage?: StorageAdapter } = {}) {\n // memoryAdapter persists nothing: without an adapter the session stays in\n // this instance only (never shared between SSR requests).\n this.storage = options.storage ?? memoryAdapter;\n // Namespace the persisted session by project so multiple clients on the\n // same origin don't clobber each other.\n this.storageKey = `${SESSION_KEY}.${this.client.getProjectSlug()}`;\n // Rehydrate a previously persisted session (browser refresh).\n let raw = this.storage.getItem(this.storageKey);\n if (raw === null) {\n // Keep users signed in across the OrbitNest → NestCore rename.\n const legacyKey = `${LEGACY_SESSION_KEY}.${this.client.getProjectSlug()}`;\n try {\n raw = this.storage.getItem(legacyKey);\n if (raw !== null) {\n this.storage.setItem(this.storageKey, raw);\n this.storage.removeItem(legacyKey);\n }\n } catch {\n /* storage is best-effort */\n }\n }\n if (raw) {\n try {\n this._session = JSON.parse(raw) as AuthSession;\n } catch {\n this.storage.removeItem(this.storageKey);\n }\n }\n }\n\n // Reading/writing `this.session` transparently persists to the configured\n // storage adapter, so every existing assignment below stays sync-on-write.\n private get session(): AuthSession | null {\n return this._session;\n }\n private set session(value: AuthSession | null) {\n this._session = withExpiry(value);\n try {\n if (value) this.storage.setItem(this.storageKey, JSON.stringify(this._session));\n else this.storage.removeItem(this.storageKey);\n } catch {\n /* storage is best-effort */\n }\n for (const cb of this.listeners) {\n try { cb(this._session); } catch { /* listener errors must not break auth */ }\n }\n }\n\n /**\n * Listen for session changes (sign-in, token refresh, sign-out). Returns an\n * unsubscribe function.\n */\n onAuthStateChange(callback: (session: AuthSession | null) => void): () => void {\n this.listeners.add(callback);\n return () => { this.listeners.delete(callback); };\n }\n\n /**\n * The current access token, refreshed first when it is about to expire.\n * Returns null when signed out or when the server rejected the refresh. A\n * transient refresh failure keeps the old token, so the request fails with\n * 401 instead of silently running as the anonymous role.\n */\n async getAccessToken(): Promise<string | null> {\n const s = this.session;\n if (!s?.access_token) return null;\n if (s.expires_at && s.expires_at - REFRESH_MARGIN_S <= Date.now() / 1000) {\n const refreshed = await this.refreshSession();\n return refreshed.data?.access_token ?? this.session?.access_token ?? null;\n }\n return s.access_token;\n }\n\n /**\n * Called after the API rejected `staleToken` (401): refresh once and return\n * the new access token, or null when the session can't be renewed. Calls\n * that raced an earlier refresh just get the current token.\n */\n async refreshAccessToken(staleToken: string): Promise<string | null> {\n const current = this.session?.access_token;\n if (!current) return null;\n if (current !== staleToken) return current;\n return (await this.refreshSession()).data?.access_token ?? null;\n }\n\n private async withToken<T>(fn: (token: string) => Promise<ApiResult<T>>): Promise<ApiResult<T>> {\n const token = await this.getAccessToken();\n return token ? fn(token) : NO_SESSION;\n }\n\n private get basePath(): string {\n return `/api/projects/${this.client.getProjectSlug()}/auth`;\n }\n\n /**\n * Get the current session\n */\n getSession(): AuthSession | null {\n return this.session;\n }\n\n /**\n * Get the current user\n */\n getUser(): AuthUser | null {\n return this.session?.user || null;\n }\n\n /**\n * Sign up with email and password - Step 1: Request OTP\n * This sends an OTP to the user's email for verification\n */\n async signUp(credentials: SignUpCredentials): Promise<ApiResult<{ success: boolean; message: string; email: string; expires_in: number }>> {\n const result = await this.client.requestPublic<{ success: boolean; message: string; email: string; expires_in: number }>(`${this.basePath}/signup`, {\n method: 'POST',\n body: {\n email: credentials.email,\n password: credentials.password,\n user_metadata: credentials.metadata,\n },\n });\n\n // Note: No session is returned in step 1, only OTP confirmation\n return result;\n }\n\n /**\n * Verify signup with OTP - Step 2: Complete registration\n * This verifies the OTP and creates the user account\n */\n async verifySignUp(options: { email: string; code: string }): Promise<ApiResult<AuthSession>> {\n const result = await this.client.requestPublic<AuthSession>(`${this.basePath}/verify-signup`, {\n method: 'POST',\n body: {\n email: options.email,\n code: options.code,\n },\n });\n\n if (result.data) {\n this.session = result.data;\n }\n\n return result;\n }\n\n /**\n * Sign in with email and password.\n *\n * If the account has a verified MFA factor, the response data is an\n * {@link MfaChallenge} (not a session) — call {@link verifyMfa} with the\n * `challenge_token` and a TOTP code to finish. Use {@link isMfaChallenge} to\n * branch.\n */\n async signIn(credentials: SignInCredentials): Promise<ApiResult<AuthSession | MfaChallenge>> {\n const result = await this.client.requestPublic<AuthSession | MfaChallenge>(`${this.basePath}/signin`, {\n method: 'POST',\n body: {\n email: credentials.email,\n password: credentials.password,\n },\n });\n\n // Only persist a real session — an MFA challenge isn't one yet.\n if (result.data && !isMfaChallenge(result.data)) {\n this.session = result.data as AuthSession;\n }\n\n return result;\n }\n\n /** Complete an MFA-gated sign-in with the challenge token and a TOTP code. */\n async verifyMfa(challengeToken: string, code: string): Promise<ApiResult<AuthSession>> {\n const result = await this.client.requestPublic<AuthSession>(`${this.basePath}/verify-mfa`, {\n method: 'POST',\n body: { challenge_token: challengeToken, code },\n });\n if (result.data) {\n this.session = result.data;\n }\n return result;\n }\n\n // ─── SMS OTP (phone sign-in; the project must have Twilio configured) ──\n\n /**\n * Send a one-time code to a phone number (E.164, e.g. `+15555550123`).\n * Rate limited per phone: a 429 error has code `SMS_RATE_LIMITED`.\n */\n async signInWithSms(phone: string): Promise<ApiResult<{ success: boolean; message: string; expires_in: number }>> {\n return this.client.requestPublic(`${this.basePath}/sms/send-otp`, { method: 'POST', body: { phone } });\n }\n\n /**\n * Verify a phone OTP and sign in (creates the user on first sign-in).\n * After 5 wrong codes a 429 error has code `OTP_RATE_LIMITED`.\n */\n async verifySmsOtp(phone: string, code: string): Promise<ApiResult<AuthSession>> {\n const result = await this.client.requestPublic<AuthSession>(`${this.basePath}/sms/verify-otp`, {\n method: 'POST',\n body: { phone, code },\n });\n if (result.data) {\n this.session = result.data;\n }\n return result;\n }\n\n /**\n * Sign out the current user\n */\n async signOut(): Promise<ApiResult<{ success: boolean }>> {\n const token = await this.getAccessToken();\n const result: ApiResult<{ success: boolean }> = token\n ? await this.client.requestWithToken<{ success: boolean }>(`${this.basePath}/signout`, token, { method: 'POST' })\n : { data: { success: true }, error: null };\n\n this.session = null;\n return result;\n }\n\n /**\n * Refresh the current session\n */\n async refreshSession(): Promise<ApiResult<AuthSession>> {\n const refreshToken = this.session?.refresh_token;\n if (!refreshToken) {\n return {\n data: null,\n error: { message: 'No refresh token available', code: 'NO_SESSION' },\n };\n }\n\n // Refresh tokens rotate: concurrent callers share one request instead of\n // replaying the same (then revoked) token.\n this.refreshing ??= this.client\n .requestPublic<AuthSession>(`${this.basePath}/refresh`, {\n method: 'POST',\n body: { refresh_token: refreshToken },\n })\n .then((result) => {\n if (result.data) this.session = result.data;\n // Rejected refresh token: the session is dead, so stop reporting it.\n else if (result.error.status === 401) this.session = null;\n return result;\n })\n .finally(() => { this.refreshing = null; });\n return this.refreshing;\n }\n\n /**\n * Send password recovery email\n */\n async resetPasswordForEmail(options: PasswordRecoveryOptions): Promise<ApiResult<{ success: boolean }>> {\n return this.client.requestPublic<{ success: boolean }>(`${this.basePath}/recover-password`, {\n method: 'POST',\n body: { email: options.email },\n });\n }\n\n /**\n * Reset password with the OTP from the recovery email.\n *\n * The API body is `{ email, code, new_password }` — we keep the SDK\n * surface (`token`, `password`) readable and map to the server names.\n */\n async updatePassword(options: PasswordResetOptions): Promise<ApiResult<{ success: boolean }>> {\n return this.client.requestPublic<{ success: boolean }>(`${this.basePath}/reset-password`, {\n method: 'POST',\n body: {\n email: options.email,\n code: options.token,\n new_password: options.password,\n },\n });\n }\n\n /**\n * Get current user profile\n */\n async getProfile(): Promise<ApiResult<AuthUser>> {\n return this.withToken((token) => this.client.requestWithToken<AuthUser>(`${this.basePath}/user`, token));\n }\n\n /**\n * Update the signed-in user. `metadata` is merged into `user_metadata`.\n * Changing `email` or `password` requires `current_password` (accounts\n * without a password need a sign-in under 10 minutes old instead); otherwise\n * the API answers 401 with code `REAUTH_REQUIRED`. A new `email` is not\n * applied yet: a code is sent to it and the returned user carries\n * `new_email` until {@link verifyEmailChange} succeeds.\n */\n async updateUser(options: UpdateUserOptions): Promise<ApiResult<AuthUser>> {\n const { metadata, ...rest } = options;\n const result = await this.withToken((token) =>\n this.client.requestWithToken<{ user: AuthUser }>(`${this.basePath}/user`, token, {\n method: 'PUT',\n body: metadata === undefined ? rest : { ...rest, data: metadata },\n }),\n );\n if (result.error) return result;\n\n const user = result.data.user;\n if (this.session && user) this.session = { ...this.session, user };\n return { data: user, error: null };\n }\n\n /**\n * Confirm a pending email change with the code sent to the new address.\n * The access token keeps the old `email` claim until the next refresh.\n */\n async verifyEmailChange(code: string): Promise<ApiResult<AuthUser>> {\n const result = await this.withToken((token) =>\n this.client.requestWithToken<{ user: AuthUser }>(`${this.basePath}/user/email/verify`, token, {\n method: 'POST',\n body: { code },\n }),\n );\n if (result.error) return result;\n const user = result.data.user;\n if (this.session && user) this.session = { ...this.session, user };\n return { data: user, error: null };\n }\n\n /**\n * Delete current user account\n */\n async deleteUser(): Promise<ApiResult<{ success: boolean }>> {\n const result = await this.withToken((token) =>\n this.client.requestWithToken<{ success: boolean }>(`${this.basePath}/user`, token, { method: 'DELETE' }),\n );\n\n if (result.data) {\n this.session = null;\n }\n\n return result;\n }\n\n /**\n * Set session manually (e.g., from stored tokens)\n */\n setSession(session: AuthSession): void {\n this.session = session;\n }\n\n // ─── Passkey / WebAuthn ───────────────────────────────────────────────\n // Runtime-agnostic — consumers drive `navigator.credentials.*` themselves\n // (e.g. via @simplewebauthn/browser) and pass the result to the verify\n // methods below.\n\n async registerPasskeyOptions(\n opts: { device_name?: string } = {},\n ): Promise<ApiResult<PasskeyRegistrationOptions>> {\n return this.withToken((token) => this.client.requestWithToken<PasskeyRegistrationOptions>(\n `${this.basePath}/passkey/register/options`,\n token,\n { method: 'POST', body: { device_name: opts.device_name } },\n ));\n }\n\n async registerPasskeyVerify(\n input: PasskeyRegisterVerifyOptions,\n ): Promise<ApiResult<PasskeyDevice>> {\n return this.withToken((token) => this.client.requestWithToken<PasskeyDevice>(\n `${this.basePath}/passkey/register/verify`,\n token,\n { method: 'POST', body: input },\n ));\n }\n\n /**\n * Begin a passkey sign-up ceremony. Creates a passwordless user record\n * server-side and returns attestation options. If an account already\n * exists with the supplied email the server responds with 409 — route\n * the user to sign-in (then `registerPasskeyOptions` to add a passkey).\n */\n async signUpWithPasskeyOptions(\n opts: PasskeySignUpOptions,\n ): Promise<ApiResult<PasskeyRegistrationOptions>> {\n return this.client.requestPublic<PasskeyRegistrationOptions>(\n `${this.basePath}/passkey/signup/options`,\n { method: 'POST', body: opts },\n );\n }\n\n /**\n * Complete a passkey sign-up ceremony. On success the SDK stores the\n * freshly issued session so subsequent calls act as the new user.\n */\n async signUpWithPasskeyVerify(\n input: PasskeySignUpVerifyOptions,\n ): Promise<ApiResult<AuthSession>> {\n const result = await this.client.requestPublic<AuthSession>(\n `${this.basePath}/passkey/signup/verify`,\n { method: 'POST', body: input },\n );\n if (result.data) {\n this.session = result.data;\n }\n return result;\n }\n\n async signInWithPasskeyOptions(\n opts: { identifier?: string } = {},\n ): Promise<ApiResult<PasskeyAuthenticationOptions>> {\n return this.client.requestPublic<PasskeyAuthenticationOptions>(\n `${this.basePath}/passkey/login/options`,\n { method: 'POST', body: opts.identifier ? { identifier: opts.identifier } : {} },\n );\n }\n\n async signInWithPasskeyVerify(\n input: PasskeyAuthenticateVerifyOptions,\n ): Promise<ApiResult<AuthSession>> {\n const result = await this.client.requestPublic<AuthSession>(\n `${this.basePath}/passkey/login/verify`,\n { method: 'POST', body: input },\n );\n if (result.data) {\n this.session = result.data;\n }\n return result;\n }\n\n async listPasskeys(): Promise<ApiResult<PasskeyDevice[]>> {\n return this.withToken((token) => this.client.requestWithToken<PasskeyDevice[]>(\n `${this.basePath}/passkey/devices`,\n token,\n ));\n }\n\n async renamePasskey(id: string, deviceName: string): Promise<ApiResult<PasskeyDevice>> {\n return this.withToken((token) => this.client.requestWithToken<PasskeyDevice>(\n `${this.basePath}/passkey/devices/${encodeURIComponent(id)}`,\n token,\n { method: 'PATCH', body: { device_name: deviceName } },\n ));\n }\n\n async revokePasskey(id: string): Promise<ApiResult<{ success: boolean }>> {\n return this.withToken((token) => this.client.requestWithToken<{ success: boolean }>(\n `${this.basePath}/passkey/devices/${encodeURIComponent(id)}`,\n token,\n { method: 'DELETE' },\n ));\n }\n\n // ─── MFA (TOTP / authenticator apps) ──────────────────────────────────\n\n /** Begin TOTP enrollment. `code` (TOTP or recovery code) is required when a verified factor already exists. */\n async enrollMfaTotp(\n friendlyName?: string,\n code?: string,\n ): Promise<ApiResult<{ factor_id: string; secret: string; otpauth_url: string; qr_code: string }>> {\n return this.withToken((token) => this.client.requestWithToken<{ factor_id: string; secret: string; otpauth_url: string; qr_code: string }>(\n `${this.basePath}/mfa/totp/enroll`,\n token,\n { method: 'POST', body: { friendly_name: friendlyName, code } },\n ));\n }\n\n /** Confirm enrollment with the first code. On success returns the user's\n * single-use `recovery_codes` (shown once — store them safely). */\n async verifyMfaEnrollment(\n factorId: string,\n code: string,\n ): Promise<ApiResult<{ success: boolean; recovery_codes: string[] }>> {\n return this.withToken((token) => this.client.requestWithToken<{ success: boolean; recovery_codes: string[] }>(\n `${this.basePath}/mfa/totp/verify`,\n token,\n { method: 'POST', body: { factor_id: factorId, code } },\n ));\n }\n\n /** Regenerate the user's MFA recovery codes — returns the new set once.\n * `code` (TOTP or recovery code) is required while a verified factor exists. */\n async regenerateMfaRecoveryCodes(code?: string): Promise<ApiResult<{ codes: string[] }>> {\n return this.withToken((token) => this.client.requestWithToken<{ codes: string[] }>(\n `${this.basePath}/mfa/recovery-codes`,\n token,\n { method: 'POST', body: { code } },\n ));\n }\n\n async listMfaFactors(): Promise<ApiResult<MfaFactor[]>> {\n return this.withToken((token) => this.client.requestWithToken<MfaFactor[]>(\n `${this.basePath}/mfa/factors`,\n token,\n ));\n }\n\n /** Remove an MFA factor. `code` (TOTP or recovery code) is required while a verified factor exists. */\n async unenrollMfa(factorId: string, code?: string): Promise<ApiResult<{ success: boolean }>> {\n return this.withToken((token) => this.client.requestWithToken<{ success: boolean }>(\n `${this.basePath}/mfa/factors/${encodeURIComponent(factorId)}`,\n token,\n { method: 'DELETE', body: { code } },\n ));\n }\n}\n\n/** Stamp `expires_at` (unix seconds) from `expires_in` so expiry survives a reload. */\nfunction withExpiry(session: AuthSession | null): AuthSession | null {\n if (!session || session.expires_at || !session.expires_in) return session;\n return { ...session, expires_at: Math.floor(Date.now() / 1000) + session.expires_in };\n}\n","import { adminOnlyError, type HttpClient } from './client';\nimport type { ApiResult } from '../types';\n\nexport interface LogEntry {\n id: string;\n timestamp: string;\n level: 'debug' | 'info' | 'warn' | 'error';\n message: string;\n metadata?: Record<string, unknown>;\n source?: string;\n}\n\nexport interface LogQueryOptions {\n level?: 'debug' | 'info' | 'warn' | 'error';\n since?: Date;\n until?: Date;\n limit?: number;\n offset?: number;\n source?: string;\n}\n\n/**\n * @deprecated Not usable with a project API key: these routes need a NestCore console\n * admin token. Every method resolves to an `ADMIN_ONLY` error without calling\n * the API. Use the NestCore console, the CLI or the MCP server instead.\n */\nexport class LoggingClient {\n constructor(private client: HttpClient) {}\n\n /**\n * Get all logs\n */\n async getLogs(options: LogQueryOptions = {}): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n\n /**\n * Get database logs\n */\n async getDatabaseLogs(options: LogQueryOptions = {}): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n\n /**\n * Get slow query logs\n */\n async getSlowQueryLogs(options: LogQueryOptions = {}): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n\n /**\n * Get auth logs\n */\n async getAuthLogs(options: LogQueryOptions = {}): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n\n /**\n * Get auth failure logs\n */\n async getAuthFailures(options: LogQueryOptions = {}): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n\n /**\n * Get edge function logs\n */\n async getEdgeFunctionLogs(\n functionName?: string,\n options: LogQueryOptions = {}\n ): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n}\n","import { adminOnlyError, type HttpClient } from './client';\nimport type { ApiResult } from '../types';\n\nexport interface EnvironmentVariable {\n name: string;\n value: string;\n description?: string;\n isSecret?: boolean;\n createdAt?: string;\n updatedAt?: string;\n}\n\n/**\n * @deprecated Not usable with a project API key: these routes need a NestCore console\n * admin token. Every method resolves to an `ADMIN_ONLY` error without calling\n * the API. Use the NestCore console, the CLI or the MCP server instead.\n */\nexport class EnvironmentClient {\n constructor(private client: HttpClient) {}\n\n /**\n * List all environment variables\n */\n async list(): Promise<ApiResult<EnvironmentVariable[]>> {\n return adminOnlyError('client.env');\n }\n\n /**\n * Get a specific environment variable\n */\n async get(name: string): Promise<ApiResult<EnvironmentVariable>> {\n return adminOnlyError('client.env');\n }\n\n /**\n * Create or update an environment variable\n */\n async set(\n name: string,\n value: string,\n options: { description?: string; isSecret?: boolean } = {}\n ): Promise<ApiResult<EnvironmentVariable>> {\n return adminOnlyError('client.env');\n }\n\n /**\n * Create a new environment variable\n */\n async create(\n name: string,\n value: string,\n options: { description?: string; isSecret?: boolean } = {}\n ): Promise<ApiResult<EnvironmentVariable>> {\n return adminOnlyError('client.env');\n }\n\n /**\n * Bulk create environment variables\n */\n async bulkCreate(\n variables: Array<{ name: string; value: string; description?: string; isSecret?: boolean }>\n ): Promise<ApiResult<EnvironmentVariable[]>> {\n return adminOnlyError('client.env');\n }\n\n /**\n * Delete an environment variable\n */\n async delete(name: string): Promise<ApiResult<{ success: boolean }>> {\n return adminOnlyError('client.env');\n }\n}\n","import type { ApiResult } from '../types';\n\n/**\n * Validate a required string parameter. Returns an error result if invalid.\n */\nexport function validateRequired(value: unknown, paramName: string): ApiResult<never> | null {\n if (!value || typeof value !== 'string' || value.trim().length === 0) {\n return {\n data: null,\n error: { message: `${paramName} is required and must be a non-empty string`, code: 'VALIDATION_ERROR' },\n };\n }\n return null;\n}\n\n/**\n * Validate a file/storage path. Returns an error result if invalid.\n * Rejects path traversal attempts (../) and null bytes.\n */\nexport function validatePath(path: string, paramName: string = 'path'): ApiResult<never> | null {\n const required = validateRequired(path, paramName);\n if (required) return required;\n\n // Block null bytes\n if (path.includes('\\0')) {\n return {\n data: null,\n error: { message: `${paramName} must not contain null bytes`, code: 'VALIDATION_ERROR' },\n };\n }\n\n // Block path traversal\n const segments = path.split('/');\n for (const seg of segments) {\n if (seg === '..') {\n return {\n data: null,\n error: { message: `${paramName} must not contain path traversal sequences`, code: 'VALIDATION_ERROR' },\n };\n }\n }\n\n return null;\n}\n\n/**\n * Sanitize a storage path for FormData submission.\n * Normalizes separators and strips leading slashes / traversal.\n */\nexport function sanitizePath(path: string): string {\n return path\n .replace(/\\\\/g, '/') // normalize backslashes\n .split('/')\n .filter(seg => seg !== '..' && seg !== '' && seg !== '.')\n .join('/');\n}\n","import { errorMessage, type HttpClient } from './client';\nimport type { ApiResult } from '../types';\nimport { validatePath } from './validation';\n\n/**\n * Encode a storage path for safe interpolation into a URL while preserving the\n * `/` separators that denote nested paths (e.g. `avatars/user1.png`). Each\n * segment is `encodeURIComponent`-encoded so `?`, `#`, spaces, etc. inside a\n * segment can't alter URL structure. Empty segments (from `//` or leading `/`)\n * are dropped. Use only after `validatePath()` has rejected traversal/null bytes.\n */\nfunction encodeStoragePath(path: string): string {\n return path\n .split('/')\n .filter((seg) => seg.length > 0)\n .map((seg) => encodeURIComponent(seg))\n .join('/');\n}\n\nexport interface StorageFile {\n id: string;\n bucket: string;\n path: string;\n filename: string;\n originalName: string;\n mimeType: string;\n size: number;\n url: string;\n createdAt: string;\n}\n\n/** On-the-fly image transform options for {@link StorageBucket.getPublicUrl}. */\nexport interface ImageTransformOptions {\n width?: number;\n height?: number;\n format?: 'webp' | 'jpeg' | 'png' | 'avif';\n quality?: number;\n fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';\n}\n\n/** Serialize transform options into a query string (omitting empty values). */\nfunction buildTransformQuery(t?: ImageTransformOptions): string {\n if (!t) return '';\n const p = new URLSearchParams();\n if (t.width) p.set('width', String(t.width));\n if (t.height) p.set('height', String(t.height));\n if (t.format) p.set('format', t.format);\n if (t.quality) p.set('quality', String(t.quality));\n if (t.fit) p.set('fit', t.fit);\n return p.toString();\n}\n\nexport interface StorageListItem {\n name: string;\n size: number;\n createdAt: string;\n}\n\nexport interface UploadOptions {\n /** @deprecated Not supported by the API; ignored. */\n cacheControl?: string;\n upsert?: boolean;\n /**\n * MIME type stored with the file. Defaults to the Blob's own type, else a\n * guess from the path's extension, else `application/octet-stream`.\n */\n contentType?: string;\n}\n\n/** Common extensions → MIME type, so Buffer/ArrayBuffer uploads aren't stored as octet-stream. */\nconst MIME_BY_EXT: Record<string, string> = {\n png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp',\n avif: 'image/avif', svg: 'image/svg+xml', ico: 'image/x-icon', pdf: 'application/pdf',\n json: 'application/json', txt: 'text/plain', csv: 'text/csv', html: 'text/html', css: 'text/css',\n js: 'text/javascript', mp4: 'video/mp4', webm: 'video/webm', mp3: 'audio/mpeg', wav: 'audio/wav',\n zip: 'application/zip',\n};\n\nfunction guessContentType(path: string): string | undefined {\n const ext = path.split('.').pop()?.toLowerCase() ?? '';\n return MIME_BY_EXT[ext];\n}\n\n/** The controller wraps results as `{ data, error: null }`; return the inner payload. */\nfunction unwrapEnvelope<T>(result: ApiResult<unknown>): ApiResult<T> {\n if (result.error) return result;\n const body = result.data as { data?: T } | null;\n return { data: (body?.data ?? body) as T, error: null };\n}\n\nexport class StorageClient {\n constructor(private client: HttpClient) {}\n\n /**\n * Get a bucket client for performing operations\n */\n from(bucket: string): StorageBucket {\n return new StorageBucket(this.client, bucket);\n }\n}\n\nexport class StorageBucket {\n constructor(\n private client: HttpClient,\n private bucket: string\n ) {}\n\n // The project-apiKey-authenticated client storage controller lives at the\n // singular `/api/project/:slug/storage/...` mount (StorageAuthGuard). The\n // plural `/api/projects/...` route is the studio admin controller and\n // requires a studio JWT, not a project apiKey — so the SDK uses singular.\n private get basePath(): string {\n return `/api/project/${this.client.getProjectSlug()}/storage/${this.bucket}`;\n }\n\n /** Absolute URL for a path relative to the configured baseUrl. */\n private absoluteUrl(path: string): string {\n return `${this.client.getBaseUrl()}${path}`;\n }\n\n /**\n * Upload a file to the bucket.\n *\n * Posts multipart/form-data to `POST /api/project/{slug}/storage/{bucket}/upload`\n * as the signed-in user when there is a session (the project key otherwise).\n * Sends fields:\n * - `file` binary\n * - `path` destination path inside the bucket\n * - `upsert` \"true\" if overwriting (optional)\n */\n async upload(\n path: string,\n file: Blob | File | ArrayBuffer | Uint8Array,\n options?: UploadOptions\n ): Promise<ApiResult<StorageFile>> {\n // Reject path traversal (`..`) and null bytes before the path reaches the\n // server (it is sent as the `path` form field below).\n const invalid = validatePath(path);\n if (invalid) return invalid as ApiResult<StorageFile>;\n\n const formData = new FormData();\n\n // Normalize to a typed Blob. `File`/`Blob`, `ArrayBuffer`, typed arrays\n // and Node `Buffer`s (a `Uint8Array`) are all valid `BlobPart`s — no\n // Node-only global is referenced, so this is safe in the browser. The type\n // becomes the part's Content-Type, which the API stores as the MIME type.\n const type = options?.contentType || (file instanceof Blob && file.type) || guessContentType(path) || 'application/octet-stream';\n const blob = new Blob([file as BlobPart], { type });\n\n formData.append('file', blob, path.split('/').pop() || 'file');\n formData.append('path', path);\n if (options?.upsert) {\n formData.append('upsert', 'true');\n }\n\n // Storage upload bypasses the JSON HttpClient helper because it sends\n // multipart/form-data, not JSON. We still must use the absolute URL\n // (baseUrl + basePath) — Node's fetch rejects relative URLs.\n const url = this.absoluteUrl(`${this.basePath}/upload`);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30000);\n\n try {\n const response = await this.client.authFetch(url, {\n method: 'POST',\n body: formData,\n // Fail loudly on unexpected redirects rather than silently forwarding\n // the Authorization header to the redirect target.\n redirect: 'error',\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n return {\n data: null,\n error: {\n message: errorMessage(data, response.status, 'Upload failed'),\n code: data?.code,\n status: response.status,\n details: data ?? undefined,\n },\n };\n }\n\n // Controller wraps the result as `{ data, error: null }` — unwrap to\n // the StorageFile payload to match the rest of the SDK's return shape.\n return { data: (data?.data ?? data) as StorageFile, error: null };\n } catch (err) {\n clearTimeout(timeoutId);\n if (err instanceof Error && err.name === 'AbortError') {\n return { data: null, error: { message: 'Upload timeout', code: 'TIMEOUT' } };\n }\n return {\n data: null,\n error: {\n message: err instanceof Error ? err.message : 'Upload failed',\n code: 'UPLOAD_ERROR',\n },\n };\n }\n }\n\n /**\n * Download a file from the bucket.\n *\n * Calls `GET /api/project/{slug}/storage/{bucket}/download/{path}` and\n * returns the response body as a Blob.\n */\n async download(path: string): Promise<ApiResult<Blob>> {\n const invalid = validatePath(path);\n if (invalid) return invalid as ApiResult<Blob>;\n\n // Encode each segment (preserving `/` separators) so the path can't inject\n // URL syntax or traverse outside the bucket.\n const url = this.absoluteUrl(`${this.basePath}/download/${encodeStoragePath(path)}`);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30000);\n\n try {\n const response = await this.client.authFetch(url, {\n // Fail loudly on unexpected redirects rather than silently forwarding\n // the Authorization header to the redirect target.\n redirect: 'error',\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const body = await response.json().catch(() => null);\n return {\n data: null,\n error: {\n message: errorMessage(body, response.status, 'Download failed'),\n code: body?.code,\n status: response.status,\n details: body ?? undefined,\n },\n };\n }\n\n const blob = await response.blob();\n return { data: blob, error: null };\n } catch (err) {\n clearTimeout(timeoutId);\n if (err instanceof Error && err.name === 'AbortError') {\n return { data: null, error: { message: 'Download timeout', code: 'TIMEOUT' } };\n }\n return {\n data: null,\n error: {\n message: err instanceof Error ? err.message : 'Download failed',\n code: 'DOWNLOAD_ERROR',\n },\n };\n }\n }\n\n /**\n * Delete files from the bucket\n */\n async remove(paths: string[]): Promise<ApiResult<{ deleted: string[]; errors: string[] }>> {\n // Reject path traversal (`..`) and null bytes on every path before they\n // reach the server (paths are sent in the request body).\n for (const path of paths) {\n const invalid = validatePath(path);\n if (invalid) return invalid as ApiResult<{ deleted: string[]; errors: string[] }>;\n }\n return unwrapEnvelope(await this.client.request(this.basePath, {\n method: 'DELETE',\n body: { paths },\n }));\n }\n\n /**\n * List files in the bucket\n */\n async list(\n prefix?: string,\n options?: { limit?: number }\n ): Promise<ApiResult<StorageListItem[]>> {\n const params = new URLSearchParams();\n if (prefix) params.set('prefix', prefix);\n if (options?.limit) params.set('limit', options.limit.toString());\n\n const query = params.toString() ? `?${params.toString()}` : '';\n return unwrapEnvelope(await this.client.request(`${this.basePath}${query}`));\n }\n\n /**\n * Get the public URL for a file. Routes through the anonymous public-storage\n * controller mounted at `/api/public/:slug/storage/:bucket/*path` — no\n * Authorization header is required. Use this for files in public buckets.\n *\n * Pass `transform` to request an on-the-fly image rendition (resize/format):\n *\n * ```ts\n * storage.from('avatars').getPublicUrl('u/1.png', {\n * transform: { width: 128, height: 128, format: 'webp', quality: 80 },\n * });\n * ```\n */\n getPublicUrl(path: string, options?: { transform?: ImageTransformOptions }): { data: { publicUrl: string } } {\n // This method is synchronous and can't return an ApiResult error, so a\n // traversal/null-byte attempt throws (it's a caller bug or attack, not a\n // recoverable runtime error).\n const invalid = validatePath(path);\n if (invalid) {\n throw new Error(`NestCore: ${invalid.error?.message ?? 'invalid storage path'}`);\n }\n\n const baseUrl = this.client.getBaseUrl();\n const projectSlug = this.client.getProjectSlug();\n // Encode each path segment (preserving `/` separators) so the path can't\n // inject URL syntax or traverse outside the bucket.\n let publicUrl = `${baseUrl}/api/public/${projectSlug}/storage/${this.bucket}/${encodeStoragePath(path)}`;\n\n const qs = buildTransformQuery(options?.transform);\n if (qs) publicUrl += `?${qs}`;\n\n return {\n data: { publicUrl },\n };\n }\n\n /**\n * Create the bucket if it doesn't exist (requires a service_role key).\n * `public` is sent only when given, so an \"ensure bucket\" call leaves an\n * existing bucket's visibility unchanged; new buckets default to private.\n */\n async createBucket(\n options: { public?: boolean } = {},\n ): Promise<ApiResult<{ bucket: string; created: boolean }>> {\n return unwrapEnvelope(await this.client.request(\n `${this.basePath}/create`,\n { method: 'POST', body: options.public === undefined ? undefined : { public: options.public } }\n ));\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\r\n// NestCore Realtime — Node SDK\r\n// Provides a Supabase-compatible channel API over a native WebSocket.\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type RealtimeStatus = 'connecting' | 'connected' | 'disconnected' | 'error';\r\n\r\n// ─── Presence types ────────────────────────────────────────────────────────────\r\n\r\nexport type PresenceState = Record<string, any[]>;\r\nexport type PresenceSyncCallback = (state: PresenceState) => void;\r\nexport type PresenceJoinCallback = (key: string, presences: any[]) => void;\r\nexport type PresenceLeaveCallback = (key: string, presences: any[]) => void;\r\n\r\nexport type PgEvent = 'INSERT' | 'UPDATE' | 'DELETE' | '*';\r\n\r\nexport interface PgChangesPayload {\r\n op: 'INSERT' | 'UPDATE' | 'DELETE';\r\n schema: string;\r\n table: string;\r\n new?: Record<string, unknown>;\r\n old?: Record<string, unknown>;\r\n commit_ts: string;\r\n}\r\n\r\nexport interface BroadcastPayload {\r\n event: string;\r\n payload: unknown;\r\n}\r\n\r\nexport interface PgChangesFilter {\r\n event: PgEvent | PgEvent[];\r\n schema?: string;\r\n table: string;\r\n filter?: Record<string, FilterValue>;\r\n}\r\n\r\nexport interface FilterValue {\r\n eq?: string | number;\r\n neq?: string | number;\r\n in?: Array<string | number>;\r\n gt?: number | string;\r\n lt?: number | string;\r\n like?: string;\r\n}\r\n\r\ntype PgCallback = (payload: PgChangesPayload) => void;\r\ntype BroadcastCallback = (payload: BroadcastPayload) => void;\r\ntype StatusCallback = (status: RealtimeStatus, err?: Error) => void;\r\n\r\ninterface ChannelHandler {\r\n type: 'postgres_changes' | 'broadcast';\r\n opts: PgChangesFilter | { event: string };\r\n callback: PgCallback | BroadcastCallback;\r\n}\r\n\r\n// ─── PresenceManager ─────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Presence API attached to a broadcast channel. Tracks who is currently\r\n * online and fires join/leave callbacks when the roster changes.\r\n *\r\n * ```ts\r\n * const ch = client.channel('room')\r\n * ch.presence\r\n * .onSync((state) => console.log('roster', state))\r\n * .onJoin((key, list) => console.log('joined', key, list))\r\n * .onLeave((key, list) => console.log('left', key, list))\r\n * await ch.subscribe()\r\n * await ch.presence.track({ name: 'Alice' })\r\n * ```\r\n */\r\nexport class PresenceManager {\r\n private state: PresenceState = {};\r\n private syncCbs: PresenceSyncCallback[] = [];\r\n private joinCbs: PresenceJoinCallback[] = [];\r\n private leaveCbs: PresenceLeaveCallback[] = [];\r\n\r\n constructor(\r\n private readonly channelName: string,\r\n private readonly client: RealtimeClient,\r\n ) {}\r\n\r\n /** Send presence state to the server; triggers a `presence.state` fan-out. */\r\n async track(state: Record<string, any>): Promise<void> {\r\n await this.client['sendPresenceTrack'](this.channelName, state);\r\n }\r\n\r\n /**\r\n * Stop tracking presence on this channel. Other clients will see the\r\n * departure in their next `presence.state` message.\r\n */\r\n async untrack(): Promise<void> {\r\n this.client['sendPresenceUntrack'](this.channelName);\r\n }\r\n\r\n /** Returns a shallow copy of the last-known presence roster. */\r\n getState(): PresenceState { return { ...this.state }; }\r\n\r\n /** True once any presence callback is registered. */\r\n _hasListeners(): boolean {\r\n return this.syncCbs.length + this.joinCbs.length + this.leaveCbs.length > 0;\r\n }\r\n\r\n /** Register a callback fired on every full roster sync. */\r\n onSync(cb: PresenceSyncCallback): this { this.syncCbs.push(cb); return this; }\r\n\r\n /** Register a callback fired when a new socket joins the channel. */\r\n onJoin(cb: PresenceJoinCallback): this { this.joinCbs.push(cb); return this; }\r\n\r\n /** Register a callback fired when a socket leaves the channel. */\r\n onLeave(cb: PresenceLeaveCallback): this { this.leaveCbs.push(cb); return this; }\r\n\r\n // ─── Internal ───────────────────────────────────────────────────────────────\r\n\r\n _handleStateUpdate(newState: PresenceState): void {\r\n const prev = this.state;\r\n\r\n // Detect joins (keys present in new but not in prev)\r\n for (const [key, presences] of Object.entries(newState)) {\r\n if (!prev[key]) {\r\n for (const cb of this.joinCbs) cb(key, presences);\r\n }\r\n }\r\n\r\n // Detect leaves (keys present in prev but not in new)\r\n for (const [key, presences] of Object.entries(prev)) {\r\n if (!newState[key]) {\r\n for (const cb of this.leaveCbs) cb(key, presences);\r\n }\r\n }\r\n\r\n this.state = { ...newState };\r\n for (const cb of this.syncCbs) cb({ ...this.state });\r\n }\r\n}\r\n\r\n// ─── RealtimeChannel ──────────────────────────────────────────────────────────\r\n\r\nexport class RealtimeChannel {\r\n private handlers: ChannelHandler[] = [];\r\n private statusCb?: StatusCallback;\r\n /** Set by subscribe(); only joined channels hold server subscriptions. */\r\n _joined = false;\r\n\r\n /** Presence API for this channel. */\r\n readonly presence: PresenceManager;\r\n\r\n constructor(\r\n private readonly name: string,\r\n private readonly client: RealtimeClient,\r\n ) {\r\n this.presence = new PresenceManager(name, client);\r\n }\r\n\r\n /**\r\n * Register a handler for Postgres row-level changes.\r\n *\r\n * ```ts\r\n * ch.on('postgres_changes',\r\n * { event: 'INSERT', table: 'orders', filter: { user_id: { eq: 'abc' } } },\r\n * (e) => console.log(e))\r\n * ```\r\n */\r\n on(type: 'postgres_changes', opts: PgChangesFilter, cb: PgCallback): this;\r\n on(type: 'broadcast', opts: { event: string }, cb: BroadcastCallback): this;\r\n on(type: string, opts: any, cb: any): this {\r\n this.handlers.push({ type: type as any, opts, callback: cb });\r\n return this;\r\n }\r\n\r\n /**\r\n * Subscribe. The optional callback receives status transitions.\r\n *\r\n * ```ts\r\n * ch.subscribe((status, err) => console.log(status, err));\r\n * ```\r\n */\r\n subscribe(cb?: StatusCallback): this {\r\n this.statusCb = cb;\r\n this._joined = true;\r\n this.client['registerChannel'](this);\r\n return this;\r\n }\r\n\r\n /** Send a broadcast message on this channel. */\r\n async send(event: string, payload: unknown): Promise<void> {\r\n await this.client['sendBroadcast'](this.name, event, payload);\r\n }\r\n\r\n /** Unsubscribe and remove the channel. */\r\n async unsubscribe(): Promise<void> {\r\n await this.client['unregisterChannel'](this.name);\r\n }\r\n\r\n // ─── Internal ───────────────────────────────────────────────────────────────\r\n\r\n get channelName(): string { return this.name; }\r\n\r\n /** Called by RealtimeClient when the WS is ready — sends subscribe messages. */\r\n _activate(ws: WebSocketLike): void {\r\n for (const key of this._tableKeys()) {\r\n const [schema, table] = splitTableKey(key);\r\n this.client['syncTable'](ws, schema, table);\r\n }\r\n if (this._hasBroadcast()) {\r\n // Subscribe to the broadcast channel (no DB trigger needed)\r\n ws.send(JSON.stringify({\r\n id: `${this.name}-bc-${Math.random().toString(36).slice(2)}`,\r\n type: 'subscribe',\r\n channel: `broadcast:${this.name}`,\r\n }));\r\n }\r\n this.statusCb?.('connected');\r\n }\r\n\r\n /** `schema.table` keys this channel listens on (empty unless joined). */\r\n _tableKeys(): string[] {\r\n if (!this._joined) return [];\r\n const keys = this.handlers\r\n .filter((h) => h.type === 'postgres_changes')\r\n .map((h) => `${(h.opts as PgChangesFilter).schema ?? 'public'}.${(h.opts as PgChangesFilter).table}`);\r\n return [...new Set(keys)];\r\n }\r\n\r\n /** Postgres handler options on one table (empty unless joined). */\r\n _tableHandlers(schema: string, table: string): PgChangesFilter[] {\r\n if (!this._joined) return [];\r\n return this.handlers\r\n .filter((h) => h.type === 'postgres_changes')\r\n .map((h) => h.opts as PgChangesFilter)\r\n .filter((o) => (o.schema ?? 'public') === schema && o.table === table);\r\n }\r\n\r\n /**\r\n * Whether the server must hold `broadcast:<name>` for this channel: broadcast\r\n * handlers, presence listeners, or no table handlers at all (a room used for\r\n * presence only still needs the roster fan-out, including leaves).\r\n */\r\n _hasBroadcast(): boolean {\r\n return this.handlers.some((h) => h.type === 'broadcast')\r\n || this.presence._hasListeners()\r\n || !this.handlers.some((h) => h.type === 'postgres_changes');\r\n }\r\n\r\n _setStatus(status: RealtimeStatus, err?: Error): void {\r\n this.statusCb?.(status, err);\r\n }\r\n\r\n /** Route an inbound server message to matching handlers. */\r\n _dispatch(msg: Record<string, any>): void {\r\n if (msg.type === 'db_event') {\r\n const payload: PgChangesPayload = {\r\n op: msg.op,\r\n schema: msg.schema,\r\n table: msg.table,\r\n new: msg.new,\r\n old: msg.old,\r\n commit_ts: msg.commit_ts,\r\n };\r\n for (const h of this.handlers) {\r\n if (h.type !== 'postgres_changes') continue;\r\n const opts = h.opts as PgChangesFilter;\r\n const events = normaliseEvents(opts.event);\r\n if (!events.includes(msg.op) && !events.includes('*')) continue;\r\n if (opts.table && opts.table !== msg.table) continue;\r\n if (opts.schema && opts.schema !== msg.schema) continue;\r\n // The server keeps one subscription per table, so re-check this\r\n // handler's own filter before delivering.\r\n if (!matchesFilter(msg.op === 'DELETE' ? msg.old : msg.new, opts.filter)) continue;\r\n (h.callback as PgCallback)(payload);\r\n }\r\n } else if (msg.type === 'broadcast') {\r\n const payload: BroadcastPayload = { event: msg.event, payload: msg.payload };\r\n for (const h of this.handlers) {\r\n if (h.type !== 'broadcast') continue;\r\n const opts = h.opts as { event: string };\r\n if (opts.event !== '*' && opts.event !== msg.event) continue;\r\n (h.callback as BroadcastCallback)(payload);\r\n }\r\n } else if (msg.type === 'presence.state') {\r\n this.presence._handleStateUpdate((msg.presences as PresenceState) ?? {});\r\n }\r\n }\r\n}\r\n\r\n// ─── RealtimeClient ───────────────────────────────────────────────────────────\r\n\r\nexport interface RealtimeOptions {\r\n /** Override with a custom WebSocket constructor (e.g. the 'ws' package on\r\n * older Node versions). Defaults to globalThis.WebSocket. */\r\n WebSocket?: WebSocketConstructor;\r\n /** Optional project-user JWT for RLS-scoped subscriptions. */\r\n userJwt?: string;\r\n /**\r\n * Resolves the signed-in user's JWT before each connect (takes precedence\r\n * over `userJwt`). `createClient` wires this to `auth`.\r\n */\r\n accessToken?: () => Promise<string | null>;\r\n /**\r\n * Called once when the server rejects the socket's user JWT (close 4001),\r\n * e.g. to refresh the session; a new token arrives through `setAuth`.\r\n */\r\n onAuthRejected?: (staleToken: string) => void;\r\n /**\r\n * Project API key. Sent on the handshake via a WS subprotocol token and an\r\n * Authorization header — never in the URL query string (it is a credential\r\n * that would otherwise leak into proxy/CDN logs and browser history).\r\n */\r\n apiKey?: string;\r\n}\r\n\r\n// `protocols` covers both browser WebSocket and the `ws` package's\r\n// `(url, protocols)` signature. `options` is the optional third argument that\r\n// the `ws` package accepts for handshake-level config (e.g. custom `headers`);\r\n// browser WebSocket ignores it, so it is best-effort for header transport.\r\ntype WebSocketConstructor = new (\r\n url: string,\r\n protocols?: string | string[],\r\n options?: { headers?: Record<string, string> },\r\n) => WebSocketLike;\r\n\r\ninterface WebSocketLike {\r\n readonly readyState: number;\r\n send(data: string): void;\r\n close(code?: number, reason?: string): void;\r\n onopen: ((ev: any) => any) | null;\r\n onclose: ((ev: any) => any) | null;\r\n onerror: ((ev: any) => any) | null;\r\n onmessage: ((ev: any) => any) | null;\r\n}\r\n\r\nconst CONNECTING = 0;\r\nconst OPEN = 1;\r\n\r\n/** Initial reconnect delay under normal conditions. */\r\nconst BASE_RECONNECT_DELAY_MS = 1_000;\r\n/** Floor delay when the server closes us for rate-limiting / policy reasons. */\r\nconst RATE_LIMITED_RECONNECT_DELAY_MS = 10_000;\r\n/** Absolute upper bound on exponential backoff. */\r\nconst MAX_RECONNECT_DELAY_MS = 60_000;\r\n/** WS close code the gateway uses when a socket is closed for rate limiting. */\r\nconst WS_CLOSE_RATE_LIMITED = 4029;\r\n\r\nexport class RealtimeClient {\r\n private ws: WebSocketLike | null = null;\r\n private channels = new Map<string, RealtimeChannel>();\r\n private reconnectDelay = BASE_RECONNECT_DELAY_MS;\r\n private maxReconnectDelay = MAX_RECONNECT_DELAY_MS;\r\n private shouldReconnect = true;\r\n private connectPromise: Promise<void> | null = null;\r\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\r\n /** User JWT the current socket was opened with. */\r\n private socketJwt: string | undefined;\r\n private authChanged = false;\r\n /** onAuthRejected already fired since the last accepted connect (no refresh loop). */\r\n private authRetried = false;\r\n\r\n constructor(\r\n private readonly url: string,\r\n private readonly options: RealtimeOptions = {},\r\n ) {}\r\n\r\n /** Get or create a channel. Call `.on()` then `.subscribe()` on the result. */\r\n channel(name: string): RealtimeChannel {\r\n if (!this.channels.has(name)) {\r\n this.channels.set(name, new RealtimeChannel(name, this));\r\n }\r\n return this.channels.get(name)!;\r\n }\r\n\r\n /**\r\n * Set the user JWT for RLS-scoped events. Re-opens the socket when it\r\n * differs from the one in use. `createClient` calls this on every auth change.\r\n */\r\n setAuth(token: string | null): void {\r\n if (!this.options.accessToken) this.options.userJwt = token ?? undefined;\r\n if ((token ?? undefined) === this.socketJwt || !this.ws) return;\r\n if (this.ws.readyState === CONNECTING) {\r\n this.authChanged = true; // re-open once the in-flight connect finishes\r\n return;\r\n }\r\n // Open, or closed (e.g. 4001 for an expired JWT): connect with the new one.\r\n this.reopen();\r\n }\r\n\r\n /** Tear down all channels and close the WebSocket. */\r\n disconnect(): void {\r\n this.shouldReconnect = false;\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = null;\r\n }\r\n this.ws?.close(1000, 'client disconnect');\r\n this.ws = null;\r\n this.channels.clear();\r\n }\r\n\r\n // ─── Internal ───────────────────────────────────────────────────────────────\r\n\r\n private connect(): Promise<void> {\r\n if (this.connectPromise) return this.connectPromise;\r\n if (this.ws?.readyState === OPEN) return Promise.resolve();\r\n\r\n this.connectPromise = this.resolveUserJwt().then((userJwt) => new Promise<void>((resolve, reject) => {\r\n const WS: WebSocketConstructor =\r\n this.options.WebSocket ?? (globalThis as any).WebSocket;\r\n\r\n if (!WS) {\r\n reject(new Error(\r\n 'No WebSocket implementation found. ' +\r\n 'On Node.js < 22, pass a WebSocket constructor via options.WebSocket ' +\r\n '(e.g. from the \"ws\" package).',\r\n ));\r\n return;\r\n }\r\n\r\n const protocols: string[] = ['orbitnest-realtime-v1'];\r\n this.socketJwt = userJwt;\r\n if (userJwt) {\r\n protocols.push(`orbitnest.user-jwt.${userJwt}`);\r\n }\r\n // Carry the project API key on the handshake instead of in the URL query\r\n // string (which leaks into proxy/CDN access logs and browser history).\r\n // We send it via BOTH a subprotocol token AND an Authorization header to\r\n // maximize server-side compatibility: subprotocols work in every\r\n // environment (browsers can't set handshake headers), while the\r\n // Authorization header is honored by the `ws` package's handshake options.\r\n let handshakeOptions: { headers?: Record<string, string> } | undefined;\r\n if (this.options.apiKey) {\r\n protocols.push(`orbitnest.apikey.${this.options.apiKey}`);\r\n handshakeOptions = { headers: { Authorization: `Bearer ${this.options.apiKey}` } };\r\n }\r\n\r\n const ws = new WS(this.url, protocols, handshakeOptions);\r\n\r\n ws.onopen = () => {\r\n this.reconnectDelay = BASE_RECONNECT_DELAY_MS;\r\n this.connectPromise = null;\r\n resolve();\r\n if (this.authChanged) {\r\n this.authChanged = false;\r\n this.reopen();\r\n return;\r\n }\r\n // Activate all joined channels\r\n for (const ch of this.channels.values()) if (ch._joined) ch._activate(ws);\r\n };\r\n\r\n ws.onmessage = (ev: any) => {\r\n try {\r\n const msg = JSON.parse(typeof ev.data === 'string' ? ev.data : ev.data.toString());\r\n this.route(msg);\r\n } catch { /* ignore malformed */ }\r\n };\r\n\r\n ws.onerror = (ev: any) => {\r\n const err = new Error(ev?.message ?? 'WebSocket error');\r\n for (const ch of this.channels.values()) ch._setStatus('error', err);\r\n this.connectPromise = null;\r\n reject(err);\r\n };\r\n\r\n ws.onclose = (ev: any) => {\r\n this.connectPromise = null;\r\n for (const ch of this.channels.values()) ch._setStatus('disconnected');\r\n\r\n // Inspect close code to decide backoff floor.\r\n const code: number | undefined = ev?.code;\r\n // 1000 (normal) + 1001 (going away) and auth-failed (4001) → do not reconnect.\r\n if (code === 1000 || code === 1001 || code === 4001) {\r\n this.shouldReconnect = false;\r\n }\r\n // Rejected user JWT (expired/revoked/clock skew): refresh once; the new\r\n // token reopens the socket via setAuth.\r\n if (code === 4001 && this.socketJwt && !this.authRetried && this.options.onAuthRejected) {\r\n this.authRetried = true;\r\n this.options.onAuthRejected(this.socketJwt);\r\n }\r\n\r\n if (this.shouldReconnect) {\r\n // If the server closed us for rate limiting / policy violation, jump to\r\n // a much longer base delay so we don't create a reconnect storm.\r\n if (\r\n code === WS_CLOSE_RATE_LIMITED ||\r\n code === 1008 ||\r\n code === 1013\r\n ) {\r\n this.reconnectDelay = Math.max(\r\n this.reconnectDelay,\r\n RATE_LIMITED_RECONNECT_DELAY_MS,\r\n );\r\n }\r\n\r\n // Add small jitter so many clients don't all reconnect at the same ms.\r\n const jitter = Math.floor(Math.random() * 500);\r\n const delay = Math.min(this.reconnectDelay + jitter, this.maxReconnectDelay);\r\n if (this.reconnectTimer) clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = setTimeout(() => this.reconnect(), delay);\r\n this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);\r\n }\r\n };\r\n\r\n this.ws = ws;\r\n }));\r\n\r\n return this.connectPromise;\r\n }\r\n\r\n private async resolveUserJwt(): Promise<string | undefined> {\r\n if (!this.options.accessToken) return this.options.userJwt;\r\n try {\r\n return (await this.options.accessToken()) ?? undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n }\r\n\r\n /** Close the socket silently and open a new one (picks up the current JWT). */\r\n private reopen(): void {\r\n const ws = this.ws;\r\n if (ws) {\r\n ws.onclose = null;\r\n ws.onerror = null;\r\n ws.onmessage = null;\r\n ws.close(1000, 'auth changed');\r\n }\r\n this.ws = null;\r\n this.connectPromise = null;\r\n if ([...this.channels.values()].some((c) => c._joined)) {\r\n this.shouldReconnect = true;\r\n this.connect().catch(() => {});\r\n }\r\n }\r\n\r\n /**\r\n * The server keeps one subscription per table per socket (last subscribe\r\n * wins), so send the union of events of every handler on the table, and a\r\n * filter only when they all share the same one. Handlers re-check their own\r\n * filters client-side.\r\n */\r\n private syncTable(ws: WebSocketLike, schema: string, table: string): void {\r\n const opts = [...this.channels.values()].flatMap((c) => c._tableHandlers(schema, table));\r\n if (opts.length === 0) return;\r\n const events = [...new Set(opts.flatMap((o) => normaliseEvents(o.event)))];\r\n const sameFilter = new Set(opts.map((o) => JSON.stringify(o.filter ?? null))).size === 1;\r\n ws.send(JSON.stringify({\r\n id: `rt-${Math.random().toString(36).slice(2)}`,\r\n type: 'subscribe',\r\n channel: `table:${schema}.${table}`,\r\n events,\r\n filter: sameFilter ? opts[0].filter : undefined,\r\n }));\r\n }\r\n\r\n private reconnect(): void {\r\n this.reconnectTimer = null;\r\n this.connectPromise = null;\r\n this.ws = null;\r\n if (this.shouldReconnect && [...this.channels.values()].some((c) => c._joined)) {\r\n this.connect().catch(() => {});\r\n }\r\n }\r\n\r\n private route(msg: Record<string, any>): void {\r\n if (msg.type === 'system' && msg.event === 'connected') this.authRetried = false;\r\n if (msg.type === 'db_event') {\r\n // Match by the full channel name stored in the channel instance\r\n for (const ch of this.channels.values()) {\r\n ch._dispatch(msg);\r\n }\r\n } else if (msg.type === 'broadcast') {\r\n for (const ch of this.channels.values()) {\r\n if (`broadcast:${ch.channelName}` === msg.channel) {\r\n ch._dispatch(msg);\r\n }\r\n }\r\n } else if (msg.type === 'presence.state') {\r\n // Route to the channel whose broadcast channel matches.\r\n const channelName = (msg.channel as string | undefined)?.replace(/^broadcast:/, '');\r\n if (channelName) {\r\n const ch = this.channels.get(channelName);\r\n if (ch) ch._dispatch(msg);\r\n }\r\n }\r\n // 'subscribed', 'system', 'error' messages — no-op for now\r\n }\r\n\r\n private registerChannel(ch: RealtimeChannel): void {\r\n this.channels.set(ch.channelName, ch);\r\n // A previous disconnect() / last-channel close disabled reconnects.\r\n this.shouldReconnect = true;\r\n ch._setStatus('connecting');\r\n // An open socket activates now; otherwise onopen activates every joined\r\n // channel (activating in both places sent duplicate subscribes).\r\n if (this.ws?.readyState === OPEN) {\r\n ch._activate(this.ws);\r\n return;\r\n }\r\n this.connect().catch((err) => ch._setStatus('error', err));\r\n }\r\n\r\n private async unregisterChannel(name: string): Promise<void> {\r\n const ch = this.channels.get(name);\r\n if (!ch) return;\r\n // Server channel names this channel used, before it leaves the map.\r\n const tableKeys = ch._tableKeys();\r\n ch._joined = false;\r\n this.channels.delete(name);\r\n\r\n const ws = this.ws;\r\n if (ws?.readyState === OPEN) {\r\n const unsubscribe = (channel: string) => ws.send(JSON.stringify({\r\n id: `unsub-${Math.random().toString(36).slice(2)}`,\r\n type: 'unsubscribe',\r\n channel,\r\n }));\r\n for (const key of tableKeys) {\r\n const [schema, table] = splitTableKey(key);\r\n const stillUsed = [...this.channels.values()].some((c) => c._tableHandlers(schema, table).length > 0);\r\n // Another channel still listens on this table: re-send its subscription.\r\n if (stillUsed) this.syncTable(ws, schema, table);\r\n else unsubscribe(`table:${key}`);\r\n }\r\n if (ch._hasBroadcast()) unsubscribe(`broadcast:${name}`);\r\n }\r\n if (![...this.channels.values()].some((c) => c._joined)) {\r\n this.ws?.close(1000, 'no channels');\r\n }\r\n }\r\n\r\n private async sendBroadcast(channelName: string, event: string, payload: unknown): Promise<void> {\r\n await this.connect();\r\n if (this.ws?.readyState !== OPEN) throw new Error('Not connected');\r\n this.ws.send(JSON.stringify({\r\n id: `bc-${Math.random().toString(36).slice(2)}`,\r\n type: 'broadcast',\r\n channel: `broadcast:${channelName}`,\r\n event,\r\n payload,\r\n }));\r\n }\r\n\r\n private async sendPresenceTrack(\r\n channelName: string,\r\n state: Record<string, any>,\r\n ): Promise<void> {\r\n await this.connect();\r\n if (this.ws?.readyState !== OPEN) throw new Error('Not connected');\r\n this.ws.send(JSON.stringify({\r\n id: `pt-${Math.random().toString(36).slice(2)}`,\r\n type: 'presence.track',\r\n channel: `broadcast:${channelName}`,\r\n state,\r\n }));\r\n }\r\n\r\n private sendPresenceUntrack(channelName: string): void {\r\n if (this.ws?.readyState !== OPEN) return;\r\n // Drops presence only; the broadcast subscription stays. No ack is sent.\r\n this.ws.send(JSON.stringify({\r\n id: `pu-${Math.random().toString(36).slice(2)}`,\r\n type: 'presence.untrack',\r\n channel: `broadcast:${channelName}`,\r\n }));\r\n }\r\n}\r\n\r\n// ─── Helpers ──────────────────────────────────────────────────────────────────\r\n\r\nfunction normaliseEvents(event: PgEvent | PgEvent[]): string[] {\r\n if (event === '*') return ['INSERT', 'UPDATE', 'DELETE'];\r\n const list = Array.isArray(event) ? event : [event];\r\n return list.includes('*') ? ['INSERT', 'UPDATE', 'DELETE'] : list;\r\n}\r\n\r\nfunction splitTableKey(key: string): [string, string] {\r\n const dot = key.indexOf('.');\r\n return [key.slice(0, dot), key.slice(dot + 1)];\r\n}\r\n\r\n/** Same semantics as the gateway's filter (loose equality, SQL LIKE). */\r\nfunction matchesFilter(\r\n row: Record<string, unknown> | undefined,\r\n filter: Record<string, FilterValue> | undefined,\r\n): boolean {\r\n if (!filter || !row) return true;\r\n for (const [col, c] of Object.entries(filter)) {\r\n const v = row[col] as any;\r\n if (c.eq !== undefined && v != c.eq) return false;\r\n if (c.neq !== undefined && v == c.neq) return false;\r\n if (c.in !== undefined && !c.in.some((x) => x == v)) return false;\r\n if (c.gt !== undefined && !(v > c.gt)) return false;\r\n if (c.lt !== undefined && !(v < c.lt)) return false;\r\n if (c.like !== undefined) {\r\n const re = c.like.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/%/g, '.*').replace(/_/g, '.');\r\n if (!new RegExp(`^${re}$`, 's').test(String(v))) return false;\r\n }\r\n }\r\n return true;\r\n}\r\n","import type { HttpClient } from './client';\nimport type { ApiResult } from '../types';\n\ntype Platform = 'node' | 'web' | 'react-native' | 'unknown';\n\nexport interface AnalyticsEvent {\n event_id: string;\n event: string;\n anonymous_id: string;\n platform: Platform;\n timestamp: string;\n session_id?: string;\n user_id?: string;\n app_id?: string;\n sdk_version?: string;\n app_version?: string;\n properties?: Record<string, unknown>;\n device_info?: {\n model?: string;\n os?: string;\n os_version?: string;\n brand?: string;\n is_tablet?: boolean;\n locale?: string;\n timezone?: string;\n screen_width?: number;\n screen_height?: number;\n };\n}\n\nexport interface IngestionResult {\n accepted: number;\n rejected: number;\n errors: Array<{ event_id: string; reason: string }>;\n}\n\nexport interface AnalyticsOptions {\n platform?: Platform;\n appVersion?: string;\n flushInterval?: number;\n maxBatchSize?: number;\n}\n\nconst MAX_BATCH_SIZE_CAP = 500;\nconst DEFAULT_BATCH_SIZE = 50;\nconst DEFAULT_FLUSH_INTERVAL = 5000;\n/** Cap on events kept for retry while the API is unreachable. */\nconst MAX_QUEUE = 1000;\n\n// Persisted identity keys. In a browser the SDK is re-instantiated on every full\n// page load, so generating a fresh anonymous_id per instance would count the same\n// visitor as a brand-new user on every visit. Persist it in localStorage instead.\nconst ANON_STORAGE_KEY = 'nestcore_anonymous_id';\nconst SESSION_ID_KEY = 'nestcore_session_id';\nconst SESSION_TS_KEY = 'nestcore_session_last_activity';\n/** Keys written before the OrbitNest → NestCore rename, migrated on first read. */\nconst LEGACY_KEYS: Record<string, string> = {\n [ANON_STORAGE_KEY]: 'orbitnest_anonymous_id',\n [SESSION_ID_KEY]: 'orbitnest_session_id',\n [SESSION_TS_KEY]: 'orbitnest_session_last_activity',\n};\n// 30 min of inactivity ends a session — matches analytics_projects.session_timeout_mins.\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000;\n\nfunction generateUuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID but with Web Crypto\n // (crypto.getRandomValues — available in Node >=18 and modern browsers).\n // Build an RFC 4122 v4 UUID from 16 cryptographically-strong random bytes\n // instead of the non-cryptographic, predictable Math.random().\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n }\n // Last-resort fallback for environments lacking Web Crypto entirely.\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n\n/** A localStorage handle, but only if it's actually usable (private-mode and\n * SSR both expose-or-throw). Returns null on the server or when blocked. */\nfunction getLocalStorage(): Storage | null {\n try {\n if (typeof localStorage === 'undefined') return null;\n const probe = '__on_ls_probe__';\n localStorage.setItem(probe, '1');\n localStorage.removeItem(probe);\n return localStorage;\n } catch {\n return null;\n }\n}\n\n/** Read a key, moving a value stored under its pre-rename name to the new key\n * so returning visitors keep their anonymous id and session. */\nfunction readKey(store: Storage, key: string): string | null {\n const value = store.getItem(key);\n if (value !== null) return value;\n const legacy = LEGACY_KEYS[key];\n const old = legacy ? store.getItem(legacy) : null;\n if (old === null || !legacy) return null;\n store.setItem(key, old);\n store.removeItem(legacy);\n return old;\n}\n\n/** Browser vs server — used to default the event platform when not specified. */\nfunction detectPlatform(): Platform {\n return typeof window !== 'undefined' && typeof document !== 'undefined' ? 'web' : 'node';\n}\n\n/** Stable, long-lived per-browser anonymous id (created once, reused forever). */\nfunction loadOrCreateAnonymousId(store: Storage | null): string {\n if (!store) return generateUuid();\n try {\n let id = readKey(store, ANON_STORAGE_KEY);\n if (!id) {\n id = generateUuid();\n store.setItem(ANON_STORAGE_KEY, id);\n }\n return id;\n } catch {\n return generateUuid();\n }\n}\n\n/** Current session id, rotating after SESSION_TIMEOUT_MS of inactivity and\n * sliding the activity timestamp on every call. */\nfunction resolveSessionId(store: Storage | null, fallback: string): string {\n if (!store) return fallback;\n try {\n const now = Date.now();\n const last = parseInt(readKey(store, SESSION_TS_KEY) ?? '0', 10) || 0;\n let sid = readKey(store, SESSION_ID_KEY);\n if (!sid || now - last > SESSION_TIMEOUT_MS) {\n sid = generateUuid();\n store.setItem(SESSION_ID_KEY, sid);\n }\n store.setItem(SESSION_TS_KEY, String(now));\n return sid;\n } catch {\n return fallback;\n }\n}\n\nexport class AnalyticsClient {\n private readonly storage: Storage | null;\n private readonly anonymousId: string;\n private readonly fallbackSessionId: string;\n private readonly platform: Platform;\n private readonly appVersion: string | undefined;\n private readonly flushInterval: number;\n private readonly maxBatchSize: number;\n\n private userId: string | undefined;\n private queue: AnalyticsEvent[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(private readonly client: HttpClient, options: AnalyticsOptions = {}) {\n this.storage = getLocalStorage();\n // anonymous_id persists across page loads (stable visitor); session_id rotates\n // on inactivity. On the server (no storage) both fall back to a per-instance uuid.\n this.anonymousId = loadOrCreateAnonymousId(this.storage);\n this.fallbackSessionId = generateUuid();\n this.platform = options.platform ?? detectPlatform();\n this.appVersion = options.appVersion;\n this.flushInterval = options.flushInterval ?? DEFAULT_FLUSH_INTERVAL;\n this.maxBatchSize = Math.min(options.maxBatchSize ?? DEFAULT_BATCH_SIZE, MAX_BATCH_SIZE_CAP);\n\n // Browser: send what's queued when the page is hidden or unloaded, with\n // keepalive so the request outlives the page.\n if (typeof window !== 'undefined' && typeof document !== 'undefined' && window.addEventListener) {\n window.addEventListener('pagehide', () => this.flushInBackground(true));\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') this.flushInBackground(true);\n });\n }\n }\n\n identify(userId: string, traits?: Record<string, unknown>): void {\n this.userId = userId;\n this.enqueue('identify', traits);\n }\n\n track(event: string, properties?: Record<string, unknown>): void {\n this.enqueue(event, properties);\n }\n\n screen(screenName: string, properties?: Record<string, unknown>): void {\n this.enqueue('screen_view', { screen: screenName, ...properties });\n }\n\n crash(error: Error | string, properties?: Record<string, unknown>): void {\n const msg = error instanceof Error ? error.message : error;\n const stack = error instanceof Error ? error.stack : undefined;\n // Field names the crash grouper reads (error_type / error_message / stack_trace).\n this.enqueue('crash', {\n ...(error instanceof Error ? { error_type: error.name } : {}),\n error_message: msg,\n ...(stack ? { stack_trace: stack } : {}),\n ...properties,\n });\n }\n\n async flush(keepalive = false): Promise<ApiResult<IngestionResult>> {\n this.clearFlushTimer();\n\n if (this.queue.length === 0) {\n return { data: { accepted: 0, rejected: 0, errors: [] }, error: null };\n }\n\n const batch = this.queue.splice(0, this.queue.length);\n\n const result = await this.client.requestForAnalytics<IngestionResult>('/api/analytics/events', {\n method: 'POST',\n body: { events: batch },\n keepalive,\n });\n\n // Network errors, timeouts, 429 and 5xx are transient: keep the batch\n // (oldest first, capped) and try again on the next flush.\n const status = result.error?.status;\n if (result.error && (status === undefined || status === 429 || status >= 500)) {\n this.queue = [...batch, ...this.queue].slice(-MAX_QUEUE);\n if (!keepalive) this.scheduleFlush();\n }\n\n return result;\n }\n\n /** Flush from a timer/unload. Dropped events are logged, never thrown. */\n private flushInBackground(keepalive = false): void {\n void this.flush(keepalive).then((r) => {\n const status = r.error?.status;\n if (r.error && status !== undefined && status !== 429 && status < 500) {\n console.warn(`[nestcore] analytics events rejected (${status}): ${r.error.message}`);\n }\n });\n }\n\n private enqueue(event: string, properties?: Record<string, unknown>): void {\n const entry: AnalyticsEvent = {\n event_id: generateUuid(),\n event,\n anonymous_id: this.anonymousId,\n platform: this.platform,\n timestamp: new Date().toISOString(),\n session_id: resolveSessionId(this.storage, this.fallbackSessionId),\n ...(this.userId ? { user_id: this.userId } : {}),\n ...(this.appVersion ? { app_version: this.appVersion } : {}),\n ...(properties ? { properties } : {}),\n };\n\n this.queue.push(entry);\n\n if (this.queue.length >= this.maxBatchSize) {\n this.flushInBackground();\n } else {\n this.scheduleFlush();\n }\n }\n\n private scheduleFlush(): void {\n if (this.flushTimer !== null) return;\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n this.flushInBackground();\n }, this.flushInterval);\n }\n\n private clearFlushTimer(): void {\n if (this.flushTimer !== null) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n }\n}\n","/** `data:` payloads of a server-sent-events body. */\r\nexport async function* sseData(body: ReadableStream<Uint8Array>): AsyncGenerator<string> {\r\n const reader = body.getReader();\r\n const decoder = new TextDecoder();\r\n let buf = '';\r\n try {\r\n for (;;) {\r\n const { value, done } = await reader.read();\r\n if (done) break;\r\n buf += decoder.decode(value, { stream: true });\r\n let nl: number;\r\n while ((nl = buf.indexOf('\\n')) >= 0) {\r\n const line = buf.slice(0, nl).replace(/\\r$/, '');\r\n buf = buf.slice(nl + 1);\r\n if (line.startsWith('data:')) yield line.slice(5).trimStart();\r\n }\r\n }\r\n if (buf.startsWith('data:')) yield buf.slice(5).trimStart();\r\n } finally {\r\n // A consumer that leaves the loop early must close the connection, or the server keeps generating (and billing).\r\n await reader.cancel().catch(() => {});\r\n }\r\n}\r\n","import { errorMessage, type HttpClient } from './client';\r\nimport { sseData } from './sse';\r\nimport type { AiContentPart, AiProviderId, AiToolCall } from './ai';\r\nimport type { ApiResult } from '../types';\r\n\r\n// ── Memory ────────────────────────────────────────────────────────────────\r\n\r\nexport interface AiMemory {\r\n id: string;\r\n subject_type: string;\r\n subject_id: string;\r\n kind: string;\r\n content: string;\r\n embed_model: string | null;\r\n importance: number;\r\n metadata: Record<string, unknown>;\r\n source_thread_id: string | null;\r\n expires_at: string | null;\r\n created_at: string;\r\n updated_at: string;\r\n}\r\n\r\nexport interface AiMemoryMatch extends AiMemory {\r\n /** Cosine similarity (null in a keyword fallback). */\r\n similarity: number | null;\r\n /** Similarity nudged by importance and recency; results are sorted by it. */\r\n score: number;\r\n}\r\n\r\nexport interface MemoryAddOptions {\r\n /** Service key only: `type:id`, e.g. `user:<id>`, `agent:support`. Signed-in users are always `user:<their id>`. */\r\n subject?: string;\r\n /** fact (default), preference, episode, summary, or your own word. */\r\n kind?: string;\r\n /** 1–5, default 3. */\r\n importance?: number;\r\n /** ISO date; the memory is deleted after it. */\r\n expiresAt?: string | null;\r\n metadata?: Record<string, unknown>;\r\n sourceThreadId?: string;\r\n /** Update a near-identical memory (cosine ≥ 0.92) instead of adding one. Default true. */\r\n dedupe?: boolean;\r\n}\r\n\r\n/**\r\n * Memory API: facts about a user (or an agent, an org…) with semantic search.\r\n * A signed-in user reads and writes only their own memory; the service key may\r\n * name any subject.\r\n *\r\n * ```ts\r\n * await client.memory.add('Prefers vegetarian recipes', { kind: 'preference' });\r\n * const { data } = await client.memory.search('what should I cook?');\r\n * ```\r\n */\r\nexport class MemoryClient {\r\n constructor(private client: HttpClient) {}\r\n\r\n private path(p = ''): string {\r\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/memory${p}`;\r\n }\r\n\r\n /** Remember something. `deduped: true` means an existing near-identical memory was updated. */\r\n add(content: string, options: MemoryAddOptions = {}): Promise<ApiResult<{ memory: AiMemory; deduped: boolean }>> {\r\n return this.client.request(this.path(), { method: 'POST', body: { content, ...options } });\r\n }\r\n\r\n /** Most relevant memories. `degraded: true` = the model provider was down and keyword search answered. */\r\n search(query: string, options: { subject?: string; k?: number; kind?: string; minSimilarity?: number } = {}): Promise<ApiResult<{ results: AiMemoryMatch[]; degraded: boolean; model: string | null }>> {\r\n return this.client.request(this.path('/search'), { method: 'POST', body: { query, ...options } });\r\n }\r\n\r\n list(options: { subject?: string; kind?: string; limit?: number; offset?: number } = {}): Promise<ApiResult<{ memories: AiMemory[]; total: number }>> {\r\n return this.client.request(this.path(query(options)));\r\n }\r\n\r\n get(id: string): Promise<ApiResult<AiMemory>> {\r\n return this.client.request(this.path(`/${encodeURIComponent(id)}`));\r\n }\r\n\r\n update(id: string, patch: { content?: string; kind?: string; importance?: number; expiresAt?: string | null; metadata?: Record<string, unknown> }): Promise<ApiResult<AiMemory>> {\r\n return this.client.request(this.path(`/${encodeURIComponent(id)}`), { method: 'PATCH', body: patch });\r\n }\r\n\r\n remove(id: string): Promise<ApiResult<{ deleted: boolean }>> {\r\n return this.client.request(this.path(`/${encodeURIComponent(id)}`), { method: 'DELETE' });\r\n }\r\n\r\n /** Delete every memory of a subject (rows and vectors). Users always forget themselves. */\r\n forget(subject?: string): Promise<ApiResult<{ deleted: number }>> {\r\n return this.client.request(this.path('/forget'), { method: 'POST', body: subject ? { subject } : {} });\r\n }\r\n\r\n /** Service key: subjects that have memories. */\r\n subjects(options: { type?: string; limit?: number } = {}): Promise<ApiResult<Array<{ subject: string; subject_type: string; subject_id: string; memories: number; last_updated: string }>>> {\r\n return this.client.request(this.path(`/subjects${query(options)}`));\r\n }\r\n\r\n /** Service key: re-embed memories made with another model (after a provider change), 64 per call. */\r\n reembed(options: { subject?: string; limit?: number } = {}): Promise<ApiResult<{ reembedded: number; remaining: number; model: string | null }>> {\r\n return this.client.request(this.path('/reembed'), { method: 'POST', body: options });\r\n }\r\n}\r\n\r\n// ── Collections ───────────────────────────────────────────────────────────\r\n\r\nexport interface AiCollection {\r\n id: string;\r\n name: string;\r\n embed_model: string | null;\r\n embed_provider: AiProviderId | null;\r\n chunk_tokens: number;\r\n chunk_overlap: number;\r\n user_ingest: boolean;\r\n metadata: Record<string, unknown>;\r\n created_at: string;\r\n documents?: number;\r\n pending?: number;\r\n}\r\n\r\nexport interface AiDocument {\r\n id: string;\r\n collection_id: string;\r\n owner_user_id: string | null;\r\n source_type: 'text' | 'storage' | 'url';\r\n source_ref: string | null;\r\n title: string | null;\r\n metadata: Record<string, unknown>;\r\n status: 'queued' | 'processing' | 'ready' | 'failed';\r\n error: string | null;\r\n chunk_count: number;\r\n bytes: number;\r\n created_at: string;\r\n updated_at: string;\r\n}\r\n\r\nexport interface AiSearchResult {\r\n /** Reciprocal-rank-fusion score (vector + keyword); results are sorted by it. */\r\n score: number;\r\n similarity: number | null;\r\n keywordMatch: boolean;\r\n /** The best-matching words, wrapped in `**`. */\r\n highlight: string;\r\n chunk: { id: string; ordinal: number; content: string; metadata: Record<string, unknown> };\r\n document: { id: string; title: string | null; sourceType: string; sourceRef: string | null; metadata: Record<string, unknown> };\r\n}\r\n\r\nexport type DocumentSource = { text: string } | { url: string } | { storage: { bucket: string; path: string } };\r\n\r\nexport interface IngestOptions {\r\n title?: string;\r\n metadata?: Record<string, unknown>;\r\n /** Service key only: the user who owns (and alone can read) this document. */\r\n ownerUserId?: string;\r\n /** Wait until the document is `ready` or `failed` (default: return at once with `queued`). */\r\n wait?: boolean | { timeoutMs?: number; intervalMs?: number };\r\n}\r\n\r\n/** One collection: `client.collections.from('handbook')`. */\r\nexport class CollectionRef {\r\n constructor(private client: HttpClient, readonly name: string) {}\r\n\r\n private path(p = ''): string {\r\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/collections/${encodeURIComponent(this.name)}${p}`;\r\n }\r\n\r\n /**\r\n * Add a document; it is chunked and embedded in the background. PDF, HTML and\r\n * text files are read from storage or a URL.\r\n */\r\n async ingest(source: DocumentSource, options: IngestOptions = {}): Promise<ApiResult<AiDocument>> {\r\n const { wait, ...rest } = options;\r\n const res = await this.client.request<AiDocument>(this.path('/documents'), { method: 'POST', body: { ...source, ...rest } });\r\n if (res.error || !wait) return res;\r\n return this.waitFor(res.data.id, typeof wait === 'object' ? wait : {});\r\n }\r\n\r\n /** Poll a document until it is `ready` or `failed`. */\r\n async waitFor(documentId: string, { timeoutMs = 120_000, intervalMs = 1_000 }: { timeoutMs?: number; intervalMs?: number } = {}): Promise<ApiResult<AiDocument>> {\r\n const until = Date.now() + timeoutMs;\r\n for (;;) {\r\n const res = await this.getDocument(documentId);\r\n if (res.error || res.data.status === 'ready' || res.data.status === 'failed') return res;\r\n if (Date.now() > until) return { data: null, error: { message: `Document ${documentId} is still ${res.data.status}`, code: 'TIMEOUT' } };\r\n await new Promise((r) => setTimeout(r, intervalMs));\r\n }\r\n }\r\n\r\n /** Hybrid search (vector + keyword). `hybrid: false` is vector only; `filter` matches document metadata. */\r\n search(query: string, options: { k?: number; hybrid?: boolean; filter?: Record<string, unknown>; minSimilarity?: number } = {}): Promise<ApiResult<{ results: AiSearchResult[]; model: string | null }>> {\r\n return this.client.request(this.path('/search'), { method: 'POST', body: { query, ...options } });\r\n }\r\n\r\n listDocuments(options: { status?: AiDocument['status']; limit?: number; offset?: number } = {}): Promise<ApiResult<{ documents: AiDocument[]; total: number }>> {\r\n return this.client.request(this.path(`/documents${query(options)}`));\r\n }\r\n\r\n getDocument(id: string): Promise<ApiResult<AiDocument>> {\r\n return this.client.request(this.path(`/documents/${encodeURIComponent(id)}`));\r\n }\r\n\r\n /** Delete a document and its chunks. */\r\n remove(documentId: string): Promise<ApiResult<{ deleted: boolean }>> {\r\n return this.client.request(this.path(`/documents/${encodeURIComponent(documentId)}`), { method: 'DELETE' });\r\n }\r\n\r\n stats(): Promise<ApiResult<{ chunks: number; current_model_chunks: number; documents: number; failed: number; model: string | null }>> {\r\n return this.client.request(this.path('/stats'));\r\n }\r\n}\r\n\r\n/**\r\n * Vector collections: documents chunked, embedded and searched with hybrid\r\n * retrieval under RLS. Creating collections and adding shared documents needs\r\n * the service key; a collection with `userIngest` lets signed-in users add\r\n * documents only they can read.\r\n *\r\n * ```ts\r\n * await admin.collections.create({ name: 'handbook' });\r\n * await admin.collections.from('handbook').ingest({ url: 'https://example.com/guide.pdf' }, { wait: true });\r\n * const { data } = await client.collections.from('handbook').search('How do refunds work?');\r\n * ```\r\n */\r\nexport class CollectionsClient {\r\n constructor(private client: HttpClient) {}\r\n\r\n private path(p = ''): string {\r\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/collections${p}`;\r\n }\r\n\r\n from(name: string): CollectionRef {\r\n return new CollectionRef(this.client, name);\r\n }\r\n\r\n create(options: { name: string; model?: string; provider?: AiProviderId; chunkTokens?: number; chunkOverlap?: number; userIngest?: boolean; metadata?: Record<string, unknown> }): Promise<ApiResult<AiCollection>> {\r\n return this.client.request(this.path(), { method: 'POST', body: options });\r\n }\r\n\r\n list(): Promise<ApiResult<AiCollection[]>> {\r\n return this.client.request(this.path());\r\n }\r\n\r\n get(name: string): Promise<ApiResult<AiCollection>> {\r\n return this.client.request(this.path(`/${encodeURIComponent(name)}`));\r\n }\r\n\r\n /** Changing `model` re-embeds every chunk in the background. */\r\n update(name: string, patch: { userIngest?: boolean; metadata?: Record<string, unknown>; model?: string; provider?: AiProviderId }): Promise<ApiResult<AiCollection>> {\r\n return this.client.request(this.path(`/${encodeURIComponent(name)}`), { method: 'PATCH', body: patch });\r\n }\r\n\r\n delete(name: string): Promise<ApiResult<{ deleted: boolean }>> {\r\n return this.client.request(this.path(`/${encodeURIComponent(name)}`), { method: 'DELETE' });\r\n }\r\n}\r\n\r\n// ── Threads ───────────────────────────────────────────────────────────────\r\n\r\nexport interface AiThread {\r\n id: string;\r\n owner_user_id: string | null;\r\n title: string | null;\r\n metadata: Record<string, unknown>;\r\n created_at: string;\r\n updated_at: string;\r\n messages?: number;\r\n cost_usd?: number;\r\n}\r\n\r\nexport type AiThreadPart = AiContentPart | { type: 'image'; storage: { bucket: string; path: string } };\r\n\r\nexport interface AiThreadMessage {\r\n id: string;\r\n thread_id: string;\r\n role: 'system' | 'user' | 'assistant' | 'tool';\r\n content: AiThreadPart[];\r\n model: string | null;\r\n provider: string | null;\r\n tokens_in: number | null;\r\n tokens_out: number | null;\r\n cost_usd: number | null;\r\n /** `streaming` while a run writes it (partial text), `error` if the run failed. */\r\n status: 'streaming' | 'complete' | 'error';\r\n error: string | null;\r\n metadata: Record<string, unknown>;\r\n created_at: string;\r\n}\r\n\r\nexport interface AiCitation {\r\n index: number;\r\n collection: string;\r\n documentId: string;\r\n chunkId: string;\r\n title: string | null;\r\n sourceRef: string | null;\r\n snippet: string;\r\n score: number;\r\n}\r\n\r\nexport interface ThreadRunOptions {\r\n /** Appended as a user message first. */\r\n input?: string | AiThreadPart[];\r\n model?: string;\r\n provider?: AiProviderId;\r\n system?: string;\r\n temperature?: number;\r\n maxTokens?: number;\r\n /** true = the user's memories as context; with the service key a `type:id` subject. */\r\n memory?: boolean | string;\r\n /** Collections searched for sources the model cites as [n]. */\r\n collections?: string[];\r\n /** Built-in tools the model may call. */\r\n tools?: Array<'memory_search' | 'collections_search'>;\r\n /** History + context budget in tokens (default 8000). */\r\n maxContextTokens?: number;\r\n}\r\n\r\nexport type ThreadRunEvent =\r\n | { type: 'message'; threadId: string; messageId: string }\r\n | ({ type: 'citation' } & AiCitation)\r\n | { type: 'delta'; text: string }\r\n | ({ type: 'tool_call' } & AiToolCall)\r\n | { type: 'tool_result'; id: string; name: string; result: unknown }\r\n | { type: 'done'; messageId: string; model: string; provider: string | null; tokensIn: number; tokensOut: number; costUsd: number }\r\n | { type: 'error'; code?: string; message: string; status?: number };\r\n\r\nexport interface ThreadRunResult {\r\n messageId: string;\r\n text: string;\r\n citations: AiCitation[];\r\n toolCalls: Array<{ id: string; name: string; result: unknown }>;\r\n usage: { model: string; provider: string | null; tokensIn: number; tokensOut: number; costUsd: number } | null;\r\n}\r\n\r\n/** One thread: `client.ai.threads.from(id)`. */\r\nexport class ThreadRef {\r\n constructor(private client: HttpClient, readonly id: string) {}\r\n\r\n private path(p = ''): string {\r\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/threads/${encodeURIComponent(this.id)}${p}`;\r\n }\r\n\r\n get(): Promise<ApiResult<AiThread>> {\r\n return this.client.request(this.path());\r\n }\r\n\r\n update(patch: { title?: string | null; metadata?: Record<string, unknown> }): Promise<ApiResult<AiThread>> {\r\n return this.client.request(this.path(), { method: 'PATCH', body: patch });\r\n }\r\n\r\n delete(): Promise<ApiResult<{ deleted: boolean }>> {\r\n return this.client.request(this.path(), { method: 'DELETE' });\r\n }\r\n\r\n /** Oldest first: the newest `limit`, or those created after `after` (resume after a reconnect). */\r\n messages(options: { limit?: number; after?: string } = {}): Promise<ApiResult<AiThreadMessage[]>> {\r\n return this.client.request(this.path(`/messages${query(options)}`));\r\n }\r\n\r\n /** Add a message without running the model (users may add `user` messages only). */\r\n append(content: string | AiThreadPart[], options: { role?: 'user' | 'assistant' | 'system'; metadata?: Record<string, unknown> } = {}): Promise<ApiResult<AiThreadMessage>> {\r\n return this.client.request(this.path('/messages'), { method: 'POST', body: { content, ...options } });\r\n }\r\n\r\n /**\r\n * Stream the assistant's next message: `message` (its id), `citation`s,\r\n * `delta`s, tool events, then `done` (or `error`). The reply is saved as it\r\n * streams, so another device can read it with `messages()` or listen on the\r\n * realtime channel `broadcast:ai:thread:<id>`. Leaving the loop early cancels.\r\n */\r\n async *run(options: ThreadRunOptions = {}, { signal }: { signal?: AbortSignal } = {}): AsyncGenerator<ThreadRunEvent> {\r\n let res: Response;\r\n try {\r\n res = await this.client.authFetch(`${this.client.getBaseUrl()}${this.path('/run')}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', Accept: 'text/event-stream' },\r\n body: JSON.stringify({ ...options, stream: true }),\r\n signal,\r\n redirect: 'error',\r\n });\r\n } catch (err) {\r\n yield { type: 'error', code: 'NETWORK_ERROR', message: err instanceof Error ? err.message : 'Network error' };\r\n return;\r\n }\r\n if (!res.ok || !res.body) {\r\n const data = await res.json().catch(() => null);\r\n yield { type: 'error', code: data?.code, message: errorMessage(data, res.status), status: res.status };\r\n return;\r\n }\r\n try {\r\n for await (const raw of sseData(res.body)) yield JSON.parse(raw) as ThreadRunEvent;\r\n } catch (err) {\r\n yield { type: 'error', code: 'STREAM_ERROR', message: err instanceof Error ? err.message : 'Stream interrupted' };\r\n }\r\n }\r\n\r\n /** Run and wait for the finished message (no streaming). */\r\n runOnce(options: ThreadRunOptions = {}): Promise<ApiResult<ThreadRunResult>> {\r\n return this.client.request(this.path('/run'), { method: 'POST', body: { ...options, stream: false }, timeout: 180_000 });\r\n }\r\n}\r\n\r\n/**\r\n * Threads: persistent conversations with per-message tokens and cost. A\r\n * signed-in user owns the threads they create; the service key can create\r\n * threads for any user (`ownerUserId`).\r\n *\r\n * ```ts\r\n * const { data: t } = await client.ai.threads.create({ title: 'Trip ideas' });\r\n * for await (const ev of client.ai.threads.from(t.id).run({ input: 'Plan a weekend in Oslo', memory: true })) {\r\n * if (ev.type === 'delta') process.stdout.write(ev.text);\r\n * }\r\n * ```\r\n */\r\nexport class ThreadsClient {\r\n constructor(private client: HttpClient) {}\r\n\r\n private path(p = ''): string {\r\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/threads${p}`;\r\n }\r\n\r\n from(id: string): ThreadRef {\r\n return new ThreadRef(this.client, id);\r\n }\r\n\r\n create(options: { title?: string; metadata?: Record<string, unknown>; ownerUserId?: string } = {}): Promise<ApiResult<AiThread>> {\r\n return this.client.request(this.path(), { method: 'POST', body: options });\r\n }\r\n\r\n list(options: { limit?: number; offset?: number; ownerUserId?: string } = {}): Promise<ApiResult<{ threads: AiThread[]; total: number }>> {\r\n return this.client.request(this.path(query(options)));\r\n }\r\n\r\n get(id: string): Promise<ApiResult<AiThread>> {\r\n return this.from(id).get();\r\n }\r\n\r\n delete(id: string): Promise<ApiResult<{ deleted: boolean }>> {\r\n return this.from(id).delete();\r\n }\r\n}\r\n\r\nexport interface AiTrace {\r\n id: string;\r\n request_id: string | null;\r\n thread_id: string | null;\r\n caller: string;\r\n kind: 'chat' | 'embed';\r\n end_user_id: string | null;\r\n provider: string;\r\n model: string;\r\n key_source: 'byok' | 'platform';\r\n prompt_hash: string | null;\r\n tokens_in: number;\r\n tokens_out: number;\r\n cost_usd: number | null;\r\n latency_ms: number;\r\n status: 'ok' | 'error';\r\n error: string | null;\r\n created_at: string;\r\n}\r\n\r\nfunction query(o: Record<string, unknown>): string {\r\n const q = Object.entries(o).filter(([, v]) => v !== undefined && v !== null).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);\r\n return q.length ? `?${q.join('&')}` : '';\r\n}\r\n","import { errorMessage, type HttpClient } from './client';\r\nimport { sseData } from './sse';\r\nimport { ThreadsClient, type AiTrace } from './ai-data';\r\nimport type { ApiResult } from '../types';\r\n\r\nexport type AiProviderId = 'gemini' | 'openai' | 'anthropic' | 'openai_compatible';\r\n\r\n/** `url`: a `data:image/png;base64,…` URL, or an https URL (OpenAI and Anthropic only). */\r\nexport type AiContentPart = { type: 'text'; text: string } | { type: 'image'; url: string };\r\n\r\nexport interface AiToolCall {\r\n id: string;\r\n name: string;\r\n arguments: Record<string, unknown>;\r\n /** Provider state; send the call back unchanged in the assistant message. */\r\n signature?: string;\r\n}\r\n\r\nexport interface AiTool {\r\n name: string;\r\n description?: string;\r\n /** JSON Schema of the arguments object. */\r\n parameters?: Record<string, unknown>;\r\n}\r\n\r\nexport interface AiMessage {\r\n role: 'system' | 'user' | 'assistant' | 'tool';\r\n content: string | AiContentPart[] | null;\r\n /** assistant: the calls from a previous reply (`toolCalls`). */\r\n toolCalls?: AiToolCall[];\r\n /** tool: the id of the call this result answers. */\r\n toolCallId?: string;\r\n}\r\n\r\nexport interface AiChatOptions {\r\n /** Defaults to the project's default model (Console → Project Settings → AI). */\r\n model?: string;\r\n /** Needed only when the model name does not identify the provider. */\r\n provider?: AiProviderId;\r\n messages: AiMessage[];\r\n temperature?: number;\r\n maxTokens?: number;\r\n tools?: AiTool[];\r\n toolChoice?: 'auto' | 'none' | 'required' | { name: string };\r\n}\r\n\r\nexport interface AiChatResult {\r\n text: string;\r\n /** Calls the model wants made; answer each with a `tool` message and chat again. */\r\n toolCalls: AiToolCall[];\r\n model: string;\r\n provider: AiProviderId;\r\n tokensIn: number;\r\n tokensOut: number;\r\n /** List price; models without a listed price are charged at their provider's top price. */\r\n costUsd: number;\r\n finishReason: string | null;\r\n}\r\n\r\nexport type AiStreamEvent =\r\n | { type: 'delta'; text: string }\r\n | ({ type: 'tool_call' } & AiToolCall)\r\n | { type: 'done'; model: string; tokensIn: number; tokensOut: number; costUsd: number; finishReason: string | null }\r\n | { type: 'error'; code?: string; message: string; status?: number };\r\n\r\nexport interface AiEmbedResult {\r\n model: string;\r\n provider: AiProviderId;\r\n dimensions: number;\r\n embeddings: number[][];\r\n tokensIn: number;\r\n}\r\n\r\nexport interface AiModel {\r\n id: string;\r\n provider: AiProviderId;\r\n kind: 'chat' | 'embed';\r\n default?: boolean;\r\n}\r\n\r\n/**\r\n * AI Gateway: chat and embeddings through the project's own provider keys.\r\n * Calls need a signed-in user (per-user caps apply) or a service-role key.\r\n *\r\n * ```ts\r\n * const { data } = await client.ai.chat({ messages: [{ role: 'user', content: 'Hi' }] });\r\n * for await (const ev of client.ai.chatStream({ messages })) {\r\n * if (ev.type === 'delta') process.stdout.write(ev.text);\r\n * }\r\n * ```\r\n */\r\nexport class AiClient {\r\n /** Persistent conversations with streamed, saved replies (`threads.from(id).run()`). */\r\n readonly threads: ThreadsClient;\r\n\r\n constructor(private client: HttpClient) {\r\n this.threads = new ThreadsClient(client);\r\n }\r\n\r\n private path(p: string): string {\r\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/${p}`;\r\n }\r\n\r\n private body(o: AiChatOptions, stream: boolean) {\r\n return {\r\n model: o.model,\r\n provider: o.provider,\r\n messages: o.messages.map((m) => ({\r\n role: m.role,\r\n content: m.content,\r\n ...(m.toolCalls ? { tool_calls: m.toolCalls } : {}),\r\n ...(m.toolCallId ? { tool_call_id: m.toolCallId } : {}),\r\n })),\r\n temperature: o.temperature,\r\n max_tokens: o.maxTokens,\r\n tools: o.tools,\r\n tool_choice: o.toolChoice,\r\n stream,\r\n };\r\n }\r\n\r\n async chat(options: AiChatOptions): Promise<ApiResult<AiChatResult>> {\r\n // A model reply can take longer than the default request timeout.\r\n return this.client.request<AiChatResult>(this.path('chat'), { method: 'POST', body: this.body(options, false), timeout: 120_000 });\r\n }\r\n\r\n /**\r\n * Stream a reply: `delta` and `tool_call` events, then one `done` (or `error`).\r\n * Errors arrive as an `error` event, never as a throw. `signal`, or leaving\r\n * the loop early, cancels the stream.\r\n */\r\n async *chatStream(options: AiChatOptions, { signal }: { signal?: AbortSignal } = {}): AsyncGenerator<AiStreamEvent> {\r\n let res: Response;\r\n try {\r\n res = await this.client.authFetch(`${this.client.getBaseUrl()}${this.path('chat')}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', Accept: 'text/event-stream' },\r\n body: JSON.stringify(this.body(options, true)),\r\n signal,\r\n redirect: 'error',\r\n });\r\n } catch (err) {\r\n yield { type: 'error', code: 'NETWORK_ERROR', message: err instanceof Error ? err.message : 'Network error' };\r\n return;\r\n }\r\n if (!res.ok || !res.body) {\r\n const data = await res.json().catch(() => null);\r\n yield { type: 'error', code: data?.code, message: errorMessage(data, res.status), status: res.status };\r\n return;\r\n }\r\n try {\r\n for await (const raw of sseData(res.body)) yield JSON.parse(raw) as AiStreamEvent;\r\n } catch (err) {\r\n yield { type: 'error', code: 'STREAM_ERROR', message: err instanceof Error ? err.message : 'Stream interrupted' };\r\n }\r\n }\r\n\r\n /** 768-dimension vectors for one string or up to 100. */\r\n async embed(input: string | string[], options: { model?: string; provider?: AiProviderId } = {}): Promise<ApiResult<AiEmbedResult>> {\r\n return this.client.request<AiEmbedResult>(this.path('embeddings'), { method: 'POST', body: { input, ...options } });\r\n }\r\n\r\n /** Models the project can call with its saved provider keys. */\r\n async models(): Promise<ApiResult<AiModel[]>> {\r\n return this.client.request<AiModel[]>(this.path('models'));\r\n }\r\n\r\n /** Service key: per-call traces (model, tokens, latency, cost, status), newest first. */\r\n async traces(options: { limit?: number; offset?: number; threadId?: string; status?: 'ok' | 'error' } = {}): Promise<ApiResult<{ traces: AiTrace[]; total: number; retentionDays: number }>> {\r\n const q = Object.entries(options).filter(([, v]) => v !== undefined).map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join('&');\r\n return this.client.request(this.path(`traces${q ? `?${q}` : ''}`));\r\n }\r\n}\r\n","import type { DatabaseClient } from './database';\n\n/** Postgres type → TypeScript type. Unknown types fall back to `unknown`. */\nconst PG_TO_TS: Record<string, string> = {\n int2: 'number', int4: 'number', int8: 'number',\n smallint: 'number', integer: 'number', bigint: 'number', serial: 'number', bigserial: 'number',\n numeric: 'number', decimal: 'number', real: 'number', float4: 'number', float8: 'number',\n 'double precision': 'number',\n bool: 'boolean', boolean: 'boolean',\n text: 'string', varchar: 'string', 'character varying': 'string',\n char: 'string', character: 'string', bpchar: 'string', citext: 'string', uuid: 'string',\n name: 'string',\n date: 'string', timestamp: 'string', timestamptz: 'string',\n 'timestamp with time zone': 'string', 'timestamp without time zone': 'string',\n time: 'string', timetz: 'string', interval: 'string',\n 'time without time zone': 'string', 'time with time zone': 'string',\n json: 'Record<string, unknown>', jsonb: 'Record<string, unknown>',\n bytea: 'string',\n // pgvector columns arrive in their text form, '[0.1,0.2,…]' (node-postgres has no vector parser).\n vector: 'string',\n};\n\nfunction tsType(pgType: string): string {\n const t = (pgType || '').toLowerCase().trim();\n if (!t) return 'unknown';\n if (t.endsWith('[]')) return `${tsType(t.slice(0, -2))}[]`;\n if (t.startsWith('_')) return `${tsType(t.slice(1))}[]`; // pg internal array name, e.g. _text\n return PG_TO_TS[t] ?? 'unknown';\n}\n\nfunction pascalCase(name: string): string {\n return name\n .replace(/(^|[_\\- ])([a-zA-Z0-9])/g, (_m, _s, c) => c.toUpperCase())\n .replace(/[^a-zA-Z0-9]/g, '');\n}\n\nfunction fieldKey(name: string): string {\n return /^[a-zA-Z_$][\\w$]*$/.test(name) ? name : JSON.stringify(name);\n}\n\nexport interface GenerateTypesOptions {\n /** Only include these tables (default: all non-internal). */\n tables?: string[];\n /** Tables to skip (default: `auth_*` and `_*` internal tables). */\n exclude?: RegExp;\n}\n\n\n/**\n * Introspect the project schema and emit TypeScript row interfaces plus a\n * `Database` map. Pair the output with `db.query<MyRow>(...)` or the query\n * builder for end-to-end type safety — NestCore's answer to\n * `supabase gen types typescript`.\n *\n * ```ts\n * import { writeFileSync } from 'fs';\n * writeFileSync('nestcore.types.ts', await generateTypes(client.db));\n * ```\n */\nexport async function generateTypes(db: DatabaseClient, opts: GenerateTypesOptions = {}): Promise<string> {\n const listed = await db.listTables();\n if (listed.error) throw new Error(`generateTypes: could not list tables: ${listed.error.message}`);\n\n const raw = listed.data as unknown;\n let tables: string[] = Array.isArray(raw)\n ? (raw as unknown[]).map((t) => (typeof t === 'string' ? t : (t as { name?: string })?.name)).filter((t): t is string => !!t)\n : ((raw as { tables?: string[] })?.tables ?? []);\n\n const exclude = opts.exclude ?? /^(auth_|_)/;\n tables = opts.tables ? tables.filter((t) => opts.tables!.includes(t)) : tables.filter((t) => !exclude.test(t));\n tables = Array.from(new Set(tables)).sort();\n\n const blocks: string[] = [];\n const dbEntries: string[] = [];\n\n for (const table of tables) {\n const meta = await db.getTableMetadata(table);\n if (meta.error) continue;\n const cols = meta.data.find((t) => t.table_name === table)?.columns ?? [];\n if (cols.length === 0) continue;\n\n const fields = cols.map((c) => {\n const opt = c.is_nullable ? '?' : '';\n const nullable = c.is_nullable ? ' | null' : '';\n return ` ${fieldKey(c.column_name)}${opt}: ${tsType(c.data_type)}${nullable};`;\n });\n const iface = pascalCase(table);\n blocks.push(`export interface ${iface} {\\n${fields.join('\\n')}\\n}`);\n dbEntries.push(` ${JSON.stringify(table)}: ${iface};`);\n }\n\n const header =\n '// Generated by @nestcorehq/node generateTypes — do not edit by hand.\\n' +\n '// Re-run after schema changes to keep types in sync.\\n';\n const dbMap = `export interface Database {\\n${dbEntries.join('\\n')}\\n}`;\n return `${header}\\n${blocks.join('\\n\\n')}\\n\\n${dbMap}\\n`;\n}\n\n// Exported for testing / advanced callers.\nexport const __typegenInternals = { tsType, pascalCase };\n","import type { ApiResult } from '../types';\n\n/**\n * Error thrown by {@link unwrap}. Carries the API error's `code` and HTTP\n * `status` so callers can branch on them — the throwing counterpart to the\n * `{ data, error }` result model (and a match for the Flutter SDK's\n * `NestCoreException`).\n */\nexport class NestCoreError extends Error {\n readonly code?: string;\n readonly status?: number;\n constructor(message: string, opts: { code?: string; status?: number } = {}) {\n super(message);\n this.name = 'NestCoreError';\n this.code = opts.code;\n this.status = opts.status;\n }\n}\n\n/** @deprecated Renamed to {@link NestCoreError}; kept so existing `instanceof` checks keep working. */\nexport const OrbitNestError = NestCoreError;\n/** @deprecated Renamed to {@link NestCoreError}. */\nexport type OrbitNestError = NestCoreError;\n\n/**\n * Bridge the `{ data, error }` result model to a throwing style: returns\n * `data` on success, throws {@link NestCoreError} on error. Lets you opt into\n * `try/catch` ergonomics (as the Flutter SDK uses) without changing the default\n * non-throwing behavior of the SDK.\n *\n * ```ts\n * import { unwrap } from '@nestcorehq/js';\n *\n * try {\n * const { rows } = unwrap(await nestcore.db.from('posts').select());\n * } catch (e) {\n * if (e instanceof NestCoreError) console.error(e.code, e.status, e.message);\n * }\n * ```\n */\nexport function unwrap<T>(result: ApiResult<T>): T {\n if (result.error) {\n throw new NestCoreError(result.error.message, {\n code: result.error.code,\n status: result.error.status,\n });\n }\n return result.data as T;\n}\n\n/**\n * `error.code` values for the project auth limits (all HTTP 429). The limits\n * are set per project in Console → Settings → Auth Security.\n */\nexport const AuthLimitCodes = {\n /** Too many wrong codes: codes are invalidated for a while. */\n OTP_RATE_LIMITED: 'OTP_RATE_LIMITED',\n /** Too many codes requested for one email in the last hour. */\n OTP_REQUEST_LIMIT: 'OTP_REQUEST_LIMIT',\n /** Too many wrong passwords: the email is locked for the lockout duration. */\n ACCOUNT_LOCKED: 'ACCOUNT_LOCKED',\n} as const;\n"],"mappings":"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","names":["index_exports","__export","AiClient","AnalyticsClient","AuthClient","AuthLimitCodes","CollectionRef","CollectionsClient","DatabaseClient","EnvironmentClient","FunctionsClient","HttpClient","JobsClient","LoggingClient","MemoryClient","NestCoreError","OrbitNestError","PresenceManager","RealtimeChannel","RealtimeClient","StorageBucket","StorageClient","ThreadRef","ThreadsClient","createClient","createLocalStorageAdapter","createMemoryAdapter","generateTypes","isMfaChallenge","localStorageAdapter","memoryAdapter","sanitizePath","unwrap","validatePath","validateRequired","__toCommonJS","HttpClient","config","fn","refresh","url","init","asUser","token","send","t","res","fresh","path","options","extraHeaders","accessToken","controller","timeoutId","response","data","errorMessage","err","body","status","fallback","b","m","nested","adminOnlyError","feature","DatabaseClient","client","table","column","query","opts","sql","params","result","col","tableName","options","data","rowId","rows","updates","conditions","policy","p","r","policyName","TableQueryBuilder","SAFE_IDENT","db","name","value","pattern","values","config","vector","n","op","dir","from","to","size","c","where","f","vals","v","cfg","fn","orderClauses","columns","res","row","filters","id","FunctionsClient","client","functionName","options","url","method","controller","timeoutId","response","contentType","data","headers","value","key","errorMessage","err","JobsClient","client","options","adminOnlyError","name","createMemoryAdapter","store","k","v","createLocalStorageAdapter","ls","probe","storage","memoryAdapter","localStorageAdapter","usableLocalStorage","isMfaChallenge","value","SESSION_KEY","LEGACY_SESSION_KEY","REFRESH_MARGIN_S","NO_SESSION","AuthClient","client","options","memoryAdapter","raw","legacyKey","withExpiry","cb","callback","s","staleToken","current","fn","token","credentials","result","challengeToken","code","phone","refreshToken","metadata","rest","user","session","opts","input","id","deviceName","friendlyName","factorId","LoggingClient","client","options","adminOnlyError","functionName","EnvironmentClient","client","adminOnlyError","name","value","options","variables","validateRequired","value","paramName","validatePath","path","required","segments","seg","sanitizePath","encodeStoragePath","path","seg","buildTransformQuery","t","p","MIME_BY_EXT","guessContentType","ext","unwrapEnvelope","result","body","StorageClient","client","bucket","StorageBucket","file","options","invalid","validatePath","formData","type","blob","url","controller","timeoutId","response","data","errorMessage","err","paths","prefix","params","query","baseUrl","projectSlug","publicUrl","qs","PresenceManager","channelName","client","state","cb","newState","prev","key","presences","RealtimeChannel","name","type","opts","event","payload","ws","schema","table","splitTableKey","keys","h","o","status","err","msg","events","normaliseEvents","matchesFilter","CONNECTING","OPEN","BASE_RECONNECT_DELAY_MS","RATE_LIMITED_RECONNECT_DELAY_MS","MAX_RECONNECT_DELAY_MS","WS_CLOSE_RATE_LIMITED","RealtimeClient","url","options","token","userJwt","resolve","reject","WS","protocols","handshakeOptions","ch","ev","code","jitter","delay","c","sameFilter","tableKeys","unsubscribe","channel","list","dot","row","filter","col","v","x","re","ANON_STORAGE_KEY","SESSION_ID_KEY","SESSION_TS_KEY","LEGACY_KEYS","SESSION_TIMEOUT_MS","generateUuid","bytes","hex","b","c","r","getLocalStorage","probe","readKey","store","key","value","legacy","old","detectPlatform","loadOrCreateAnonymousId","id","resolveSessionId","fallback","now","last","sid","AnalyticsClient","client","options","userId","traits","event","properties","screenName","error","msg","stack","keepalive","batch","result","status","entry","sseData","body","reader","decoder","buf","value","done","nl","line","MemoryClient","client","p","content","options","query","id","patch","subject","CollectionRef","name","source","wait","rest","res","documentId","timeoutMs","intervalMs","until","r","CollectionsClient","ThreadRef","signal","err","data","errorMessage","raw","sseData","ThreadsClient","o","q","v","k","AiClient","client","ThreadsClient","p","o","stream","m","options","signal","res","err","data","errorMessage","raw","sseData","input","q","v","k","PG_TO_TS","tsType","pgType","t","pascalCase","name","_m","_s","c","fieldKey","generateTypes","db","opts","listed","raw","tables","exclude","blocks","dbEntries","table","meta","cols","fields","opt","nullable","iface","header","dbMap","NestCoreError","message","opts","OrbitNestError","unwrap","result","AuthLimitCodes","DEFAULT_BASE_URL","decodeProjectSlugFromJwt","token","parts","payload","json","decoded","slug","createClient","config","projectSlug","baseUrl","httpClient","HttpClient","wsUrl","auth","AuthClient","stale","followAuth","realtimeClient","RealtimeClient","s","DatabaseClient","FunctionsClient","JobsClient","LoggingClient","EnvironmentClient","StorageClient","name","AnalyticsClient","AiClient","MemoryClient","CollectionsClient"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/client.ts","../src/lib/database.ts","../src/lib/functions.ts","../src/lib/jobs.ts","../src/lib/storage-adapters.ts","../src/lib/auth.ts","../src/lib/logging.ts","../src/lib/environment.ts","../src/lib/validation.ts","../src/lib/storage.ts","../src/lib/realtime.ts","../src/lib/analytics.ts","../src/lib/sse.ts","../src/lib/ai-data.ts","../src/lib/ai.ts","../src/lib/typegen.ts","../src/lib/errors.ts"],"sourcesContent":["import { HttpClient } from './lib/client';\nimport { DatabaseClient } from './lib/database';\nimport { FunctionsClient } from './lib/functions';\nimport { JobsClient } from './lib/jobs';\nimport { AuthClient } from './lib/auth';\nimport { LoggingClient } from './lib/logging';\nimport { EnvironmentClient } from './lib/environment';\nimport { StorageClient } from './lib/storage';\nimport { RealtimeClient } from './lib/realtime';\nimport { AnalyticsClient } from './lib/analytics';\nimport { AiClient } from './lib/ai';\nimport { MemoryClient, CollectionsClient } from './lib/ai-data';\nimport type { NestCoreConfig } from './types';\nimport type { RealtimeOptions } from './lib/realtime';\nimport { type StorageAdapter, localStorageAdapter, memoryAdapter, createLocalStorageAdapter, createMemoryAdapter } from './lib/storage-adapters';\n\nexport * from './types';\nexport * from './lib/logging';\nexport * from './lib/environment';\nexport * from './lib/storage';\nexport * from './lib/realtime';\nexport * from './lib/analytics';\nexport * from './lib/ai';\nexport * from './lib/ai-data';\nexport { generateTypes, type GenerateTypesOptions } from './lib/typegen';\nexport { validatePath, sanitizePath, validateRequired } from './lib/validation';\nexport { NestCoreError, OrbitNestError, unwrap, AuthLimitCodes, ProjectErrorCodes, type CreditsExhaustedReason, type CreditsExhaustedDetails } from './lib/errors';\nexport { isMfaChallenge, type MfaChallenge, type MfaFactor } from './lib/auth';\nexport {\n type StorageAdapter,\n localStorageAdapter,\n memoryAdapter,\n createLocalStorageAdapter,\n createMemoryAdapter,\n};\n\nconst DEFAULT_BASE_URL = 'https://api.nestcore.io';\n\n/**\n * Decode the `project_slug` claim from an NestCore API key (JWT).\n *\n * The signing key isn't needed — we only read the payload to look up which\n * project the key belongs to. Throws when the token isn't a JWT or doesn't\n * carry a `project_slug` claim, which signals a misconfigured key.\n */\nfunction decodeProjectSlugFromJwt(token: string): string {\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new Error('NestCore: apiKey is not a valid JWT (expected 3 segments)');\n }\n let payload = parts[1];\n // base64url → base64 padding\n payload = payload.replace(/-/g, '+').replace(/_/g, '/');\n switch (payload.length % 4) {\n case 2: payload += '=='; break;\n case 3: payload += '='; break;\n }\n let json: Record<string, unknown>;\n try {\n // Browsers expose `atob`; Node (for SSR/isomorphic use) exposes `Buffer`.\n const decoded = typeof atob !== 'undefined'\n ? atob(payload)\n : Buffer.from(payload, 'base64').toString('utf8');\n json = JSON.parse(decoded) as Record<string, unknown>;\n } catch {\n throw new Error('NestCore: could not decode apiKey JWT payload');\n }\n const slug = json['project_slug'];\n if (typeof slug !== 'string' || slug.length === 0) {\n throw new Error('NestCore: apiKey JWT does not contain a project_slug claim');\n }\n return slug;\n}\n\n/** @deprecated Renamed to {@link NestCoreClient}. */\nexport type OrbitNestClient = NestCoreClient;\n\nexport interface NestCoreClient {\n db: DatabaseClient;\n functions: FunctionsClient;\n /** @deprecated Needs a console admin token; every call returns an `ADMIN_ONLY` error. */\n jobs: JobsClient;\n auth: AuthClient;\n /** @deprecated Needs a console admin token; every call returns an `ADMIN_ONLY` error. */\n logs: LoggingClient;\n /** @deprecated Needs a console admin token; every call returns an `ADMIN_ONLY` error. */\n env: EnvironmentClient;\n storage: StorageClient;\n analytics: AnalyticsClient;\n /** AI Gateway: chat, streaming and embeddings with the project's own provider keys; `ai.threads` for conversations. */\n ai: AiClient;\n /** Memory API: facts about a user or agent, with semantic search. */\n memory: MemoryClient;\n /** Vector collections: ingest documents, hybrid search. */\n collections: CollectionsClient;\n /**\n * Create a realtime channel for live DB-change subscriptions and broadcasts.\n *\n * ```ts\n * const ch = client.channel('orders')\n * .on('postgres_changes',\n * { event: 'INSERT', table: 'orders', filter: { user_id: { eq: userId } } },\n * (e) => console.log('new order', e.new))\n * .subscribe();\n * ```\n *\n * In the browser this uses the native `WebSocket`. For isomorphic/SSR use on\n * Node.js < 22, pass a constructor: `realtimeOptions: { WebSocket }`.\n */\n channel: (name: string) => ReturnType<RealtimeClient['channel']>;\n /** Direct access to the underlying RealtimeClient. */\n realtime: RealtimeClient;\n}\n\n/**\n * Create an NestCore client for the **browser** (and other DOM/fetch\n * environments).\n *\n * Only `apiKey` is required — the project slug is decoded from the API key's\n * JWT payload and `baseUrl` defaults to `https://api.nestcore.io`.\n *\n * Pass a `storage` adapter to persist the auth session across page loads\n * (`localStorageAdapter` keeps users signed in after a refresh; the default is\n * in-memory). Use a **publishable anon key** in client-side code — never a\n * service-role key.\n *\n * ```ts\n * import { createClient, localStorageAdapter } from '@nestcorehq/js';\n *\n * const nestcore = createClient({\n * apiKey: import.meta.env.VITE_NESTCORE_ANON_KEY,\n * storage: localStorageAdapter, // keep the session after refresh\n * });\n *\n * const { data, error } = await nestcore.db.from('posts').eq('published', true).select();\n * ```\n */\nexport function createClient(\n config: NestCoreConfig & { realtimeOptions?: RealtimeOptions; storage?: StorageAdapter },\n): NestCoreClient {\n if (!config.apiKey) {\n throw new Error('NestCore: apiKey is required');\n }\n\n // Resolve projectSlug: caller override → JWT payload claim\n const projectSlug = config.projectSlug ?? decodeProjectSlugFromJwt(config.apiKey);\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n\n const httpClient = new HttpClient({ ...config, projectSlug, baseUrl });\n\n // The project slug is not a secret, so it stays in the URL query string.\n // The apiKey is a credential and must NOT appear in the URL, where it would\n // leak into proxy/CDN access logs, APM traces, and browser history. It is\n // passed to RealtimeClient and sent on the WS handshake via subprotocol (the\n // only credential channel a browser WebSocket allows).\n const wsUrl = `${baseUrl.replace(/^http/, 'ws')}/realtime/v1/ws?project=${encodeURIComponent(projectSlug)}`;\n const auth = new AuthClient(httpClient, { storage: config.storage });\n // Data, storage and function calls run as the signed-in user (RLS\n // `auth.uid()`), refreshing the access token shortly before it expires.\n httpClient.setAccessTokenProvider(() => auth.getAccessToken(), (stale) => auth.refreshAccessToken(stale));\n\n // An explicit realtimeOptions.userJwt wins; otherwise realtime follows auth.\n const followAuth = !config.realtimeOptions?.userJwt;\n const realtimeClient = new RealtimeClient(wsUrl, {\n ...config.realtimeOptions,\n apiKey: config.apiKey,\n ...(followAuth\n ? {\n accessToken: () => auth.getAccessToken(),\n // A refresh notifies onAuthStateChange → setAuth, which reconnects.\n onAuthRejected: (stale: string) => { void auth.refreshAccessToken(stale); },\n }\n : {}),\n });\n if (followAuth) auth.onAuthStateChange((s) => realtimeClient.setAuth(s?.access_token ?? null));\n\n const db = new DatabaseClient(httpClient);\n\n return {\n db,\n functions: new FunctionsClient(httpClient),\n jobs: new JobsClient(httpClient),\n auth,\n logs: new LoggingClient(httpClient),\n env: new EnvironmentClient(httpClient),\n storage: new StorageClient(httpClient),\n channel: (name: string) => realtimeClient.channel(name),\n realtime: realtimeClient,\n analytics: new AnalyticsClient(httpClient),\n ai: new AiClient(httpClient),\n memory: new MemoryClient(httpClient),\n collections: new CollectionsClient(httpClient),\n };\n}\n\nexport {\n HttpClient, DatabaseClient, FunctionsClient, JobsClient,\n AuthClient, LoggingClient, EnvironmentClient, StorageClient,\n RealtimeClient, AnalyticsClient,\n};\n","import type { NestCoreConfig, RequestOptions, ApiResult, ApiError } from '../types';\n\n// Internal config — by the time it reaches the HttpClient the slug has been\n// resolved (decoded from JWT or supplied by the caller), so it's required.\ntype ResolvedConfig = Omit<NestCoreConfig, 'projectSlug'> & { projectSlug: string };\n\nexport class HttpClient {\n private baseUrl: string;\n private apiKey: string;\n private projectSlug: string;\n private timeout: number;\n private analyticsToken: string | undefined;\n private clientIp: string | undefined;\n private accessTokenProvider?: () => Promise<string | null>;\n private tokenRefresher?: (staleToken: string) => Promise<string | null>;\n\n constructor(config: ResolvedConfig) {\n this.baseUrl = (config.baseUrl || 'https://api.nestcore.io').replace(/\\/$/, '');\n this.apiKey = config.apiKey;\n this.projectSlug = config.projectSlug;\n this.timeout = config.timeout || 30000;\n this.analyticsToken = config.analyticsToken;\n this.clientIp = config.clientIp;\n }\n\n getProjectSlug(): string {\n return this.projectSlug;\n }\n\n /** Configured API origin (e.g. `https://api.nestcore.io`) without trailing slash. */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n /** Default request timeout in ms. */\n getTimeout(): number {\n return this.timeout;\n }\n\n /** API key (used by storage methods that bypass the JSON request helper). */\n getApiKey(): string {\n return this.apiKey;\n }\n\n /**\n * Supply the signed-in user's access token (or null). `createClient` wires\n * this to the AuthClient so data, storage and function calls run as the user.\n * `refresh` is called once when the API rejects that token (401); it returns\n * a fresh token (or null when the session can't be renewed).\n */\n setAccessTokenProvider(\n fn: () => Promise<string | null>,\n refresh?: (staleToken: string) => Promise<string | null>,\n ): void {\n this.accessTokenProvider = fn;\n this.tokenRefresher = refresh;\n }\n\n /**\n * Credential headers for a project request. `apikey` always carries the\n * project key; `Authorization` carries the user's access token when signed\n * in (the project key otherwise). Matches the API's ApiKeyGuard precedence.\n */\n async authHeaders(): Promise<Record<string, string>> {\n return this.headersFor(this.accessTokenProvider ? await this.accessTokenProvider() : null);\n }\n\n /**\n * `fetch` with project credentials ahead of `init.headers`. As the user\n * (`asUser`), a 401 for the user token refreshes the session and retries\n * once; a second 401 is returned as-is (never a sign-out by itself).\n */\n async authFetch(\n url: string,\n init: RequestInit & { headers?: Record<string, string> },\n asUser = true,\n ): Promise<Response> {\n const token = asUser && this.accessTokenProvider ? await this.accessTokenProvider() : null;\n const send = (t: string | null) => fetch(url, { ...init, headers: { ...this.headersFor(t), ...init.headers } });\n const res = await send(token);\n if (res.status !== 401 || !token || !this.tokenRefresher) return res;\n const fresh = await this.tokenRefresher(token);\n if (!fresh || fresh === token) return res;\n await res.body?.cancel().catch(() => {});\n // FormData / string bodies are re-sendable; a one-shot stream would not be.\n return send(fresh);\n }\n\n async request<T>(path: string, options: RequestOptions = {}): Promise<ApiResult<T>> {\n return this._fetch<T>(path, options, true);\n }\n\n /**\n * Same as `request()` but never attaches the user session, only the project\n * key. Auth endpoints (sign-in, refresh, …) use it so refreshing a session\n * can't recurse into itself.\n */\n async requestPublic<T>(path: string, options: RequestOptions = {}): Promise<ApiResult<T>> {\n return this._fetch<T>(path, options, false);\n }\n\n /** Analytics-specific request — adds `x-analytics-token` header when configured. */\n async requestForAnalytics<T>(path: string, options: RequestOptions = {}): Promise<ApiResult<T>> {\n const extraHeaders = this.analyticsToken\n ? { 'x-analytics-token': this.analyticsToken }\n : undefined;\n return this._fetch<T>(path, { ...options, headers: { ...extraHeaders, ...options.headers } }, false);\n }\n\n /**\n * Same as `request()` but sends the supplied user access token as the\n * Bearer auth header. Used for endpoints that authenticate as a project\n * user (e.g. passkey device management).\n */\n async requestWithToken<T>(\n path: string,\n accessToken: string,\n options: RequestOptions = {},\n ): Promise<ApiResult<T>> {\n return this._fetch<T>(path, { ...options, headers: { Authorization: `Bearer ${accessToken}`, ...options.headers } }, false);\n }\n\n private headersFor(token: string | null): Record<string, string> {\n return {\n apikey: this.apiKey,\n Authorization: `Bearer ${token || this.apiKey}`,\n ...(this.clientIp ? { 'X-NestCore-Client-IP': this.clientIp } : {}),\n };\n }\n\n private async _fetch<T>(\n path: string,\n options: RequestOptions,\n asUser: boolean,\n ): Promise<ApiResult<T>> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), options.timeout || this.timeout);\n\n try {\n const response = await this.authFetch(url, {\n method: options.method || 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n keepalive: options.keepalive,\n // Fail loudly on unexpected redirects rather than silently forwarding\n // the Bearer credential to the redirect target.\n redirect: 'error',\n }, asUser);\n\n clearTimeout(timeoutId);\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n return {\n data: null,\n error: {\n message: errorMessage(data, response.status),\n code: data?.code || data?.statusCode?.toString(),\n status: response.status,\n // Structured fields, e.g. QUOTA_EXCEEDED's quota/plan/limit/usage.\n details: data ?? undefined,\n },\n };\n }\n\n return { data: data as T, error: null };\n } catch (err) {\n clearTimeout(timeoutId);\n\n if (err instanceof Error && err.name === 'AbortError') {\n return {\n data: null,\n error: { message: 'Request timeout', code: 'TIMEOUT' },\n };\n }\n\n return {\n data: null,\n error: {\n message: err instanceof Error ? err.message : 'Unknown error',\n code: 'NETWORK_ERROR',\n },\n };\n }\n }\n}\n\n/** Readable message from an API error body (NestJS validation sends `message: string[]`). */\nexport function errorMessage(body: unknown, status: number, fallback?: string): string {\n const b = body as { message?: unknown; error?: unknown } | null;\n const m = b?.message ?? b?.error;\n if (Array.isArray(m)) return m.join('; ');\n if (typeof m === 'string' && m) return m;\n const nested = (m as { message?: unknown } | null)?.message;\n if (typeof nested === 'string' && nested) return nested;\n return fallback ?? `Request failed with status ${status}`;\n}\n\n/**\n * `jobs`, `env` and `logs` map to console-admin routes (admin JWT + project\n * UUID, owner only). A project anon/service key can never call them, so these\n * clients fail fast instead of sending a request that always 401s.\n */\nexport function adminOnlyError(feature: string): ApiError {\n return {\n data: null,\n error: {\n message:\n `NestCore: ${feature} requires a NestCore console admin token and is not available with a project API key. ` +\n 'Manage it in the NestCore console, the NestCore CLI or the MCP server.',\n code: 'ADMIN_ONLY',\n },\n };\n}\n","import type { HttpClient } from './client';\r\nimport type {\r\n QueryResult,\r\n TableMetadata,\r\n PaginationOptions,\r\n RlsPolicy,\r\n ApiResult,\r\n} from '../types';\r\n\r\nexport class DatabaseClient {\r\n constructor(private client: HttpClient) {}\r\n\r\n /**\r\n * Get the base path for client database operations.\r\n *\r\n * Note: this path is intentionally **singular** (`/api/project/:slug/...`).\r\n * The plural form (`/api/projects/:projectId/...`) is reserved for the\r\n * admin/console routes which authenticate with the global JWT_SECRET. The\r\n * client SDK uses a per-project anon/service key, so it must hit the\r\n * singular endpoint where `ApiKeyGuard` validates with the project's own\r\n * `jwt_secret`.\r\n */\r\n private get basePath(): string {\r\n return `/api/project/${this.client.getProjectSlug()}/database`;\r\n }\r\n\r\n /**\r\n * Execute a raw SQL query with optional parameter binding.\r\n *\r\n * Params are bound on the backend via `pg`'s native $1/$2/... placeholders\r\n * (see studio_api `ProjectDatabaseUtil.query`). Always prefer this over\r\n * string interpolation when you have untrusted input:\r\n *\r\n * db.query('SELECT * FROM users WHERE email = $1', [email])\r\n */\r\n /**\r\n * Rows nearest to `query` by meaning, from a column embedded with automatic\r\n * embeddings (Console → AI → Embeddings). Runs under RLS as the signed-in user.\r\n * Each row carries `_similarity` (cosine) and `_score`. With `hybridColumn`,\r\n * keyword matches on that text column are blended in (reciprocal rank fusion).\r\n * Usually reached through `db.from(table).eq(...).semantic(column, query)`.\r\n */\r\n async semantic<T = Record<string, unknown>>(\r\n table: string,\r\n column: string,\r\n query: string,\r\n opts: SemanticOptions & { filters?: SemanticFilter[] } = {},\r\n ): Promise<ApiResult<{ rows: Array<T & { _similarity: number | null; _score: number }>; model: string | null }>> {\r\n return this.client.request(`${this.basePath}/semantic`, { method: 'POST', body: { table, column, query, ...opts } });\r\n }\r\n\r\n async query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<ApiResult<QueryResult<T>>> {\r\n const result = await this.client.request<{\r\n success: boolean;\r\n data: T[];\r\n rows_affected: number;\r\n columns?: Array<{ name: string; type: string }>;\r\n }>(`${this.basePath}/sql`, {\r\n method: 'POST',\r\n body: params && params.length > 0 ? { sql, params } : { sql },\r\n });\r\n\r\n if (result.error) {\r\n return result as ApiResult<QueryResult<T>>;\r\n }\r\n\r\n // Transform API response to QueryResult format\r\n return {\r\n data: {\r\n rows: result.data.data || [],\r\n rowCount: result.data.rows_affected || 0,\r\n fields: result.data.columns?.map(col => ({ name: col.name, dataType: col.type })),\r\n },\r\n error: null,\r\n };\r\n }\r\n\r\n /**\r\n * Get all tables in the database\r\n */\r\n async listTables(): Promise<ApiResult<string[]>> {\r\n return this.client.request<string[]>(`${this.basePath}/tables/list`);\r\n }\r\n\r\n /**\r\n * Get table metadata including columns. The API returns one entry per\r\n * matching table (an empty array when the table doesn't exist).\r\n */\r\n async getTableMetadata(tableName: string): Promise<ApiResult<TableMetadata[]>> {\r\n const query = new URLSearchParams({ table: tableName });\r\n return this.client.request<TableMetadata[]>(`${this.basePath}/tables?${query.toString()}`);\r\n }\r\n\r\n /**\r\n * Get table data with pagination\r\n */\r\n async getTableData<T = Record<string, unknown>>(\r\n tableName: string,\r\n options: PaginationOptions = {}\r\n ): Promise<ApiResult<{ rows: T[]; total: number }>> {\r\n const params = new URLSearchParams();\r\n if (options.page) params.set('page', options.page.toString());\r\n if (options.limit) params.set('limit', options.limit.toString());\r\n if (options.sortBy) params.set('sortBy', options.sortBy);\r\n if (options.sortOrder) params.set('sortOrder', options.sortOrder);\r\n\r\n const query = params.toString() ? `?${params.toString()}` : '';\r\n const result = await this.client.request<{ data: T[]; total_rows: number }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/data${query}`\r\n );\r\n if (result.error) return result;\r\n return { data: { rows: result.data.data ?? [], total: result.data.total_rows ?? 0 }, error: null };\r\n }\r\n\r\n /**\r\n * Insert a row into a table\r\n * Returns the inserted row data\r\n */\r\n async insert<T = Record<string, unknown>>(\r\n tableName: string,\r\n data: Record<string, unknown>\r\n ): Promise<ApiResult<T>> {\r\n const result = await this.client.request<{\r\n success: boolean;\r\n data: T[];\r\n rows_affected: number;\r\n message: string;\r\n }>(`${this.basePath}/tables/${encodeURIComponent(tableName)}/rows`, {\r\n method: 'POST',\r\n body: data,\r\n });\r\n\r\n if (result.error) {\r\n return result as ApiResult<T>;\r\n }\r\n\r\n // Return the first inserted row\r\n return {\r\n data: result.data.data?.[0] || (result.data as unknown as T),\r\n error: null,\r\n };\r\n }\r\n\r\n /**\r\n * Update a row by ID\r\n * Note: Requires service_role_key for authentication\r\n */\r\n async update<T = Record<string, unknown>>(\r\n tableName: string,\r\n rowId: string | number,\r\n data: Record<string, unknown>\r\n ): Promise<ApiResult<T>> {\r\n const result = await this.client.request<{\r\n success: boolean;\r\n data: T[];\r\n message: string;\r\n }>(`${this.basePath}/tables/${encodeURIComponent(tableName)}/rows/${encodeURIComponent(String(rowId))}`, {\r\n method: 'PUT',\r\n body: data,\r\n });\r\n\r\n if (result.error) {\r\n return result as ApiResult<T>;\r\n }\r\n\r\n // The API returns the updated rows as an array; return the row.\r\n return {\r\n data: result.data.data?.[0] ?? (result.data as unknown as T),\r\n error: null,\r\n };\r\n }\r\n\r\n /**\r\n * Delete a row by ID\r\n * Note: Requires service_role_key for authentication\r\n */\r\n async delete(tableName: string, rowId: string | number): Promise<ApiResult<{ success: boolean }>> {\r\n return this.client.request<{ success: boolean }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/rows/${encodeURIComponent(String(rowId))}`,\r\n { method: 'DELETE' }\r\n );\r\n }\r\n\r\n /**\r\n * Bulk insert rows. Returns the inserted rows.\r\n * Note: Requires service_role_key for authentication\r\n */\r\n async bulkInsert<T = Record<string, unknown>>(\r\n tableName: string,\r\n rows: Record<string, unknown>[]\r\n ): Promise<ApiResult<T[]>> {\r\n const result = await this.client.request<{ data?: T[] }>(`${this.basePath}/tables/${encodeURIComponent(tableName)}/rows/bulk`, {\r\n method: 'POST',\r\n body: rows,\r\n });\r\n if (result.error) return result;\r\n return { data: result.data.data ?? [], error: null };\r\n }\r\n\r\n /**\r\n * Bulk update rows. Returns the number of rows updated.\r\n * Note: Requires a service_role key or a signed-in user (RLS applies)\r\n */\r\n async bulkUpdate(\r\n tableName: string,\r\n updates: Array<{ where: Record<string, unknown>; data: Record<string, unknown> }>\r\n ): Promise<ApiResult<{ updated: number }>> {\r\n const result = await this.client.request<{ rows_affected?: number }>(`${this.basePath}/tables/${encodeURIComponent(tableName)}/rows/bulk`, {\r\n method: 'PUT',\r\n body: updates,\r\n });\r\n if (result.error) return result;\r\n return { data: { updated: result.data.rows_affected ?? 0 }, error: null };\r\n }\r\n\r\n /**\r\n * Bulk delete rows. Returns the number of rows deleted.\r\n * Note: Requires a service_role key or a signed-in user (RLS applies)\r\n */\r\n async bulkDelete(\r\n tableName: string,\r\n conditions: Record<string, unknown>[]\r\n ): Promise<ApiResult<{ deleted: number }>> {\r\n const result = await this.client.request<{ rows_affected?: number }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/rows/bulk`,\r\n {\r\n method: 'DELETE',\r\n body: conditions,\r\n }\r\n );\r\n if (result.error) return result;\r\n return { data: { deleted: result.data.rows_affected ?? 0 }, error: null };\r\n }\r\n\r\n /**\r\n * Enable Row Level Security on a table\r\n */\r\n async enableRls(tableName: string): Promise<ApiResult<{ success: boolean }>> {\r\n return this.client.request<{ success: boolean }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/rls/enable`,\r\n { method: 'POST' }\r\n );\r\n }\r\n\r\n /**\r\n * Disable Row Level Security on a table\r\n */\r\n async disableRls(tableName: string): Promise<ApiResult<{ success: boolean }>> {\r\n return this.client.request<{ success: boolean }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/rls/disable`,\r\n { method: 'POST' }\r\n );\r\n }\r\n\r\n /**\r\n * Create an RLS policy\r\n */\r\n async createPolicy(tableName: string, policy: RlsPolicy): Promise<ApiResult<{ success: boolean }>> {\r\n return this.client.request<{ success: boolean }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/policies`,\r\n {\r\n method: 'POST',\r\n body: policy,\r\n }\r\n );\r\n }\r\n\r\n /**\r\n * List RLS policies for a table\r\n */\r\n async listPolicies(tableName: string): Promise<ApiResult<RlsPolicy[]>> {\r\n const result = await this.client.request<{\r\n policies?: Array<{ name: string; command: RlsPolicy['command']; role?: string; using?: string | null; with_check?: string | null }>;\r\n }>(`${this.basePath}/tables/${encodeURIComponent(tableName)}/policies`);\r\n if (result.error) return result;\r\n // Map the API's pg_policies shape to the RlsPolicy fields createPolicy takes.\r\n const policies = (result.data.policies ?? []).map((p) => ({\r\n name: p.name,\r\n command: p.command,\r\n definition: p.using ?? '',\r\n ...(p.with_check ? { check: p.with_check } : {}),\r\n roles: String(p.role ?? '').replace(/[{}]/g, '').split(',').map((r) => r.trim()).filter(Boolean),\r\n }));\r\n return { data: policies, error: null };\r\n }\r\n\r\n /**\r\n * Delete an RLS policy\r\n */\r\n async deletePolicy(tableName: string, policyName: string): Promise<ApiResult<{ success: boolean }>> {\r\n return this.client.request<{ success: boolean }>(\r\n `${this.basePath}/tables/${encodeURIComponent(tableName)}/policies/${encodeURIComponent(policyName)}`,\r\n { method: 'DELETE' }\r\n );\r\n }\r\n\r\n /**\r\n * Create a fluent query builder for a table\r\n */\r\n from(tableName: string): TableQueryBuilder {\r\n return new TableQueryBuilder(this, tableName);\r\n }\r\n}\r\n\r\nconst SAFE_IDENT = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\r\n\r\ntype QueryFilter = { col: string; op: string; value: unknown; ftsType?: string; config?: string };\r\n\r\nexport interface SemanticOptions {\r\n /** Rows to return (default 10, max 100; a builder `.limit()` sets it too). */\r\n k?: number;\r\n /** A text column to also rank by keyword match. */\r\n hybridColumn?: string;\r\n /** Drop rows below this cosine similarity (0–1). */\r\n minSimilarity?: number;\r\n}\r\n\r\nexport type SemanticFilter = { column: string; op: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'in' | 'is'; value?: unknown };\r\n\r\n/**\r\n * Fluent, Supabase-style query builder. Filters/ordering/pagination compose into\r\n * a single parameterized SELECT executed via the SQL endpoint — so values are\r\n * always bound ($1, $2, …), never interpolated, and RLS still applies. Mirrors\r\n * the Flutter SDK's builder so the two SDKs feel the same.\r\n *\r\n * ```ts\r\n * const { data } = await client.db.from('posts')\r\n * .eq('status', 'published')\r\n * .gt('views', 100)\r\n * .order('created_at', { ascending: false })\r\n * .limit(10)\r\n * .select('id, title, views');\r\n * ```\r\n */\r\nclass TableQueryBuilder {\r\n private _filters: QueryFilter[] = [];\r\n private _orders: Array<{ col: string; asc: boolean }> = [];\r\n private _limit?: number;\r\n private _offset?: number;\r\n private _columns = '*';\r\n private _vectorOrder?: { col: string; vec: string; op: string };\r\n\r\n constructor(\r\n private db: DatabaseClient,\r\n private tableName: string,\r\n ) {\r\n this.ident(tableName);\r\n }\r\n\r\n private ident(name: string): string {\r\n if (!SAFE_IDENT.test(name)) {\r\n throw new Error(`NestCore: unsafe SQL identifier \"${name}\"`);\r\n }\r\n return `\"${name}\"`;\r\n }\r\n\r\n // ── Filter operators (match the backend's supported set) ──────────────────\r\n eq(column: string, value: unknown): this { return this.push(column, 'eq', value); }\r\n neq(column: string, value: unknown): this { return this.push(column, 'neq', value); }\r\n gt(column: string, value: unknown): this { return this.push(column, 'gt', value); }\r\n gte(column: string, value: unknown): this { return this.push(column, 'gte', value); }\r\n lt(column: string, value: unknown): this { return this.push(column, 'lt', value); }\r\n lte(column: string, value: unknown): this { return this.push(column, 'lte', value); }\r\n like(column: string, pattern: string): this { return this.push(column, 'like', pattern); }\r\n ilike(column: string, pattern: string): this { return this.push(column, 'ilike', pattern); }\r\n in(column: string, values: unknown[]): this { return this.push(column, 'in', values); }\r\n is(column: string, value: null | boolean): this { return this.push(column, 'is', value); }\r\n\r\n /**\r\n * Full-text search on a column using PostgreSQL text search.\r\n *\r\n * ```ts\r\n * await client.db.from('articles').textSearch('body', 'postgres & search').select();\r\n * ```\r\n *\r\n * @param type `websearch` (default, Google-style operators), `plain`, or `phrase`.\r\n * @param config text-search configuration / language (default `english`).\r\n */\r\n textSearch(\r\n column: string,\r\n query: string,\r\n opts?: { type?: 'websearch' | 'plain' | 'phrase'; config?: string },\r\n ): this {\r\n this.ident(column);\r\n const config = opts?.config ?? 'english';\r\n if (!SAFE_IDENT.test(config)) {\r\n throw new Error(`NestCore: unsafe text-search config \"${config}\"`);\r\n }\r\n this._filters.push({ col: column, op: 'fts', value: query, ftsType: opts?.type ?? 'websearch', config });\r\n return this;\r\n }\r\n\r\n /**\r\n * Order by vector similarity (pgvector). Returns the nearest rows to\r\n * `vector` by the chosen distance metric, closest first.\r\n *\r\n * ```ts\r\n * await client.db.from('documents').nearest('embedding', queryEmbedding, { metric: 'cosine', limit: 5 }).select();\r\n * ```\r\n *\r\n * Requires the `vector` extension (`CREATE EXTENSION IF NOT EXISTS vector`).\r\n * @param metric `l2` (default, `<->`), `cosine` (`<=>`), or `ip` (`<#>`).\r\n */\r\n nearest(column: string, vector: number[], opts?: { metric?: 'l2' | 'cosine' | 'ip'; limit?: number }): this {\r\n this.ident(column);\r\n if (!Array.isArray(vector) || !vector.every((n) => typeof n === 'number' && Number.isFinite(n))) {\r\n throw new Error('NestCore: nearest() vector must be an array of finite numbers');\r\n }\r\n const op = opts?.metric === 'cosine' ? '<=>' : opts?.metric === 'ip' ? '<#>' : '<->';\r\n this._vectorOrder = { col: column, vec: `[${vector.join(',')}]`, op };\r\n if (opts?.limit != null) this._limit = opts.limit;\r\n return this;\r\n }\r\n\r\n private push(column: string, op: string, value: unknown): this {\r\n this.ident(column);\r\n this._filters.push({ col: column, op, value });\r\n return this;\r\n }\r\n\r\n // ── Ordering / pagination ─────────────────────────────────────────────────\r\n order(column: string, opts?: { ascending?: boolean }): this {\r\n this.ident(column);\r\n this._orders.push({ col: column, asc: opts?.ascending !== false });\r\n return this;\r\n }\r\n /** @deprecated use {@link order} */\r\n orderBy(column: string, dir: 'ASC' | 'DESC' = 'ASC'): this {\r\n return this.order(column, { ascending: dir === 'ASC' });\r\n }\r\n limit(n: number): this { this._limit = n; return this; }\r\n /** Inclusive range, like Supabase: `.range(0, 9)` → first 10 rows. */\r\n range(from: number, to: number): this {\r\n this._offset = from;\r\n this._limit = to - from + 1;\r\n return this;\r\n }\r\n page(n: number, size = 20): this {\r\n this._limit = size;\r\n this._offset = Math.max(0, (n - 1) * size);\r\n return this;\r\n }\r\n\r\n private build(): { sql: string; params: unknown[] } {\r\n const params: unknown[] = [];\r\n const cols =\r\n this._columns === '*'\r\n ? '*'\r\n : this._columns.split(',').map((c) => this.ident(c.trim())).join(', ');\r\n let sql = `SELECT ${cols} FROM ${this.ident(this.tableName)}`;\r\n\r\n const where: string[] = [];\r\n for (const f of this._filters) {\r\n const col = this.ident(f.col);\r\n switch (f.op) {\r\n case 'eq': where.push(`${col} = $${params.push(f.value)}`); break;\r\n case 'neq': where.push(`${col} != $${params.push(f.value)}`); break;\r\n case 'gt': where.push(`${col} > $${params.push(f.value)}`); break;\r\n case 'gte': where.push(`${col} >= $${params.push(f.value)}`); break;\r\n case 'lt': where.push(`${col} < $${params.push(f.value)}`); break;\r\n case 'lte': where.push(`${col} <= $${params.push(f.value)}`); break;\r\n case 'like': where.push(`${col} LIKE $${params.push(f.value)}`); break;\r\n case 'ilike': where.push(`${col} ILIKE $${params.push(f.value)}`); break;\r\n case 'in': {\r\n const vals = (f.value as unknown[]) ?? [];\r\n if (vals.length === 0) { where.push('FALSE'); break; }\r\n where.push(`${col} IN (${vals.map((v) => `$${params.push(v)}`).join(', ')})`);\r\n break;\r\n }\r\n case 'is':\r\n if (f.value === null) where.push(`${col} IS NULL`);\r\n else where.push(`${col} = $${params.push(f.value)}`);\r\n break;\r\n case 'fts': {\r\n // config is validated as a safe identifier in textSearch(); the query\r\n // value is always bound as a parameter.\r\n const cfg = f.config ?? 'english';\r\n const fn =\r\n f.ftsType === 'plain' ? 'plainto_tsquery'\r\n : f.ftsType === 'phrase' ? 'phraseto_tsquery'\r\n : 'websearch_to_tsquery';\r\n where.push(`to_tsvector('${cfg}', ${col}::text) @@ ${fn}('${cfg}', $${params.push(f.value)})`);\r\n break;\r\n }\r\n }\r\n }\r\n if (where.length) sql += ` WHERE ${where.join(' AND ')}`;\r\n const orderClauses: string[] = [];\r\n if (this._vectorOrder) {\r\n // vector bound as a param, cast to ::vector — values are never interpolated.\r\n orderClauses.push(`${this.ident(this._vectorOrder.col)} ${this._vectorOrder.op} $${params.push(this._vectorOrder.vec)}::vector`);\r\n }\r\n for (const o of this._orders) orderClauses.push(`${this.ident(o.col)} ${o.asc ? 'ASC' : 'DESC'}`);\r\n if (orderClauses.length) sql += ` ORDER BY ${orderClauses.join(', ')}`;\r\n if (this._limit != null) sql += ` LIMIT $${params.push(this._limit)}`;\r\n if (this._offset != null) sql += ` OFFSET $${params.push(this._offset)}`;\r\n return { sql, params };\r\n }\r\n\r\n /** Run the built SELECT. Returns `{ rows, total }` (total = rows returned). */\r\n async select<T = Record<string, unknown>>(columns = '*'): Promise<ApiResult<{ rows: T[]; total: number }>> {\r\n this._columns = columns;\r\n const { sql, params } = this.build();\r\n const res = await this.db.query<T>(sql, params);\r\n if (res.error) return res as ApiResult<{ rows: T[]; total: number }>;\r\n return { data: { rows: res.data.rows, total: res.data.rows.length }, error: null };\r\n }\r\n\r\n /** Expect exactly one row; errors if zero rows. */\r\n async single<T = Record<string, unknown>>(columns = '*'): Promise<ApiResult<T>> {\r\n this._limit = 1;\r\n const res = await this.select<T>(columns);\r\n if (res.error) return res as ApiResult<T>;\r\n const row = res.data.rows[0];\r\n if (!row) return { data: null, error: { message: 'No rows found', code: 'NOT_FOUND' } };\r\n return { data: row, error: null };\r\n }\r\n\r\n /** Return the first row or null (no error when empty). */\r\n async maybeSingle<T = Record<string, unknown>>(columns = '*'): Promise<ApiResult<T | null>> {\r\n this._limit = 1;\r\n const res = await this.select<T>(columns);\r\n if (res.error) return res as ApiResult<T | null>;\r\n return { data: res.data.rows[0] ?? null, error: null };\r\n }\r\n\r\n /**\r\n * Semantic search over an embedded column, keeping this builder's filters\r\n * (`eq`, `gt`, `in`, …; not `textSearch` or `nearest`).\r\n *\r\n * ```ts\r\n * const { data } = await client.db.from('articles').eq('published', true)\r\n * .semantic('body_embedding', 'how do I reset my password?', { k: 5 });\r\n * ```\r\n */\r\n async semantic<T = Record<string, unknown>>(column: string, query: string, opts: SemanticOptions = {}): Promise<ApiResult<{ rows: Array<T & { _similarity: number | null; _score: number }>; model: string | null }>> {\r\n this.ident(column);\r\n if (this._vectorOrder || this._filters.some((f) => f.op === 'fts')) {\r\n return { data: null, error: { message: 'semantic() cannot be combined with textSearch() or nearest(); use hybridColumn for keyword ranking', code: 'INVALID_QUERY' } };\r\n }\r\n const filters = this._filters.map((f) => ({ column: f.col, op: f.op as SemanticFilter['op'], value: f.value }));\r\n return this.db.semantic<T>(this.tableName, column, query, { k: this._limit, ...opts, ...(filters.length ? { filters } : {}) });\r\n }\r\n\r\n // ── Writes (delegate to the row endpoints) ────────────────────────────────\r\n async insert<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<ApiResult<T>> {\r\n return this.db.insert<T>(this.tableName, data);\r\n }\r\n async update<T = Record<string, unknown>>(id: string | number, data: Record<string, unknown>): Promise<ApiResult<T>> {\r\n return this.db.update<T>(this.tableName, id, data);\r\n }\r\n async delete(id: string | number): Promise<ApiResult<{ success: boolean }>> {\r\n return this.db.delete(this.tableName, id);\r\n }\r\n}\r\n","import { errorMessage, type HttpClient } from './client';\nimport type { FunctionInvokeOptions, FunctionResponse, ApiResult } from '../types';\n\nexport class FunctionsClient {\n constructor(private client: HttpClient) {}\n\n /**\n * Invoke an edge function. Runs as the signed-in user when there is a\n * session (fills `ctx.user`), otherwise with the project key.\n *\n * Returns the function's real `status` and `headers`. The body is parsed by\n * content type: JSON → object, `text/*` → string, anything else → Blob.\n * A non-2xx response is an error whose `details` holds the parsed body.\n */\n async invoke<T = unknown>(\n functionName: string,\n options: FunctionInvokeOptions = {}\n ): Promise<ApiResult<FunctionResponse<T>>> {\n const url = `${this.client.getBaseUrl()}/api/projects/${this.client.getProjectSlug()}/functions/v1/${encodeURIComponent(functionName)}`;\n const method = options.method || 'POST';\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.client.getTimeout());\n\n try {\n const response = await this.client.authFetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: options.body !== undefined && method !== 'GET' ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n // Never forward credentials to a redirect target.\n redirect: 'error',\n });\n clearTimeout(timeoutId);\n\n const contentType = response.headers.get('content-type') ?? '';\n const data: unknown = contentType.includes('json')\n ? await response.json().catch(() => null)\n : contentType.startsWith('text/')\n ? await response.text()\n : await response.blob();\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => { headers[key] = value; });\n\n if (!response.ok) {\n return {\n data: null,\n error: {\n message: errorMessage(data, response.status),\n code: (data as { code?: string } | null)?.code,\n status: response.status,\n details: data,\n },\n };\n }\n return { data: { data: data as T, status: response.status, headers }, error: null };\n } catch (err) {\n clearTimeout(timeoutId);\n if (err instanceof Error && err.name === 'AbortError') {\n return { data: null, error: { message: 'Request timeout', code: 'TIMEOUT' } };\n }\n return {\n data: null,\n error: { message: err instanceof Error ? err.message : 'Unknown error', code: 'NETWORK_ERROR' },\n };\n }\n }\n}\n","import { adminOnlyError, type HttpClient } from './client';\nimport type {\n BackgroundJob,\n BackgroundJobRun,\n CreateJobOptions,\n UpdateJobOptions,\n ApiResult,\n} from '../types';\n\n/**\n * @deprecated Not usable with a project API key: these routes need a NestCore console\n * admin token. Every method resolves to an `ADMIN_ONLY` error without calling\n * the API. Use the NestCore console, the CLI or the MCP server instead.\n */\nexport class JobsClient {\n constructor(private client: HttpClient) {}\n\n /**\n * Create a background job with a cron schedule.\n */\n async create(options: CreateJobOptions): Promise<ApiResult<BackgroundJob>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * List all background jobs for the current project.\n */\n async list(): Promise<ApiResult<BackgroundJob[]>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * Get a single background job by name.\n */\n async get(name: string): Promise<ApiResult<BackgroundJob>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * Update a background job's code, schedule, or configuration.\n */\n async update(\n name: string,\n options: UpdateJobOptions,\n ): Promise<ApiResult<BackgroundJob>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * Delete a background job.\n */\n async delete(name: string): Promise<ApiResult<{ success: boolean }>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * Manually trigger a background job to run immediately.\n */\n async trigger(name: string): Promise<ApiResult<BackgroundJobRun>> {\n return adminOnlyError('client.jobs');\n }\n\n /**\n * Get execution history for a background job.\n */\n async getRuns(\n name: string,\n options?: { limit?: number },\n ): Promise<ApiResult<BackgroundJobRun[]>> {\n return adminOnlyError('client.jobs');\n }\n}\n","/**\n * Pluggable key/value storage for persisting the auth session across page\n * loads. A browser app typically wants `localStorageAdapter` so a signed-in\n * user stays signed in after a refresh. Without an adapter the session lives\n * only in that client instance — the right choice for SSR (one client per\n * request) or when you manage tokens yourself.\n */\nexport interface StorageAdapter {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/** In-memory storage — sessions live only for the lifetime of the tab/process. */\nexport function createMemoryAdapter(): StorageAdapter {\n const store = new Map<string, string>();\n return {\n getItem: (k) => (store.has(k) ? store.get(k)! : null),\n setItem: (k, v) => void store.set(k, v),\n removeItem: (k) => void store.delete(k),\n };\n}\n\n/**\n * Browser `localStorage`-backed storage. Falls back to in-memory automatically\n * when `localStorage` is unavailable or throws (SSR, private-mode, quota), so\n * it's always safe to pass.\n */\nexport function createLocalStorageAdapter(): StorageAdapter {\n let ls: Storage | null = null;\n try {\n ls = typeof globalThis !== 'undefined' ? (globalThis as { localStorage?: Storage }).localStorage ?? null : null;\n // Probe — Safari private mode exposes localStorage but throws on write.\n if (ls) {\n const probe = '__nestcore_probe__';\n ls.setItem(probe, '1');\n ls.removeItem(probe);\n }\n } catch {\n ls = null;\n }\n if (!ls) return createMemoryAdapter();\n\n const storage = ls;\n return {\n getItem: (k) => {\n try {\n return storage.getItem(k);\n } catch {\n return null;\n }\n },\n setItem: (k, v) => {\n try {\n storage.setItem(k, v);\n } catch {\n /* ignore quota / private-mode errors */\n }\n },\n removeItem: (k) => {\n try {\n storage.removeItem(k);\n } catch {\n /* ignore */\n }\n },\n };\n}\n\n/**\n * Non-persisting adapter: the session lives only in the client instance that\n * holds it. Shared module-level state here would leak one user's session into\n * every other client in an SSR process, so this stores nothing.\n */\nexport const memoryAdapter: StorageAdapter = {\n getItem: () => null,\n setItem: () => undefined,\n removeItem: () => undefined,\n};\n\n/**\n * Browser `localStorage`, resolved on each call. Where `localStorage` is\n * unavailable (SSR, private mode) it persists nothing, so on a server every\n * client keeps its session to itself instead of sharing one in-memory store.\n */\nexport const localStorageAdapter: StorageAdapter = {\n getItem: (k) => {\n try { return usableLocalStorage()?.getItem(k) ?? null; } catch { return null; }\n },\n setItem: (k, v) => {\n try { usableLocalStorage()?.setItem(k, v); } catch { /* quota / private mode */ }\n },\n removeItem: (k) => {\n try { usableLocalStorage()?.removeItem(k); } catch { /* ignore */ }\n },\n};\n\nfunction usableLocalStorage(): Storage | null {\n try {\n return (globalThis as { localStorage?: Storage }).localStorage ?? null;\n } catch {\n return null;\n }\n}\n","import type { HttpClient } from './client';\nimport type {\n AuthSession,\n AuthUser,\n SignUpCredentials,\n SignInCredentials,\n PasswordRecoveryOptions,\n PasswordResetOptions,\n UpdateUserOptions,\n ApiResult,\n PasskeyDevice,\n PasskeyRegistrationOptions,\n PasskeyAuthenticationOptions,\n PasskeyRegisterVerifyOptions,\n PasskeyAuthenticateVerifyOptions,\n PasskeySignUpOptions,\n PasskeySignUpVerifyOptions,\n} from '../types';\nimport type { StorageAdapter } from './storage-adapters';\nimport { memoryAdapter } from './storage-adapters';\n\n/** Returned by `signIn` when the account has MFA enrolled — complete with `verifyMfa`. */\nexport interface MfaChallenge {\n mfa_required: true;\n challenge_token: string;\n factors: Array<{ id: string; type: string }>;\n}\n\n/** An enrolled MFA factor. */\nexport interface MfaFactor {\n id: string;\n factor_type: string;\n friendly_name?: string | null;\n verified: boolean;\n created_at: string;\n last_used_at?: string | null;\n}\n\n/** Type guard: did `signIn` return an MFA challenge instead of a session? */\nexport function isMfaChallenge(value: unknown): value is MfaChallenge {\n return !!value && typeof value === 'object' && (value as MfaChallenge).mfa_required === true;\n}\n\nconst SESSION_KEY = 'nestcore.session';\n/** Pre-rename key prefix; a session stored under it is migrated on first read. */\nconst LEGACY_SESSION_KEY = 'orbitnest.session';\n/** Refresh the access token this many seconds before it expires. */\nconst REFRESH_MARGIN_S = 30;\nconst NO_SESSION = { data: null, error: { message: 'No active session', code: 'NO_SESSION' } } as const;\n\nexport class AuthClient {\n private _session: AuthSession | null = null;\n private readonly storage: StorageAdapter;\n private readonly storageKey: string;\n private refreshing: Promise<ApiResult<AuthSession>> | null = null;\n private readonly listeners = new Set<(session: AuthSession | null) => void>();\n\n constructor(private client: HttpClient, options: { storage?: StorageAdapter } = {}) {\n // memoryAdapter persists nothing: without an adapter the session stays in\n // this instance only (never shared between SSR requests).\n this.storage = options.storage ?? memoryAdapter;\n // Namespace the persisted session by project so multiple clients on the\n // same origin don't clobber each other.\n this.storageKey = `${SESSION_KEY}.${this.client.getProjectSlug()}`;\n // Rehydrate a previously persisted session (browser refresh).\n let raw = this.storage.getItem(this.storageKey);\n if (raw === null) {\n // Keep users signed in across the OrbitNest → NestCore rename.\n const legacyKey = `${LEGACY_SESSION_KEY}.${this.client.getProjectSlug()}`;\n try {\n raw = this.storage.getItem(legacyKey);\n if (raw !== null) {\n this.storage.setItem(this.storageKey, raw);\n this.storage.removeItem(legacyKey);\n }\n } catch {\n /* storage is best-effort */\n }\n }\n if (raw) {\n try {\n this._session = JSON.parse(raw) as AuthSession;\n } catch {\n this.storage.removeItem(this.storageKey);\n }\n }\n }\n\n // Reading/writing `this.session` transparently persists to the configured\n // storage adapter, so every existing assignment below stays sync-on-write.\n private get session(): AuthSession | null {\n return this._session;\n }\n private set session(value: AuthSession | null) {\n this._session = withExpiry(value);\n try {\n if (value) this.storage.setItem(this.storageKey, JSON.stringify(this._session));\n else this.storage.removeItem(this.storageKey);\n } catch {\n /* storage is best-effort */\n }\n for (const cb of this.listeners) {\n try { cb(this._session); } catch { /* listener errors must not break auth */ }\n }\n }\n\n /**\n * Listen for session changes (sign-in, token refresh, sign-out). Returns an\n * unsubscribe function.\n */\n onAuthStateChange(callback: (session: AuthSession | null) => void): () => void {\n this.listeners.add(callback);\n return () => { this.listeners.delete(callback); };\n }\n\n /**\n * The current access token, refreshed first when it is about to expire.\n * Returns null when signed out or when the server rejected the refresh. A\n * transient refresh failure keeps the old token, so the request fails with\n * 401 instead of silently running as the anonymous role.\n */\n async getAccessToken(): Promise<string | null> {\n const s = this.session;\n if (!s?.access_token) return null;\n if (s.expires_at && s.expires_at - REFRESH_MARGIN_S <= Date.now() / 1000) {\n const refreshed = await this.refreshSession();\n return refreshed.data?.access_token ?? this.session?.access_token ?? null;\n }\n return s.access_token;\n }\n\n /**\n * Called after the API rejected `staleToken` (401): refresh once and return\n * the new access token, or null when the session can't be renewed. Calls\n * that raced an earlier refresh just get the current token.\n */\n async refreshAccessToken(staleToken: string): Promise<string | null> {\n const current = this.session?.access_token;\n if (!current) return null;\n if (current !== staleToken) return current;\n return (await this.refreshSession()).data?.access_token ?? null;\n }\n\n private async withToken<T>(fn: (token: string) => Promise<ApiResult<T>>): Promise<ApiResult<T>> {\n const token = await this.getAccessToken();\n return token ? fn(token) : NO_SESSION;\n }\n\n private get basePath(): string {\n return `/api/projects/${this.client.getProjectSlug()}/auth`;\n }\n\n /**\n * Get the current session\n */\n getSession(): AuthSession | null {\n return this.session;\n }\n\n /**\n * Get the current user\n */\n getUser(): AuthUser | null {\n return this.session?.user || null;\n }\n\n /**\n * Sign up with email and password - Step 1: Request OTP\n * This sends an OTP to the user's email for verification\n */\n async signUp(credentials: SignUpCredentials): Promise<ApiResult<{ success: boolean; message: string; email: string; expires_in: number }>> {\n const result = await this.client.requestPublic<{ success: boolean; message: string; email: string; expires_in: number }>(`${this.basePath}/signup`, {\n method: 'POST',\n body: {\n email: credentials.email,\n password: credentials.password,\n user_metadata: credentials.metadata,\n },\n });\n\n // Note: No session is returned in step 1, only OTP confirmation\n return result;\n }\n\n /**\n * Verify signup with OTP - Step 2: Complete registration\n * This verifies the OTP and creates the user account\n */\n async verifySignUp(options: { email: string; code: string }): Promise<ApiResult<AuthSession>> {\n const result = await this.client.requestPublic<AuthSession>(`${this.basePath}/verify-signup`, {\n method: 'POST',\n body: {\n email: options.email,\n code: options.code,\n },\n });\n\n if (result.data) {\n this.session = result.data;\n }\n\n return result;\n }\n\n /**\n * Sign in with email and password.\n *\n * If the account has a verified MFA factor, the response data is an\n * {@link MfaChallenge} (not a session) — call {@link verifyMfa} with the\n * `challenge_token` and a TOTP code to finish. Use {@link isMfaChallenge} to\n * branch.\n */\n async signIn(credentials: SignInCredentials): Promise<ApiResult<AuthSession | MfaChallenge>> {\n const result = await this.client.requestPublic<AuthSession | MfaChallenge>(`${this.basePath}/signin`, {\n method: 'POST',\n body: {\n email: credentials.email,\n password: credentials.password,\n },\n });\n\n // Only persist a real session — an MFA challenge isn't one yet.\n if (result.data && !isMfaChallenge(result.data)) {\n this.session = result.data as AuthSession;\n }\n\n return result;\n }\n\n /** Complete an MFA-gated sign-in with the challenge token and a TOTP code. */\n async verifyMfa(challengeToken: string, code: string): Promise<ApiResult<AuthSession>> {\n const result = await this.client.requestPublic<AuthSession>(`${this.basePath}/verify-mfa`, {\n method: 'POST',\n body: { challenge_token: challengeToken, code },\n });\n if (result.data) {\n this.session = result.data;\n }\n return result;\n }\n\n // ─── SMS OTP (phone sign-in; the project must have Twilio configured) ──\n\n /**\n * Send a one-time code to a phone number (E.164, e.g. `+15555550123`).\n * Rate limited per phone: a 429 error has code `SMS_RATE_LIMITED`.\n */\n async signInWithSms(phone: string): Promise<ApiResult<{ success: boolean; message: string; expires_in: number }>> {\n return this.client.requestPublic(`${this.basePath}/sms/send-otp`, { method: 'POST', body: { phone } });\n }\n\n /**\n * Verify a phone OTP and sign in (creates the user on first sign-in).\n * After 5 wrong codes a 429 error has code `OTP_RATE_LIMITED`.\n */\n async verifySmsOtp(phone: string, code: string): Promise<ApiResult<AuthSession>> {\n const result = await this.client.requestPublic<AuthSession>(`${this.basePath}/sms/verify-otp`, {\n method: 'POST',\n body: { phone, code },\n });\n if (result.data) {\n this.session = result.data;\n }\n return result;\n }\n\n /**\n * Sign out the current user\n */\n async signOut(): Promise<ApiResult<{ success: boolean }>> {\n const token = await this.getAccessToken();\n const result: ApiResult<{ success: boolean }> = token\n ? await this.client.requestWithToken<{ success: boolean }>(`${this.basePath}/signout`, token, { method: 'POST' })\n : { data: { success: true }, error: null };\n\n this.session = null;\n return result;\n }\n\n /**\n * Refresh the current session\n */\n async refreshSession(): Promise<ApiResult<AuthSession>> {\n const refreshToken = this.session?.refresh_token;\n if (!refreshToken) {\n return {\n data: null,\n error: { message: 'No refresh token available', code: 'NO_SESSION' },\n };\n }\n\n // Refresh tokens rotate: concurrent callers share one request instead of\n // replaying the same (then revoked) token.\n this.refreshing ??= this.client\n .requestPublic<AuthSession>(`${this.basePath}/refresh`, {\n method: 'POST',\n body: { refresh_token: refreshToken },\n })\n .then((result) => {\n if (result.data) this.session = result.data;\n // Rejected refresh token: the session is dead, so stop reporting it.\n else if (result.error.status === 401) this.session = null;\n return result;\n })\n .finally(() => { this.refreshing = null; });\n return this.refreshing;\n }\n\n /**\n * Send password recovery email\n */\n async resetPasswordForEmail(options: PasswordRecoveryOptions): Promise<ApiResult<{ success: boolean }>> {\n return this.client.requestPublic<{ success: boolean }>(`${this.basePath}/recover-password`, {\n method: 'POST',\n body: { email: options.email },\n });\n }\n\n /**\n * Reset password with the OTP from the recovery email.\n *\n * The API body is `{ email, code, new_password }` — we keep the SDK\n * surface (`token`, `password`) readable and map to the server names.\n */\n async updatePassword(options: PasswordResetOptions): Promise<ApiResult<{ success: boolean }>> {\n return this.client.requestPublic<{ success: boolean }>(`${this.basePath}/reset-password`, {\n method: 'POST',\n body: {\n email: options.email,\n code: options.token,\n new_password: options.password,\n },\n });\n }\n\n /**\n * Get current user profile\n */\n async getProfile(): Promise<ApiResult<AuthUser>> {\n return this.withToken((token) => this.client.requestWithToken<AuthUser>(`${this.basePath}/user`, token));\n }\n\n /**\n * Update the signed-in user. `metadata` is merged into `user_metadata`.\n * Changing `email` or `password` requires `current_password` (accounts\n * without a password need a sign-in under 10 minutes old instead); otherwise\n * the API answers 401 with code `REAUTH_REQUIRED`. A new `email` is not\n * applied yet: a code is sent to it and the returned user carries\n * `new_email` until {@link verifyEmailChange} succeeds.\n */\n async updateUser(options: UpdateUserOptions): Promise<ApiResult<AuthUser>> {\n const { metadata, ...rest } = options;\n const result = await this.withToken((token) =>\n this.client.requestWithToken<{ user: AuthUser }>(`${this.basePath}/user`, token, {\n method: 'PUT',\n body: metadata === undefined ? rest : { ...rest, data: metadata },\n }),\n );\n if (result.error) return result;\n\n const user = result.data.user;\n if (this.session && user) this.session = { ...this.session, user };\n return { data: user, error: null };\n }\n\n /**\n * Confirm a pending email change with the code sent to the new address.\n * The access token keeps the old `email` claim until the next refresh.\n */\n async verifyEmailChange(code: string): Promise<ApiResult<AuthUser>> {\n const result = await this.withToken((token) =>\n this.client.requestWithToken<{ user: AuthUser }>(`${this.basePath}/user/email/verify`, token, {\n method: 'POST',\n body: { code },\n }),\n );\n if (result.error) return result;\n const user = result.data.user;\n if (this.session && user) this.session = { ...this.session, user };\n return { data: user, error: null };\n }\n\n /**\n * Delete current user account\n */\n async deleteUser(): Promise<ApiResult<{ success: boolean }>> {\n const result = await this.withToken((token) =>\n this.client.requestWithToken<{ success: boolean }>(`${this.basePath}/user`, token, { method: 'DELETE' }),\n );\n\n if (result.data) {\n this.session = null;\n }\n\n return result;\n }\n\n /**\n * Set session manually (e.g., from stored tokens)\n */\n setSession(session: AuthSession): void {\n this.session = session;\n }\n\n // ─── Passkey / WebAuthn ───────────────────────────────────────────────\n // Runtime-agnostic — consumers drive `navigator.credentials.*` themselves\n // (e.g. via @simplewebauthn/browser) and pass the result to the verify\n // methods below.\n\n async registerPasskeyOptions(\n opts: { device_name?: string } = {},\n ): Promise<ApiResult<PasskeyRegistrationOptions>> {\n return this.withToken((token) => this.client.requestWithToken<PasskeyRegistrationOptions>(\n `${this.basePath}/passkey/register/options`,\n token,\n { method: 'POST', body: { device_name: opts.device_name } },\n ));\n }\n\n async registerPasskeyVerify(\n input: PasskeyRegisterVerifyOptions,\n ): Promise<ApiResult<PasskeyDevice>> {\n return this.withToken((token) => this.client.requestWithToken<PasskeyDevice>(\n `${this.basePath}/passkey/register/verify`,\n token,\n { method: 'POST', body: input },\n ));\n }\n\n /**\n * Begin a passkey sign-up ceremony. Creates a passwordless user record\n * server-side and returns attestation options. If an account already\n * exists with the supplied email the server responds with 409 — route\n * the user to sign-in (then `registerPasskeyOptions` to add a passkey).\n */\n async signUpWithPasskeyOptions(\n opts: PasskeySignUpOptions,\n ): Promise<ApiResult<PasskeyRegistrationOptions>> {\n return this.client.requestPublic<PasskeyRegistrationOptions>(\n `${this.basePath}/passkey/signup/options`,\n { method: 'POST', body: opts },\n );\n }\n\n /**\n * Complete a passkey sign-up ceremony. On success the SDK stores the\n * freshly issued session so subsequent calls act as the new user.\n */\n async signUpWithPasskeyVerify(\n input: PasskeySignUpVerifyOptions,\n ): Promise<ApiResult<AuthSession>> {\n const result = await this.client.requestPublic<AuthSession>(\n `${this.basePath}/passkey/signup/verify`,\n { method: 'POST', body: input },\n );\n if (result.data) {\n this.session = result.data;\n }\n return result;\n }\n\n async signInWithPasskeyOptions(\n opts: { identifier?: string } = {},\n ): Promise<ApiResult<PasskeyAuthenticationOptions>> {\n return this.client.requestPublic<PasskeyAuthenticationOptions>(\n `${this.basePath}/passkey/login/options`,\n { method: 'POST', body: opts.identifier ? { identifier: opts.identifier } : {} },\n );\n }\n\n async signInWithPasskeyVerify(\n input: PasskeyAuthenticateVerifyOptions,\n ): Promise<ApiResult<AuthSession>> {\n const result = await this.client.requestPublic<AuthSession>(\n `${this.basePath}/passkey/login/verify`,\n { method: 'POST', body: input },\n );\n if (result.data) {\n this.session = result.data;\n }\n return result;\n }\n\n async listPasskeys(): Promise<ApiResult<PasskeyDevice[]>> {\n return this.withToken((token) => this.client.requestWithToken<PasskeyDevice[]>(\n `${this.basePath}/passkey/devices`,\n token,\n ));\n }\n\n async renamePasskey(id: string, deviceName: string): Promise<ApiResult<PasskeyDevice>> {\n return this.withToken((token) => this.client.requestWithToken<PasskeyDevice>(\n `${this.basePath}/passkey/devices/${encodeURIComponent(id)}`,\n token,\n { method: 'PATCH', body: { device_name: deviceName } },\n ));\n }\n\n async revokePasskey(id: string): Promise<ApiResult<{ success: boolean }>> {\n return this.withToken((token) => this.client.requestWithToken<{ success: boolean }>(\n `${this.basePath}/passkey/devices/${encodeURIComponent(id)}`,\n token,\n { method: 'DELETE' },\n ));\n }\n\n // ─── MFA (TOTP / authenticator apps) ──────────────────────────────────\n\n /** Begin TOTP enrollment. `code` (TOTP or recovery code) is required when a verified factor already exists. */\n async enrollMfaTotp(\n friendlyName?: string,\n code?: string,\n ): Promise<ApiResult<{ factor_id: string; secret: string; otpauth_url: string; qr_code: string }>> {\n return this.withToken((token) => this.client.requestWithToken<{ factor_id: string; secret: string; otpauth_url: string; qr_code: string }>(\n `${this.basePath}/mfa/totp/enroll`,\n token,\n { method: 'POST', body: { friendly_name: friendlyName, code } },\n ));\n }\n\n /** Confirm enrollment with the first code. On success returns the user's\n * single-use `recovery_codes` (shown once — store them safely). */\n async verifyMfaEnrollment(\n factorId: string,\n code: string,\n ): Promise<ApiResult<{ success: boolean; recovery_codes: string[] }>> {\n return this.withToken((token) => this.client.requestWithToken<{ success: boolean; recovery_codes: string[] }>(\n `${this.basePath}/mfa/totp/verify`,\n token,\n { method: 'POST', body: { factor_id: factorId, code } },\n ));\n }\n\n /** Regenerate the user's MFA recovery codes — returns the new set once.\n * `code` (TOTP or recovery code) is required while a verified factor exists. */\n async regenerateMfaRecoveryCodes(code?: string): Promise<ApiResult<{ codes: string[] }>> {\n return this.withToken((token) => this.client.requestWithToken<{ codes: string[] }>(\n `${this.basePath}/mfa/recovery-codes`,\n token,\n { method: 'POST', body: { code } },\n ));\n }\n\n async listMfaFactors(): Promise<ApiResult<MfaFactor[]>> {\n return this.withToken((token) => this.client.requestWithToken<MfaFactor[]>(\n `${this.basePath}/mfa/factors`,\n token,\n ));\n }\n\n /** Remove an MFA factor. `code` (TOTP or recovery code) is required while a verified factor exists. */\n async unenrollMfa(factorId: string, code?: string): Promise<ApiResult<{ success: boolean }>> {\n return this.withToken((token) => this.client.requestWithToken<{ success: boolean }>(\n `${this.basePath}/mfa/factors/${encodeURIComponent(factorId)}`,\n token,\n { method: 'DELETE', body: { code } },\n ));\n }\n}\n\n/** Stamp `expires_at` (unix seconds) from `expires_in` so expiry survives a reload. */\nfunction withExpiry(session: AuthSession | null): AuthSession | null {\n if (!session || session.expires_at || !session.expires_in) return session;\n return { ...session, expires_at: Math.floor(Date.now() / 1000) + session.expires_in };\n}\n","import { adminOnlyError, type HttpClient } from './client';\nimport type { ApiResult } from '../types';\n\nexport interface LogEntry {\n id: string;\n timestamp: string;\n level: 'debug' | 'info' | 'warn' | 'error';\n message: string;\n metadata?: Record<string, unknown>;\n source?: string;\n}\n\nexport interface LogQueryOptions {\n level?: 'debug' | 'info' | 'warn' | 'error';\n since?: Date;\n until?: Date;\n limit?: number;\n offset?: number;\n source?: string;\n}\n\n/**\n * @deprecated Not usable with a project API key: these routes need a NestCore console\n * admin token. Every method resolves to an `ADMIN_ONLY` error without calling\n * the API. Use the NestCore console, the CLI or the MCP server instead.\n */\nexport class LoggingClient {\n constructor(private client: HttpClient) {}\n\n /**\n * Get all logs\n */\n async getLogs(options: LogQueryOptions = {}): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n\n /**\n * Get database logs\n */\n async getDatabaseLogs(options: LogQueryOptions = {}): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n\n /**\n * Get slow query logs\n */\n async getSlowQueryLogs(options: LogQueryOptions = {}): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n\n /**\n * Get auth logs\n */\n async getAuthLogs(options: LogQueryOptions = {}): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n\n /**\n * Get auth failure logs\n */\n async getAuthFailures(options: LogQueryOptions = {}): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n\n /**\n * Get edge function logs\n */\n async getEdgeFunctionLogs(\n functionName?: string,\n options: LogQueryOptions = {}\n ): Promise<ApiResult<LogEntry[]>> {\n return adminOnlyError('client.logs');\n }\n}\n","import { adminOnlyError, type HttpClient } from './client';\nimport type { ApiResult } from '../types';\n\nexport interface EnvironmentVariable {\n name: string;\n value: string;\n description?: string;\n isSecret?: boolean;\n createdAt?: string;\n updatedAt?: string;\n}\n\n/**\n * @deprecated Not usable with a project API key: these routes need a NestCore console\n * admin token. Every method resolves to an `ADMIN_ONLY` error without calling\n * the API. Use the NestCore console, the CLI or the MCP server instead.\n */\nexport class EnvironmentClient {\n constructor(private client: HttpClient) {}\n\n /**\n * List all environment variables\n */\n async list(): Promise<ApiResult<EnvironmentVariable[]>> {\n return adminOnlyError('client.env');\n }\n\n /**\n * Get a specific environment variable\n */\n async get(name: string): Promise<ApiResult<EnvironmentVariable>> {\n return adminOnlyError('client.env');\n }\n\n /**\n * Create or update an environment variable\n */\n async set(\n name: string,\n value: string,\n options: { description?: string; isSecret?: boolean } = {}\n ): Promise<ApiResult<EnvironmentVariable>> {\n return adminOnlyError('client.env');\n }\n\n /**\n * Create a new environment variable\n */\n async create(\n name: string,\n value: string,\n options: { description?: string; isSecret?: boolean } = {}\n ): Promise<ApiResult<EnvironmentVariable>> {\n return adminOnlyError('client.env');\n }\n\n /**\n * Bulk create environment variables\n */\n async bulkCreate(\n variables: Array<{ name: string; value: string; description?: string; isSecret?: boolean }>\n ): Promise<ApiResult<EnvironmentVariable[]>> {\n return adminOnlyError('client.env');\n }\n\n /**\n * Delete an environment variable\n */\n async delete(name: string): Promise<ApiResult<{ success: boolean }>> {\n return adminOnlyError('client.env');\n }\n}\n","import type { ApiResult } from '../types';\n\n/**\n * Validate a required string parameter. Returns an error result if invalid.\n */\nexport function validateRequired(value: unknown, paramName: string): ApiResult<never> | null {\n if (!value || typeof value !== 'string' || value.trim().length === 0) {\n return {\n data: null,\n error: { message: `${paramName} is required and must be a non-empty string`, code: 'VALIDATION_ERROR' },\n };\n }\n return null;\n}\n\n/**\n * Validate a file/storage path. Returns an error result if invalid.\n * Rejects path traversal attempts (../) and null bytes.\n */\nexport function validatePath(path: string, paramName: string = 'path'): ApiResult<never> | null {\n const required = validateRequired(path, paramName);\n if (required) return required;\n\n // Block null bytes\n if (path.includes('\\0')) {\n return {\n data: null,\n error: { message: `${paramName} must not contain null bytes`, code: 'VALIDATION_ERROR' },\n };\n }\n\n // Block path traversal\n const segments = path.split('/');\n for (const seg of segments) {\n if (seg === '..') {\n return {\n data: null,\n error: { message: `${paramName} must not contain path traversal sequences`, code: 'VALIDATION_ERROR' },\n };\n }\n }\n\n return null;\n}\n\n/**\n * Sanitize a storage path for FormData submission.\n * Normalizes separators and strips leading slashes / traversal.\n */\nexport function sanitizePath(path: string): string {\n return path\n .replace(/\\\\/g, '/') // normalize backslashes\n .split('/')\n .filter(seg => seg !== '..' && seg !== '' && seg !== '.')\n .join('/');\n}\n","import { errorMessage, type HttpClient } from './client';\nimport type { ApiResult } from '../types';\nimport { validatePath } from './validation';\n\n/**\n * Encode a storage path for safe interpolation into a URL while preserving the\n * `/` separators that denote nested paths (e.g. `avatars/user1.png`). Each\n * segment is `encodeURIComponent`-encoded so `?`, `#`, spaces, etc. inside a\n * segment can't alter URL structure. Empty segments (from `//` or leading `/`)\n * are dropped. Use only after `validatePath()` has rejected traversal/null bytes.\n */\nfunction encodeStoragePath(path: string): string {\n return path\n .split('/')\n .filter((seg) => seg.length > 0)\n .map((seg) => encodeURIComponent(seg))\n .join('/');\n}\n\nexport interface StorageFile {\n id: string;\n bucket: string;\n path: string;\n filename: string;\n originalName: string;\n mimeType: string;\n size: number;\n url: string;\n createdAt: string;\n}\n\n/** On-the-fly image transform options for {@link StorageBucket.getPublicUrl}. */\nexport interface ImageTransformOptions {\n width?: number;\n height?: number;\n format?: 'webp' | 'jpeg' | 'png' | 'avif';\n quality?: number;\n fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';\n}\n\n/** Serialize transform options into a query string (omitting empty values). */\nfunction buildTransformQuery(t?: ImageTransformOptions): string {\n if (!t) return '';\n const p = new URLSearchParams();\n if (t.width) p.set('width', String(t.width));\n if (t.height) p.set('height', String(t.height));\n if (t.format) p.set('format', t.format);\n if (t.quality) p.set('quality', String(t.quality));\n if (t.fit) p.set('fit', t.fit);\n return p.toString();\n}\n\nexport interface StorageListItem {\n name: string;\n size: number;\n createdAt: string;\n}\n\nexport interface UploadOptions {\n /** @deprecated Not supported by the API; ignored. */\n cacheControl?: string;\n upsert?: boolean;\n /**\n * MIME type stored with the file. Defaults to the Blob's own type, else a\n * guess from the path's extension, else `application/octet-stream`.\n */\n contentType?: string;\n}\n\n/** Common extensions → MIME type, so Buffer/ArrayBuffer uploads aren't stored as octet-stream. */\nconst MIME_BY_EXT: Record<string, string> = {\n png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp',\n avif: 'image/avif', svg: 'image/svg+xml', ico: 'image/x-icon', pdf: 'application/pdf',\n json: 'application/json', txt: 'text/plain', csv: 'text/csv', html: 'text/html', css: 'text/css',\n js: 'text/javascript', mp4: 'video/mp4', webm: 'video/webm', mp3: 'audio/mpeg', wav: 'audio/wav',\n zip: 'application/zip',\n};\n\nfunction guessContentType(path: string): string | undefined {\n const ext = path.split('.').pop()?.toLowerCase() ?? '';\n return MIME_BY_EXT[ext];\n}\n\n/** The controller wraps results as `{ data, error: null }`; return the inner payload. */\nfunction unwrapEnvelope<T>(result: ApiResult<unknown>): ApiResult<T> {\n if (result.error) return result;\n const body = result.data as { data?: T } | null;\n return { data: (body?.data ?? body) as T, error: null };\n}\n\nexport class StorageClient {\n constructor(private client: HttpClient) {}\n\n /**\n * Get a bucket client for performing operations\n */\n from(bucket: string): StorageBucket {\n return new StorageBucket(this.client, bucket);\n }\n}\n\nexport class StorageBucket {\n constructor(\n private client: HttpClient,\n private bucket: string\n ) {}\n\n // The project-apiKey-authenticated client storage controller lives at the\n // singular `/api/project/:slug/storage/...` mount (StorageAuthGuard). The\n // plural `/api/projects/...` route is the studio admin controller and\n // requires a studio JWT, not a project apiKey — so the SDK uses singular.\n private get basePath(): string {\n return `/api/project/${this.client.getProjectSlug()}/storage/${this.bucket}`;\n }\n\n /** Absolute URL for a path relative to the configured baseUrl. */\n private absoluteUrl(path: string): string {\n return `${this.client.getBaseUrl()}${path}`;\n }\n\n /**\n * Upload a file to the bucket.\n *\n * Posts multipart/form-data to `POST /api/project/{slug}/storage/{bucket}/upload`\n * as the signed-in user when there is a session (the project key otherwise).\n * Sends fields:\n * - `file` binary\n * - `path` destination path inside the bucket\n * - `upsert` \"true\" if overwriting (optional)\n */\n async upload(\n path: string,\n file: Blob | File | ArrayBuffer | Uint8Array,\n options?: UploadOptions\n ): Promise<ApiResult<StorageFile>> {\n // Reject path traversal (`..`) and null bytes before the path reaches the\n // server (it is sent as the `path` form field below).\n const invalid = validatePath(path);\n if (invalid) return invalid as ApiResult<StorageFile>;\n\n const formData = new FormData();\n\n // Normalize to a typed Blob. `File`/`Blob`, `ArrayBuffer`, typed arrays\n // and Node `Buffer`s (a `Uint8Array`) are all valid `BlobPart`s — no\n // Node-only global is referenced, so this is safe in the browser. The type\n // becomes the part's Content-Type, which the API stores as the MIME type.\n const type = options?.contentType || (file instanceof Blob && file.type) || guessContentType(path) || 'application/octet-stream';\n const blob = new Blob([file as BlobPart], { type });\n\n formData.append('file', blob, path.split('/').pop() || 'file');\n formData.append('path', path);\n if (options?.upsert) {\n formData.append('upsert', 'true');\n }\n\n // Storage upload bypasses the JSON HttpClient helper because it sends\n // multipart/form-data, not JSON. We still must use the absolute URL\n // (baseUrl + basePath) — Node's fetch rejects relative URLs.\n const url = this.absoluteUrl(`${this.basePath}/upload`);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30000);\n\n try {\n const response = await this.client.authFetch(url, {\n method: 'POST',\n body: formData,\n // Fail loudly on unexpected redirects rather than silently forwarding\n // the Authorization header to the redirect target.\n redirect: 'error',\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n return {\n data: null,\n error: {\n message: errorMessage(data, response.status, 'Upload failed'),\n code: data?.code,\n status: response.status,\n details: data ?? undefined,\n },\n };\n }\n\n // Controller wraps the result as `{ data, error: null }` — unwrap to\n // the StorageFile payload to match the rest of the SDK's return shape.\n return { data: (data?.data ?? data) as StorageFile, error: null };\n } catch (err) {\n clearTimeout(timeoutId);\n if (err instanceof Error && err.name === 'AbortError') {\n return { data: null, error: { message: 'Upload timeout', code: 'TIMEOUT' } };\n }\n return {\n data: null,\n error: {\n message: err instanceof Error ? err.message : 'Upload failed',\n code: 'UPLOAD_ERROR',\n },\n };\n }\n }\n\n /**\n * Download a file from the bucket.\n *\n * Calls `GET /api/project/{slug}/storage/{bucket}/download/{path}` and\n * returns the response body as a Blob.\n */\n async download(path: string): Promise<ApiResult<Blob>> {\n const invalid = validatePath(path);\n if (invalid) return invalid as ApiResult<Blob>;\n\n // Encode each segment (preserving `/` separators) so the path can't inject\n // URL syntax or traverse outside the bucket.\n const url = this.absoluteUrl(`${this.basePath}/download/${encodeStoragePath(path)}`);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30000);\n\n try {\n const response = await this.client.authFetch(url, {\n // Fail loudly on unexpected redirects rather than silently forwarding\n // the Authorization header to the redirect target.\n redirect: 'error',\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const body = await response.json().catch(() => null);\n return {\n data: null,\n error: {\n message: errorMessage(body, response.status, 'Download failed'),\n code: body?.code,\n status: response.status,\n details: body ?? undefined,\n },\n };\n }\n\n const blob = await response.blob();\n return { data: blob, error: null };\n } catch (err) {\n clearTimeout(timeoutId);\n if (err instanceof Error && err.name === 'AbortError') {\n return { data: null, error: { message: 'Download timeout', code: 'TIMEOUT' } };\n }\n return {\n data: null,\n error: {\n message: err instanceof Error ? err.message : 'Download failed',\n code: 'DOWNLOAD_ERROR',\n },\n };\n }\n }\n\n /**\n * Delete files from the bucket\n */\n async remove(paths: string[]): Promise<ApiResult<{ deleted: string[]; errors: string[] }>> {\n // Reject path traversal (`..`) and null bytes on every path before they\n // reach the server (paths are sent in the request body).\n for (const path of paths) {\n const invalid = validatePath(path);\n if (invalid) return invalid as ApiResult<{ deleted: string[]; errors: string[] }>;\n }\n return unwrapEnvelope(await this.client.request(this.basePath, {\n method: 'DELETE',\n body: { paths },\n }));\n }\n\n /**\n * List files in the bucket\n */\n async list(\n prefix?: string,\n options?: { limit?: number }\n ): Promise<ApiResult<StorageListItem[]>> {\n const params = new URLSearchParams();\n if (prefix) params.set('prefix', prefix);\n if (options?.limit) params.set('limit', options.limit.toString());\n\n const query = params.toString() ? `?${params.toString()}` : '';\n return unwrapEnvelope(await this.client.request(`${this.basePath}${query}`));\n }\n\n /**\n * Get the public URL for a file. Routes through the anonymous public-storage\n * controller mounted at `/api/public/:slug/storage/:bucket/*path` — no\n * Authorization header is required. Use this for files in public buckets.\n *\n * Pass `transform` to request an on-the-fly image rendition (resize/format):\n *\n * ```ts\n * storage.from('avatars').getPublicUrl('u/1.png', {\n * transform: { width: 128, height: 128, format: 'webp', quality: 80 },\n * });\n * ```\n */\n getPublicUrl(path: string, options?: { transform?: ImageTransformOptions }): { data: { publicUrl: string } } {\n // This method is synchronous and can't return an ApiResult error, so a\n // traversal/null-byte attempt throws (it's a caller bug or attack, not a\n // recoverable runtime error).\n const invalid = validatePath(path);\n if (invalid) {\n throw new Error(`NestCore: ${invalid.error?.message ?? 'invalid storage path'}`);\n }\n\n const baseUrl = this.client.getBaseUrl();\n const projectSlug = this.client.getProjectSlug();\n // Encode each path segment (preserving `/` separators) so the path can't\n // inject URL syntax or traverse outside the bucket.\n let publicUrl = `${baseUrl}/api/public/${projectSlug}/storage/${this.bucket}/${encodeStoragePath(path)}`;\n\n const qs = buildTransformQuery(options?.transform);\n if (qs) publicUrl += `?${qs}`;\n\n return {\n data: { publicUrl },\n };\n }\n\n /**\n * Create the bucket if it doesn't exist (requires a service_role key).\n * `public` is sent only when given, so an \"ensure bucket\" call leaves an\n * existing bucket's visibility unchanged; new buckets default to private.\n */\n async createBucket(\n options: { public?: boolean } = {},\n ): Promise<ApiResult<{ bucket: string; created: boolean }>> {\n return unwrapEnvelope(await this.client.request(\n `${this.basePath}/create`,\n { method: 'POST', body: options.public === undefined ? undefined : { public: options.public } }\n ));\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\r\n// NestCore Realtime — Node SDK\r\n// Provides a Supabase-compatible channel API over a native WebSocket.\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type RealtimeStatus = 'connecting' | 'connected' | 'disconnected' | 'error';\r\n\r\n// ─── Presence types ────────────────────────────────────────────────────────────\r\n\r\nexport type PresenceState = Record<string, any[]>;\r\nexport type PresenceSyncCallback = (state: PresenceState) => void;\r\nexport type PresenceJoinCallback = (key: string, presences: any[]) => void;\r\nexport type PresenceLeaveCallback = (key: string, presences: any[]) => void;\r\n\r\nexport type PgEvent = 'INSERT' | 'UPDATE' | 'DELETE' | '*';\r\n\r\nexport interface PgChangesPayload {\r\n op: 'INSERT' | 'UPDATE' | 'DELETE';\r\n schema: string;\r\n table: string;\r\n new?: Record<string, unknown>;\r\n old?: Record<string, unknown>;\r\n commit_ts: string;\r\n}\r\n\r\nexport interface BroadcastPayload {\r\n event: string;\r\n payload: unknown;\r\n}\r\n\r\nexport interface PgChangesFilter {\r\n event: PgEvent | PgEvent[];\r\n schema?: string;\r\n table: string;\r\n filter?: Record<string, FilterValue>;\r\n}\r\n\r\nexport interface FilterValue {\r\n eq?: string | number;\r\n neq?: string | number;\r\n in?: Array<string | number>;\r\n gt?: number | string;\r\n lt?: number | string;\r\n like?: string;\r\n}\r\n\r\ntype PgCallback = (payload: PgChangesPayload) => void;\r\ntype BroadcastCallback = (payload: BroadcastPayload) => void;\r\ntype StatusCallback = (status: RealtimeStatus, err?: Error) => void;\r\n\r\ninterface ChannelHandler {\r\n type: 'postgres_changes' | 'broadcast';\r\n opts: PgChangesFilter | { event: string };\r\n callback: PgCallback | BroadcastCallback;\r\n}\r\n\r\n// ─── PresenceManager ─────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Presence API attached to a broadcast channel. Tracks who is currently\r\n * online and fires join/leave callbacks when the roster changes.\r\n *\r\n * ```ts\r\n * const ch = client.channel('room')\r\n * ch.presence\r\n * .onSync((state) => console.log('roster', state))\r\n * .onJoin((key, list) => console.log('joined', key, list))\r\n * .onLeave((key, list) => console.log('left', key, list))\r\n * await ch.subscribe()\r\n * await ch.presence.track({ name: 'Alice' })\r\n * ```\r\n */\r\nexport class PresenceManager {\r\n private state: PresenceState = {};\r\n private syncCbs: PresenceSyncCallback[] = [];\r\n private joinCbs: PresenceJoinCallback[] = [];\r\n private leaveCbs: PresenceLeaveCallback[] = [];\r\n\r\n constructor(\r\n private readonly channelName: string,\r\n private readonly client: RealtimeClient,\r\n ) {}\r\n\r\n /** Send presence state to the server; triggers a `presence.state` fan-out. */\r\n async track(state: Record<string, any>): Promise<void> {\r\n await this.client['sendPresenceTrack'](this.channelName, state);\r\n }\r\n\r\n /**\r\n * Stop tracking presence on this channel. Other clients will see the\r\n * departure in their next `presence.state` message.\r\n */\r\n async untrack(): Promise<void> {\r\n this.client['sendPresenceUntrack'](this.channelName);\r\n }\r\n\r\n /** Returns a shallow copy of the last-known presence roster. */\r\n getState(): PresenceState { return { ...this.state }; }\r\n\r\n /** True once any presence callback is registered. */\r\n _hasListeners(): boolean {\r\n return this.syncCbs.length + this.joinCbs.length + this.leaveCbs.length > 0;\r\n }\r\n\r\n /** Register a callback fired on every full roster sync. */\r\n onSync(cb: PresenceSyncCallback): this { this.syncCbs.push(cb); return this; }\r\n\r\n /** Register a callback fired when a new socket joins the channel. */\r\n onJoin(cb: PresenceJoinCallback): this { this.joinCbs.push(cb); return this; }\r\n\r\n /** Register a callback fired when a socket leaves the channel. */\r\n onLeave(cb: PresenceLeaveCallback): this { this.leaveCbs.push(cb); return this; }\r\n\r\n // ─── Internal ───────────────────────────────────────────────────────────────\r\n\r\n _handleStateUpdate(newState: PresenceState): void {\r\n const prev = this.state;\r\n\r\n // Detect joins (keys present in new but not in prev)\r\n for (const [key, presences] of Object.entries(newState)) {\r\n if (!prev[key]) {\r\n for (const cb of this.joinCbs) cb(key, presences);\r\n }\r\n }\r\n\r\n // Detect leaves (keys present in prev but not in new)\r\n for (const [key, presences] of Object.entries(prev)) {\r\n if (!newState[key]) {\r\n for (const cb of this.leaveCbs) cb(key, presences);\r\n }\r\n }\r\n\r\n this.state = { ...newState };\r\n for (const cb of this.syncCbs) cb({ ...this.state });\r\n }\r\n}\r\n\r\n// ─── RealtimeChannel ──────────────────────────────────────────────────────────\r\n\r\nexport class RealtimeChannel {\r\n private handlers: ChannelHandler[] = [];\r\n private statusCb?: StatusCallback;\r\n /** Set by subscribe(); only joined channels hold server subscriptions. */\r\n _joined = false;\r\n\r\n /** Presence API for this channel. */\r\n readonly presence: PresenceManager;\r\n\r\n constructor(\r\n private readonly name: string,\r\n private readonly client: RealtimeClient,\r\n ) {\r\n this.presence = new PresenceManager(name, client);\r\n }\r\n\r\n /**\r\n * Register a handler for Postgres row-level changes.\r\n *\r\n * ```ts\r\n * ch.on('postgres_changes',\r\n * { event: 'INSERT', table: 'orders', filter: { user_id: { eq: 'abc' } } },\r\n * (e) => console.log(e))\r\n * ```\r\n */\r\n on(type: 'postgres_changes', opts: PgChangesFilter, cb: PgCallback): this;\r\n on(type: 'broadcast', opts: { event: string }, cb: BroadcastCallback): this;\r\n on(type: string, opts: any, cb: any): this {\r\n this.handlers.push({ type: type as any, opts, callback: cb });\r\n return this;\r\n }\r\n\r\n /**\r\n * Subscribe. The optional callback receives status transitions.\r\n *\r\n * ```ts\r\n * ch.subscribe((status, err) => console.log(status, err));\r\n * ```\r\n */\r\n subscribe(cb?: StatusCallback): this {\r\n this.statusCb = cb;\r\n this._joined = true;\r\n this.client['registerChannel'](this);\r\n return this;\r\n }\r\n\r\n /** Send a broadcast message on this channel. */\r\n async send(event: string, payload: unknown): Promise<void> {\r\n await this.client['sendBroadcast'](this.name, event, payload);\r\n }\r\n\r\n /** Unsubscribe and remove the channel. */\r\n async unsubscribe(): Promise<void> {\r\n await this.client['unregisterChannel'](this.name);\r\n }\r\n\r\n // ─── Internal ───────────────────────────────────────────────────────────────\r\n\r\n get channelName(): string { return this.name; }\r\n\r\n /** Called by RealtimeClient when the WS is ready — sends subscribe messages. */\r\n _activate(ws: WebSocketLike): void {\r\n for (const key of this._tableKeys()) {\r\n const [schema, table] = splitTableKey(key);\r\n this.client['syncTable'](ws, schema, table);\r\n }\r\n if (this._hasBroadcast()) {\r\n // Subscribe to the broadcast channel (no DB trigger needed)\r\n ws.send(JSON.stringify({\r\n id: `${this.name}-bc-${Math.random().toString(36).slice(2)}`,\r\n type: 'subscribe',\r\n channel: `broadcast:${this.name}`,\r\n }));\r\n }\r\n this.statusCb?.('connected');\r\n }\r\n\r\n /** `schema.table` keys this channel listens on (empty unless joined). */\r\n _tableKeys(): string[] {\r\n if (!this._joined) return [];\r\n const keys = this.handlers\r\n .filter((h) => h.type === 'postgres_changes')\r\n .map((h) => `${(h.opts as PgChangesFilter).schema ?? 'public'}.${(h.opts as PgChangesFilter).table}`);\r\n return [...new Set(keys)];\r\n }\r\n\r\n /** Postgres handler options on one table (empty unless joined). */\r\n _tableHandlers(schema: string, table: string): PgChangesFilter[] {\r\n if (!this._joined) return [];\r\n return this.handlers\r\n .filter((h) => h.type === 'postgres_changes')\r\n .map((h) => h.opts as PgChangesFilter)\r\n .filter((o) => (o.schema ?? 'public') === schema && o.table === table);\r\n }\r\n\r\n /**\r\n * Whether the server must hold `broadcast:<name>` for this channel: broadcast\r\n * handlers, presence listeners, or no table handlers at all (a room used for\r\n * presence only still needs the roster fan-out, including leaves).\r\n */\r\n _hasBroadcast(): boolean {\r\n return this.handlers.some((h) => h.type === 'broadcast')\r\n || this.presence._hasListeners()\r\n || !this.handlers.some((h) => h.type === 'postgres_changes');\r\n }\r\n\r\n _setStatus(status: RealtimeStatus, err?: Error): void {\r\n this.statusCb?.(status, err);\r\n }\r\n\r\n /** Route an inbound server message to matching handlers. */\r\n _dispatch(msg: Record<string, any>): void {\r\n if (msg.type === 'db_event') {\r\n const payload: PgChangesPayload = {\r\n op: msg.op,\r\n schema: msg.schema,\r\n table: msg.table,\r\n new: msg.new,\r\n old: msg.old,\r\n commit_ts: msg.commit_ts,\r\n };\r\n for (const h of this.handlers) {\r\n if (h.type !== 'postgres_changes') continue;\r\n const opts = h.opts as PgChangesFilter;\r\n const events = normaliseEvents(opts.event);\r\n if (!events.includes(msg.op) && !events.includes('*')) continue;\r\n if (opts.table && opts.table !== msg.table) continue;\r\n if (opts.schema && opts.schema !== msg.schema) continue;\r\n // The server keeps one subscription per table, so re-check this\r\n // handler's own filter before delivering.\r\n if (!matchesFilter(msg.op === 'DELETE' ? msg.old : msg.new, opts.filter)) continue;\r\n (h.callback as PgCallback)(payload);\r\n }\r\n } else if (msg.type === 'broadcast') {\r\n const payload: BroadcastPayload = { event: msg.event, payload: msg.payload };\r\n for (const h of this.handlers) {\r\n if (h.type !== 'broadcast') continue;\r\n const opts = h.opts as { event: string };\r\n if (opts.event !== '*' && opts.event !== msg.event) continue;\r\n (h.callback as BroadcastCallback)(payload);\r\n }\r\n } else if (msg.type === 'presence.state') {\r\n this.presence._handleStateUpdate((msg.presences as PresenceState) ?? {});\r\n }\r\n }\r\n}\r\n\r\n// ─── RealtimeClient ───────────────────────────────────────────────────────────\r\n\r\nexport interface RealtimeOptions {\r\n /** Override with a custom WebSocket constructor (e.g. the 'ws' package on\r\n * older Node versions). Defaults to globalThis.WebSocket. */\r\n WebSocket?: WebSocketConstructor;\r\n /** Optional project-user JWT for RLS-scoped subscriptions. */\r\n userJwt?: string;\r\n /**\r\n * Resolves the signed-in user's JWT before each connect (takes precedence\r\n * over `userJwt`). `createClient` wires this to `auth`.\r\n */\r\n accessToken?: () => Promise<string | null>;\r\n /**\r\n * Called once when the server rejects the socket's user JWT (close 4001),\r\n * e.g. to refresh the session; a new token arrives through `setAuth`.\r\n */\r\n onAuthRejected?: (staleToken: string) => void;\r\n /**\r\n * Project API key. Sent on the handshake via a WS subprotocol token and an\r\n * Authorization header — never in the URL query string (it is a credential\r\n * that would otherwise leak into proxy/CDN logs and browser history).\r\n */\r\n apiKey?: string;\r\n}\r\n\r\n// `protocols` covers both browser WebSocket and the `ws` package's\r\n// `(url, protocols)` signature. `options` is the optional third argument that\r\n// the `ws` package accepts for handshake-level config (e.g. custom `headers`);\r\n// browser WebSocket ignores it, so it is best-effort for header transport.\r\ntype WebSocketConstructor = new (\r\n url: string,\r\n protocols?: string | string[],\r\n options?: { headers?: Record<string, string> },\r\n) => WebSocketLike;\r\n\r\ninterface WebSocketLike {\r\n readonly readyState: number;\r\n send(data: string): void;\r\n close(code?: number, reason?: string): void;\r\n onopen: ((ev: any) => any) | null;\r\n onclose: ((ev: any) => any) | null;\r\n onerror: ((ev: any) => any) | null;\r\n onmessage: ((ev: any) => any) | null;\r\n}\r\n\r\nconst CONNECTING = 0;\r\nconst OPEN = 1;\r\n\r\n/** Initial reconnect delay under normal conditions. */\r\nconst BASE_RECONNECT_DELAY_MS = 1_000;\r\n/** Floor delay when the server closes us for rate-limiting / policy reasons. */\r\nconst RATE_LIMITED_RECONNECT_DELAY_MS = 10_000;\r\n/** Absolute upper bound on exponential backoff. */\r\nconst MAX_RECONNECT_DELAY_MS = 60_000;\r\n/** WS close code the gateway uses when a socket is closed for rate limiting. */\r\nconst WS_CLOSE_RATE_LIMITED = 4029;\r\n\r\nexport class RealtimeClient {\r\n private ws: WebSocketLike | null = null;\r\n private channels = new Map<string, RealtimeChannel>();\r\n private reconnectDelay = BASE_RECONNECT_DELAY_MS;\r\n private maxReconnectDelay = MAX_RECONNECT_DELAY_MS;\r\n private shouldReconnect = true;\r\n private connectPromise: Promise<void> | null = null;\r\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\r\n /** User JWT the current socket was opened with. */\r\n private socketJwt: string | undefined;\r\n private authChanged = false;\r\n /** onAuthRejected already fired since the last accepted connect (no refresh loop). */\r\n private authRetried = false;\r\n\r\n constructor(\r\n private readonly url: string,\r\n private readonly options: RealtimeOptions = {},\r\n ) {}\r\n\r\n /** Get or create a channel. Call `.on()` then `.subscribe()` on the result. */\r\n channel(name: string): RealtimeChannel {\r\n if (!this.channels.has(name)) {\r\n this.channels.set(name, new RealtimeChannel(name, this));\r\n }\r\n return this.channels.get(name)!;\r\n }\r\n\r\n /**\r\n * Set the user JWT for RLS-scoped events. Re-opens the socket when it\r\n * differs from the one in use. `createClient` calls this on every auth change.\r\n */\r\n setAuth(token: string | null): void {\r\n if (!this.options.accessToken) this.options.userJwt = token ?? undefined;\r\n if ((token ?? undefined) === this.socketJwt || !this.ws) return;\r\n if (this.ws.readyState === CONNECTING) {\r\n this.authChanged = true; // re-open once the in-flight connect finishes\r\n return;\r\n }\r\n // Open, or closed (e.g. 4001 for an expired JWT): connect with the new one.\r\n this.reopen();\r\n }\r\n\r\n /** Tear down all channels and close the WebSocket. */\r\n disconnect(): void {\r\n this.shouldReconnect = false;\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = null;\r\n }\r\n this.ws?.close(1000, 'client disconnect');\r\n this.ws = null;\r\n this.channels.clear();\r\n }\r\n\r\n // ─── Internal ───────────────────────────────────────────────────────────────\r\n\r\n private connect(): Promise<void> {\r\n if (this.connectPromise) return this.connectPromise;\r\n if (this.ws?.readyState === OPEN) return Promise.resolve();\r\n\r\n this.connectPromise = this.resolveUserJwt().then((userJwt) => new Promise<void>((resolve, reject) => {\r\n const WS: WebSocketConstructor =\r\n this.options.WebSocket ?? (globalThis as any).WebSocket;\r\n\r\n if (!WS) {\r\n reject(new Error(\r\n 'No WebSocket implementation found. ' +\r\n 'On Node.js < 22, pass a WebSocket constructor via options.WebSocket ' +\r\n '(e.g. from the \"ws\" package).',\r\n ));\r\n return;\r\n }\r\n\r\n const protocols: string[] = ['orbitnest-realtime-v1'];\r\n this.socketJwt = userJwt;\r\n if (userJwt) {\r\n protocols.push(`orbitnest.user-jwt.${userJwt}`);\r\n }\r\n // Carry the project API key on the handshake instead of in the URL query\r\n // string (which leaks into proxy/CDN access logs and browser history).\r\n // We send it via BOTH a subprotocol token AND an Authorization header to\r\n // maximize server-side compatibility: subprotocols work in every\r\n // environment (browsers can't set handshake headers), while the\r\n // Authorization header is honored by the `ws` package's handshake options.\r\n let handshakeOptions: { headers?: Record<string, string> } | undefined;\r\n if (this.options.apiKey) {\r\n protocols.push(`orbitnest.apikey.${this.options.apiKey}`);\r\n handshakeOptions = { headers: { Authorization: `Bearer ${this.options.apiKey}` } };\r\n }\r\n\r\n const ws = new WS(this.url, protocols, handshakeOptions);\r\n\r\n ws.onopen = () => {\r\n this.reconnectDelay = BASE_RECONNECT_DELAY_MS;\r\n this.connectPromise = null;\r\n resolve();\r\n if (this.authChanged) {\r\n this.authChanged = false;\r\n this.reopen();\r\n return;\r\n }\r\n // Activate all joined channels\r\n for (const ch of this.channels.values()) if (ch._joined) ch._activate(ws);\r\n };\r\n\r\n ws.onmessage = (ev: any) => {\r\n try {\r\n const msg = JSON.parse(typeof ev.data === 'string' ? ev.data : ev.data.toString());\r\n this.route(msg);\r\n } catch { /* ignore malformed */ }\r\n };\r\n\r\n ws.onerror = (ev: any) => {\r\n const err = new Error(ev?.message ?? 'WebSocket error');\r\n for (const ch of this.channels.values()) ch._setStatus('error', err);\r\n this.connectPromise = null;\r\n reject(err);\r\n };\r\n\r\n ws.onclose = (ev: any) => {\r\n this.connectPromise = null;\r\n for (const ch of this.channels.values()) ch._setStatus('disconnected');\r\n\r\n // Inspect close code to decide backoff floor.\r\n const code: number | undefined = ev?.code;\r\n // 1000 (normal) + 1001 (going away) and auth-failed (4001) → do not reconnect.\r\n if (code === 1000 || code === 1001 || code === 4001) {\r\n this.shouldReconnect = false;\r\n }\r\n // Rejected user JWT (expired/revoked/clock skew): refresh once; the new\r\n // token reopens the socket via setAuth.\r\n if (code === 4001 && this.socketJwt && !this.authRetried && this.options.onAuthRejected) {\r\n this.authRetried = true;\r\n this.options.onAuthRejected(this.socketJwt);\r\n }\r\n\r\n if (this.shouldReconnect) {\r\n // If the server closed us for rate limiting / policy violation, jump to\r\n // a much longer base delay so we don't create a reconnect storm.\r\n if (\r\n code === WS_CLOSE_RATE_LIMITED ||\r\n code === 1008 ||\r\n code === 1013\r\n ) {\r\n this.reconnectDelay = Math.max(\r\n this.reconnectDelay,\r\n RATE_LIMITED_RECONNECT_DELAY_MS,\r\n );\r\n }\r\n\r\n // Add small jitter so many clients don't all reconnect at the same ms.\r\n const jitter = Math.floor(Math.random() * 500);\r\n const delay = Math.min(this.reconnectDelay + jitter, this.maxReconnectDelay);\r\n if (this.reconnectTimer) clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = setTimeout(() => this.reconnect(), delay);\r\n this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);\r\n }\r\n };\r\n\r\n this.ws = ws;\r\n }));\r\n\r\n return this.connectPromise;\r\n }\r\n\r\n private async resolveUserJwt(): Promise<string | undefined> {\r\n if (!this.options.accessToken) return this.options.userJwt;\r\n try {\r\n return (await this.options.accessToken()) ?? undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n }\r\n\r\n /** Close the socket silently and open a new one (picks up the current JWT). */\r\n private reopen(): void {\r\n const ws = this.ws;\r\n if (ws) {\r\n ws.onclose = null;\r\n ws.onerror = null;\r\n ws.onmessage = null;\r\n ws.close(1000, 'auth changed');\r\n }\r\n this.ws = null;\r\n this.connectPromise = null;\r\n if ([...this.channels.values()].some((c) => c._joined)) {\r\n this.shouldReconnect = true;\r\n this.connect().catch(() => {});\r\n }\r\n }\r\n\r\n /**\r\n * The server keeps one subscription per table per socket (last subscribe\r\n * wins), so send the union of events of every handler on the table, and a\r\n * filter only when they all share the same one. Handlers re-check their own\r\n * filters client-side.\r\n */\r\n private syncTable(ws: WebSocketLike, schema: string, table: string): void {\r\n const opts = [...this.channels.values()].flatMap((c) => c._tableHandlers(schema, table));\r\n if (opts.length === 0) return;\r\n const events = [...new Set(opts.flatMap((o) => normaliseEvents(o.event)))];\r\n const sameFilter = new Set(opts.map((o) => JSON.stringify(o.filter ?? null))).size === 1;\r\n ws.send(JSON.stringify({\r\n id: `rt-${Math.random().toString(36).slice(2)}`,\r\n type: 'subscribe',\r\n channel: `table:${schema}.${table}`,\r\n events,\r\n filter: sameFilter ? opts[0].filter : undefined,\r\n }));\r\n }\r\n\r\n private reconnect(): void {\r\n this.reconnectTimer = null;\r\n this.connectPromise = null;\r\n this.ws = null;\r\n if (this.shouldReconnect && [...this.channels.values()].some((c) => c._joined)) {\r\n this.connect().catch(() => {});\r\n }\r\n }\r\n\r\n private route(msg: Record<string, any>): void {\r\n if (msg.type === 'system' && msg.event === 'connected') this.authRetried = false;\r\n if (msg.type === 'db_event') {\r\n // Match by the full channel name stored in the channel instance\r\n for (const ch of this.channels.values()) {\r\n ch._dispatch(msg);\r\n }\r\n } else if (msg.type === 'broadcast') {\r\n for (const ch of this.channels.values()) {\r\n if (`broadcast:${ch.channelName}` === msg.channel) {\r\n ch._dispatch(msg);\r\n }\r\n }\r\n } else if (msg.type === 'presence.state') {\r\n // Route to the channel whose broadcast channel matches.\r\n const channelName = (msg.channel as string | undefined)?.replace(/^broadcast:/, '');\r\n if (channelName) {\r\n const ch = this.channels.get(channelName);\r\n if (ch) ch._dispatch(msg);\r\n }\r\n }\r\n // 'subscribed', 'system', 'error' messages — no-op for now\r\n }\r\n\r\n private registerChannel(ch: RealtimeChannel): void {\r\n this.channels.set(ch.channelName, ch);\r\n // A previous disconnect() / last-channel close disabled reconnects.\r\n this.shouldReconnect = true;\r\n ch._setStatus('connecting');\r\n // An open socket activates now; otherwise onopen activates every joined\r\n // channel (activating in both places sent duplicate subscribes).\r\n if (this.ws?.readyState === OPEN) {\r\n ch._activate(this.ws);\r\n return;\r\n }\r\n this.connect().catch((err) => ch._setStatus('error', err));\r\n }\r\n\r\n private async unregisterChannel(name: string): Promise<void> {\r\n const ch = this.channels.get(name);\r\n if (!ch) return;\r\n // Server channel names this channel used, before it leaves the map.\r\n const tableKeys = ch._tableKeys();\r\n ch._joined = false;\r\n this.channels.delete(name);\r\n\r\n const ws = this.ws;\r\n if (ws?.readyState === OPEN) {\r\n const unsubscribe = (channel: string) => ws.send(JSON.stringify({\r\n id: `unsub-${Math.random().toString(36).slice(2)}`,\r\n type: 'unsubscribe',\r\n channel,\r\n }));\r\n for (const key of tableKeys) {\r\n const [schema, table] = splitTableKey(key);\r\n const stillUsed = [...this.channels.values()].some((c) => c._tableHandlers(schema, table).length > 0);\r\n // Another channel still listens on this table: re-send its subscription.\r\n if (stillUsed) this.syncTable(ws, schema, table);\r\n else unsubscribe(`table:${key}`);\r\n }\r\n if (ch._hasBroadcast()) unsubscribe(`broadcast:${name}`);\r\n }\r\n if (![...this.channels.values()].some((c) => c._joined)) {\r\n this.ws?.close(1000, 'no channels');\r\n }\r\n }\r\n\r\n private async sendBroadcast(channelName: string, event: string, payload: unknown): Promise<void> {\r\n await this.connect();\r\n if (this.ws?.readyState !== OPEN) throw new Error('Not connected');\r\n this.ws.send(JSON.stringify({\r\n id: `bc-${Math.random().toString(36).slice(2)}`,\r\n type: 'broadcast',\r\n channel: `broadcast:${channelName}`,\r\n event,\r\n payload,\r\n }));\r\n }\r\n\r\n private async sendPresenceTrack(\r\n channelName: string,\r\n state: Record<string, any>,\r\n ): Promise<void> {\r\n await this.connect();\r\n if (this.ws?.readyState !== OPEN) throw new Error('Not connected');\r\n this.ws.send(JSON.stringify({\r\n id: `pt-${Math.random().toString(36).slice(2)}`,\r\n type: 'presence.track',\r\n channel: `broadcast:${channelName}`,\r\n state,\r\n }));\r\n }\r\n\r\n private sendPresenceUntrack(channelName: string): void {\r\n if (this.ws?.readyState !== OPEN) return;\r\n // Drops presence only; the broadcast subscription stays. No ack is sent.\r\n this.ws.send(JSON.stringify({\r\n id: `pu-${Math.random().toString(36).slice(2)}`,\r\n type: 'presence.untrack',\r\n channel: `broadcast:${channelName}`,\r\n }));\r\n }\r\n}\r\n\r\n// ─── Helpers ──────────────────────────────────────────────────────────────────\r\n\r\nfunction normaliseEvents(event: PgEvent | PgEvent[]): string[] {\r\n if (event === '*') return ['INSERT', 'UPDATE', 'DELETE'];\r\n const list = Array.isArray(event) ? event : [event];\r\n return list.includes('*') ? ['INSERT', 'UPDATE', 'DELETE'] : list;\r\n}\r\n\r\nfunction splitTableKey(key: string): [string, string] {\r\n const dot = key.indexOf('.');\r\n return [key.slice(0, dot), key.slice(dot + 1)];\r\n}\r\n\r\n/** Same semantics as the gateway's filter (loose equality, SQL LIKE). */\r\nfunction matchesFilter(\r\n row: Record<string, unknown> | undefined,\r\n filter: Record<string, FilterValue> | undefined,\r\n): boolean {\r\n if (!filter || !row) return true;\r\n for (const [col, c] of Object.entries(filter)) {\r\n const v = row[col] as any;\r\n if (c.eq !== undefined && v != c.eq) return false;\r\n if (c.neq !== undefined && v == c.neq) return false;\r\n if (c.in !== undefined && !c.in.some((x) => x == v)) return false;\r\n if (c.gt !== undefined && !(v > c.gt)) return false;\r\n if (c.lt !== undefined && !(v < c.lt)) return false;\r\n if (c.like !== undefined) {\r\n const re = c.like.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/%/g, '.*').replace(/_/g, '.');\r\n if (!new RegExp(`^${re}$`, 's').test(String(v))) return false;\r\n }\r\n }\r\n return true;\r\n}\r\n","import type { HttpClient } from './client';\nimport type { ApiResult } from '../types';\n\ntype Platform = 'node' | 'web' | 'react-native' | 'unknown';\n\nexport interface AnalyticsEvent {\n event_id: string;\n event: string;\n anonymous_id: string;\n platform: Platform;\n timestamp: string;\n session_id?: string;\n user_id?: string;\n app_id?: string;\n sdk_version?: string;\n app_version?: string;\n properties?: Record<string, unknown>;\n device_info?: {\n model?: string;\n os?: string;\n os_version?: string;\n brand?: string;\n is_tablet?: boolean;\n locale?: string;\n timezone?: string;\n screen_width?: number;\n screen_height?: number;\n };\n}\n\nexport interface IngestionResult {\n accepted: number;\n rejected: number;\n errors: Array<{ event_id: string; reason: string }>;\n}\n\nexport interface AnalyticsOptions {\n platform?: Platform;\n appVersion?: string;\n flushInterval?: number;\n maxBatchSize?: number;\n}\n\nconst MAX_BATCH_SIZE_CAP = 500;\nconst DEFAULT_BATCH_SIZE = 50;\nconst DEFAULT_FLUSH_INTERVAL = 5000;\n/** Cap on events kept for retry while the API is unreachable. */\nconst MAX_QUEUE = 1000;\n\n// Persisted identity keys. In a browser the SDK is re-instantiated on every full\n// page load, so generating a fresh anonymous_id per instance would count the same\n// visitor as a brand-new user on every visit. Persist it in localStorage instead.\nconst ANON_STORAGE_KEY = 'nestcore_anonymous_id';\nconst SESSION_ID_KEY = 'nestcore_session_id';\nconst SESSION_TS_KEY = 'nestcore_session_last_activity';\n/** Keys written before the OrbitNest → NestCore rename, migrated on first read. */\nconst LEGACY_KEYS: Record<string, string> = {\n [ANON_STORAGE_KEY]: 'orbitnest_anonymous_id',\n [SESSION_ID_KEY]: 'orbitnest_session_id',\n [SESSION_TS_KEY]: 'orbitnest_session_last_activity',\n};\n// 30 min of inactivity ends a session — matches analytics_projects.session_timeout_mins.\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000;\n\nfunction generateUuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID but with Web Crypto\n // (crypto.getRandomValues — available in Node >=18 and modern browsers).\n // Build an RFC 4122 v4 UUID from 16 cryptographically-strong random bytes\n // instead of the non-cryptographic, predictable Math.random().\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n }\n // Last-resort fallback for environments lacking Web Crypto entirely.\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n\n/** A localStorage handle, but only if it's actually usable (private-mode and\n * SSR both expose-or-throw). Returns null on the server or when blocked. */\nfunction getLocalStorage(): Storage | null {\n try {\n if (typeof localStorage === 'undefined') return null;\n const probe = '__on_ls_probe__';\n localStorage.setItem(probe, '1');\n localStorage.removeItem(probe);\n return localStorage;\n } catch {\n return null;\n }\n}\n\n/** Read a key, moving a value stored under its pre-rename name to the new key\n * so returning visitors keep their anonymous id and session. */\nfunction readKey(store: Storage, key: string): string | null {\n const value = store.getItem(key);\n if (value !== null) return value;\n const legacy = LEGACY_KEYS[key];\n const old = legacy ? store.getItem(legacy) : null;\n if (old === null || !legacy) return null;\n store.setItem(key, old);\n store.removeItem(legacy);\n return old;\n}\n\n/** Browser vs server — used to default the event platform when not specified. */\nfunction detectPlatform(): Platform {\n return typeof window !== 'undefined' && typeof document !== 'undefined' ? 'web' : 'node';\n}\n\n/** Stable, long-lived per-browser anonymous id (created once, reused forever). */\nfunction loadOrCreateAnonymousId(store: Storage | null): string {\n if (!store) return generateUuid();\n try {\n let id = readKey(store, ANON_STORAGE_KEY);\n if (!id) {\n id = generateUuid();\n store.setItem(ANON_STORAGE_KEY, id);\n }\n return id;\n } catch {\n return generateUuid();\n }\n}\n\n/** Current session id, rotating after SESSION_TIMEOUT_MS of inactivity and\n * sliding the activity timestamp on every call. */\nfunction resolveSessionId(store: Storage | null, fallback: string): string {\n if (!store) return fallback;\n try {\n const now = Date.now();\n const last = parseInt(readKey(store, SESSION_TS_KEY) ?? '0', 10) || 0;\n let sid = readKey(store, SESSION_ID_KEY);\n if (!sid || now - last > SESSION_TIMEOUT_MS) {\n sid = generateUuid();\n store.setItem(SESSION_ID_KEY, sid);\n }\n store.setItem(SESSION_TS_KEY, String(now));\n return sid;\n } catch {\n return fallback;\n }\n}\n\nexport class AnalyticsClient {\n private readonly storage: Storage | null;\n private readonly anonymousId: string;\n private readonly fallbackSessionId: string;\n private readonly platform: Platform;\n private readonly appVersion: string | undefined;\n private readonly flushInterval: number;\n private readonly maxBatchSize: number;\n\n private userId: string | undefined;\n private queue: AnalyticsEvent[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(private readonly client: HttpClient, options: AnalyticsOptions = {}) {\n this.storage = getLocalStorage();\n // anonymous_id persists across page loads (stable visitor); session_id rotates\n // on inactivity. On the server (no storage) both fall back to a per-instance uuid.\n this.anonymousId = loadOrCreateAnonymousId(this.storage);\n this.fallbackSessionId = generateUuid();\n this.platform = options.platform ?? detectPlatform();\n this.appVersion = options.appVersion;\n this.flushInterval = options.flushInterval ?? DEFAULT_FLUSH_INTERVAL;\n this.maxBatchSize = Math.min(options.maxBatchSize ?? DEFAULT_BATCH_SIZE, MAX_BATCH_SIZE_CAP);\n\n // Browser: send what's queued when the page is hidden or unloaded, with\n // keepalive so the request outlives the page.\n if (typeof window !== 'undefined' && typeof document !== 'undefined' && window.addEventListener) {\n window.addEventListener('pagehide', () => this.flushInBackground(true));\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') this.flushInBackground(true);\n });\n }\n }\n\n identify(userId: string, traits?: Record<string, unknown>): void {\n this.userId = userId;\n this.enqueue('identify', traits);\n }\n\n track(event: string, properties?: Record<string, unknown>): void {\n this.enqueue(event, properties);\n }\n\n screen(screenName: string, properties?: Record<string, unknown>): void {\n this.enqueue('screen_view', { screen: screenName, ...properties });\n }\n\n crash(error: Error | string, properties?: Record<string, unknown>): void {\n const msg = error instanceof Error ? error.message : error;\n const stack = error instanceof Error ? error.stack : undefined;\n // Field names the crash grouper reads (error_type / error_message / stack_trace).\n this.enqueue('crash', {\n ...(error instanceof Error ? { error_type: error.name } : {}),\n error_message: msg,\n ...(stack ? { stack_trace: stack } : {}),\n ...properties,\n });\n }\n\n async flush(keepalive = false): Promise<ApiResult<IngestionResult>> {\n this.clearFlushTimer();\n\n if (this.queue.length === 0) {\n return { data: { accepted: 0, rejected: 0, errors: [] }, error: null };\n }\n\n const batch = this.queue.splice(0, this.queue.length);\n\n const result = await this.client.requestForAnalytics<IngestionResult>('/api/analytics/events', {\n method: 'POST',\n body: { events: batch },\n keepalive,\n });\n\n // Network errors, timeouts, 429 and 5xx are transient: keep the batch\n // (oldest first, capped) and try again on the next flush.\n const status = result.error?.status;\n if (result.error && (status === undefined || status === 429 || status >= 500)) {\n this.queue = [...batch, ...this.queue].slice(-MAX_QUEUE);\n if (!keepalive) this.scheduleFlush();\n }\n\n return result;\n }\n\n /** Flush from a timer/unload. Dropped events are logged, never thrown. */\n private flushInBackground(keepalive = false): void {\n void this.flush(keepalive).then((r) => {\n const status = r.error?.status;\n if (r.error && status !== undefined && status !== 429 && status < 500) {\n console.warn(`[nestcore] analytics events rejected (${status}): ${r.error.message}`);\n }\n });\n }\n\n private enqueue(event: string, properties?: Record<string, unknown>): void {\n const entry: AnalyticsEvent = {\n event_id: generateUuid(),\n event,\n anonymous_id: this.anonymousId,\n platform: this.platform,\n timestamp: new Date().toISOString(),\n session_id: resolveSessionId(this.storage, this.fallbackSessionId),\n ...(this.userId ? { user_id: this.userId } : {}),\n ...(this.appVersion ? { app_version: this.appVersion } : {}),\n ...(properties ? { properties } : {}),\n };\n\n this.queue.push(entry);\n\n if (this.queue.length >= this.maxBatchSize) {\n this.flushInBackground();\n } else {\n this.scheduleFlush();\n }\n }\n\n private scheduleFlush(): void {\n if (this.flushTimer !== null) return;\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n this.flushInBackground();\n }, this.flushInterval);\n }\n\n private clearFlushTimer(): void {\n if (this.flushTimer !== null) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n }\n}\n","/** `data:` payloads of a server-sent-events body. */\r\nexport async function* sseData(body: ReadableStream<Uint8Array>): AsyncGenerator<string> {\r\n const reader = body.getReader();\r\n const decoder = new TextDecoder();\r\n let buf = '';\r\n try {\r\n for (;;) {\r\n const { value, done } = await reader.read();\r\n if (done) break;\r\n buf += decoder.decode(value, { stream: true });\r\n let nl: number;\r\n while ((nl = buf.indexOf('\\n')) >= 0) {\r\n const line = buf.slice(0, nl).replace(/\\r$/, '');\r\n buf = buf.slice(nl + 1);\r\n if (line.startsWith('data:')) yield line.slice(5).trimStart();\r\n }\r\n }\r\n if (buf.startsWith('data:')) yield buf.slice(5).trimStart();\r\n } finally {\r\n // A consumer that leaves the loop early must close the connection, or the server keeps generating (and billing).\r\n await reader.cancel().catch(() => {});\r\n }\r\n}\r\n","import { errorMessage, type HttpClient } from './client';\nimport { sseData } from './sse';\nimport type { AiContentPart, AiProviderId, AiToolCall } from './ai';\nimport type { ApiResult } from '../types';\n\n// ── Memory ────────────────────────────────────────────────────────────────\n\nexport interface AiMemory {\n id: string;\n subject_type: string;\n subject_id: string;\n kind: string;\n content: string;\n embed_model: string | null;\n importance: number;\n metadata: Record<string, unknown>;\n source_thread_id: string | null;\n expires_at: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface AiMemoryMatch extends AiMemory {\n /** Cosine similarity (null in a keyword fallback). */\n similarity: number | null;\n /** Similarity nudged by importance and recency; results are sorted by it. */\n score: number;\n}\n\n/** `metadata.trust` (and the search `trust` filter): a lowercase word such as `user`, `tool` or `web`. */\nexport type AiTrust = string | string[];\n\n/**\n * A vector you computed with your own (e.g. local) embedding model. The server\n * then calls no model provider and charges nothing. Search compares only\n * vectors of the same `model` and size, so switching models means re-sending.\n */\nexport interface ClientEmbedding {\n /** 1–2000 finite numbers, not all zeros. Needs `model`. */\n embedding?: number[];\n /** The model that produced the vector (names the vector space), e.g. `nomic-embed-text-v1.5`. */\n model?: string;\n /** Checked against the vector length. */\n dims?: number;\n}\n\nexport interface MemoryAddOptions extends ClientEmbedding {\n /** Service key only: `type:id`, e.g. `user:<id>`, `agent:support`. Signed-in users are always `user:<their id>`; device and phone tokens default to `owner:<owner id>`. */\n subject?: string;\n /** fact (default), preference, episode, summary, or your own word. */\n kind?: string;\n /** 1–5, default 3. */\n importance?: number;\n /** ISO date; the memory is deleted after it. */\n expiresAt?: string | null;\n /** `trust` is reserved: a lowercase word (`^[a-z][a-z0-9_-]{0,31}$`). */\n metadata?: Record<string, unknown>;\n sourceThreadId?: string;\n /** Update a near-identical memory (cosine ≥ 0.92) instead of adding one. Default true. */\n dedupe?: boolean;\n}\n\n/** One item of {@link MemoryClient.addMany}. */\nexport interface MemoryAddItem extends MemoryAddOptions {\n content: string;\n}\n\nexport interface MemorySearchOptions {\n subject?: string;\n /** 1–50, default 5. */\n k?: number;\n kind?: string;\n minSimilarity?: number;\n /** Only memories whose `metadata.trust` is this word (or one of these words). */\n trust?: AiTrust;\n /** Your own query vector; `query` text is then optional. Needs `model`. */\n queryEmbedding?: number[];\n /** The model of `queryEmbedding`. */\n model?: string;\n dims?: number;\n}\n\n/** One query of {@link MemoryClient.searchMany}: `query` text, `queryEmbedding`, or both. */\nexport interface MemorySearchItem extends MemorySearchOptions {\n query?: string;\n}\n\nexport interface MemorySearchResult {\n results: AiMemoryMatch[];\n /** true = the model provider was down and keyword search answered. */\n degraded: boolean;\n model: string | null;\n}\n\nexport interface MemoryUpdatePatch extends ClientEmbedding {\n content?: string;\n kind?: string;\n importance?: number;\n expiresAt?: string | null;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Memory API: facts about a user (or an agent, an org…) with semantic search.\n * A signed-in user reads and writes only their own memory; the service key may\n * name any subject.\n *\n * ```ts\n * await client.memory.add('Prefers vegetarian recipes', { kind: 'preference' });\n * const { data } = await client.memory.search('what should I cook?');\n *\n * // Your own embeddings (no provider call, nothing charged):\n * await client.memory.add('Prefers tea', { embedding: vec, model: 'nomic-embed-text-v1.5' });\n * await client.memory.search({ queryEmbedding: q, model: 'nomic-embed-text-v1.5', trust: 'user' });\n * ```\n */\nexport class MemoryClient {\n constructor(private client: HttpClient) {}\n\n private path(p = ''): string {\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/memory${p}`;\n }\n\n /**\n * Remember something. `deduped: true` means an existing near-identical memory was updated.\n * Pass `embedding` + `model` to use your own vector.\n */\n add(content: string, options: MemoryAddOptions = {}): Promise<ApiResult<{ memory: AiMemory; deduped: boolean }>> {\n return this.client.request(this.path(), { method: 'POST', body: { content, ...options } });\n }\n\n /**\n * Most relevant memories. `degraded: true` = the model provider was down and keyword search answered.\n * With `queryEmbedding` + `model` the text is optional: `search({ queryEmbedding, model })`\n * or `search(null, { queryEmbedding, model })`.\n */\n search(query: string | null | undefined, options?: MemorySearchOptions): Promise<ApiResult<MemorySearchResult>>;\n search(options: MemorySearchItem): Promise<ApiResult<MemorySearchResult>>;\n search(queryOrOptions: string | null | undefined | MemorySearchItem, options: MemorySearchOptions = {}): Promise<ApiResult<MemorySearchResult>> {\n return this.client.request(this.path('/search'), { method: 'POST', body: searchBody(queryOrOptions, options) });\n }\n\n /** Up to 100 memories in one call (each counts against the memory quota). An invalid item fails the whole call with `400 \"item <i>: …\"`. */\n addMany(items: MemoryAddItem[]): Promise<ApiResult<{ results: Array<{ memory: AiMemory; deduped: boolean }> }>> {\n return this.client.request(this.path('/batch/add'), { method: 'POST', body: { items } });\n }\n\n /** Up to 20 searches in one call; `results[i]` answers `queries[i]`. */\n searchMany(queries: MemorySearchItem[]): Promise<ApiResult<{ results: MemorySearchResult[] }>> {\n return this.client.request(this.path('/batch/search'), { method: 'POST', body: { queries } });\n }\n\n /** Delete memories by id (up to 500) and/or whole subjects (up to 20; users and agents only their own). */\n forgetMany(target: { ids?: string[]; subjects?: string[] }): Promise<ApiResult<{ deleted: number }>> {\n return this.client.request(this.path('/batch/forget'), { method: 'POST', body: target });\n }\n\n list(options: { subject?: string; kind?: string; limit?: number; offset?: number } = {}): Promise<ApiResult<{ memories: AiMemory[]; total: number }>> {\n return this.client.request(this.path(query(options)));\n }\n\n get(id: string): Promise<ApiResult<AiMemory>> {\n return this.client.request(this.path(`/${encodeURIComponent(id)}`));\n }\n\n /** `embedding` + `model` only together with new `content`. */\n update(id: string, patch: MemoryUpdatePatch): Promise<ApiResult<AiMemory>> {\n return this.client.request(this.path(`/${encodeURIComponent(id)}`), { method: 'PATCH', body: patch });\n }\n\n remove(id: string): Promise<ApiResult<{ deleted: boolean }>> {\n return this.client.request(this.path(`/${encodeURIComponent(id)}`), { method: 'DELETE' });\n }\n\n /** Delete every memory of a subject (rows and vectors). Users always forget themselves. */\n forget(subject?: string): Promise<ApiResult<{ deleted: number }>> {\n return this.client.request(this.path('/forget'), { method: 'POST', body: subject ? { subject } : {} });\n }\n\n /** Service key: subjects that have memories. */\n subjects(options: { type?: string; limit?: number } = {}): Promise<ApiResult<Array<{ subject: string; subject_type: string; subject_id: string; memories: number; last_updated: string }>>> {\n return this.client.request(this.path(`/subjects${query(options)}`));\n }\n\n /** Service key: re-embed memories made with another model (after a provider change), 64 per call. */\n reembed(options: { subject?: string; limit?: number } = {}): Promise<ApiResult<{ reembedded: number; remaining: number; model: string | null }>> {\n return this.client.request(this.path('/reembed'), { method: 'POST', body: options });\n }\n}\n\n// ── Collections ───────────────────────────────────────────────────────────\n\nexport interface AiCollection {\n id: string;\n name: string;\n embed_model: string | null;\n embed_provider: AiProviderId | null;\n chunk_tokens: number;\n chunk_overlap: number;\n user_ingest: boolean;\n metadata: Record<string, unknown>;\n created_at: string;\n documents?: number;\n pending?: number;\n}\n\nexport interface AiDocument {\n id: string;\n collection_id: string;\n owner_user_id: string | null;\n source_type: 'text' | 'storage' | 'url';\n source_ref: string | null;\n title: string | null;\n metadata: Record<string, unknown>;\n status: 'queued' | 'processing' | 'ready' | 'failed';\n error: string | null;\n chunk_count: number;\n bytes: number;\n created_at: string;\n updated_at: string;\n}\n\nexport interface AiSearchResult {\n /** Reciprocal-rank-fusion score (vector + keyword); results are sorted by it. */\n score: number;\n similarity: number | null;\n keywordMatch: boolean;\n /** The best-matching words, wrapped in `**`. */\n highlight: string;\n chunk: { id: string; ordinal: number; content: string; metadata: Record<string, unknown> };\n document: { id: string; title: string | null; sourceType: string; sourceRef: string | null; metadata: Record<string, unknown> };\n}\n\nexport type DocumentSource = { text: string } | { url: string } | { storage: { bucket: string; path: string } };\n\n/** A chunk you split and embedded yourself (see {@link CollectionRef.addChunks}). */\nexport interface ClientChunk {\n content: string;\n embedding: number[];\n /** Chunk metadata, e.g. `{ trust: 'user' }`. */\n metadata?: Record<string, unknown>;\n}\n\nexport interface AddChunksOptions {\n /** The embedding model of every chunk. The first client chunks fix the collection's model; another model answers 409. */\n model: string;\n /** Checked against each vector's length. */\n dims?: number;\n title?: string;\n metadata?: Record<string, unknown>;\n /** Service key only: the user who owns (and alone can read) this document. */\n ownerUserId?: string;\n}\n\nexport interface CollectionSearchOptions {\n /** 1–50, default 8. */\n k?: number;\n /** Vector + keyword (default); `false` = vector only. */\n hybrid?: boolean;\n /** Documents whose metadata contains this object. */\n filter?: Record<string, unknown>;\n minSimilarity?: number;\n /** Only chunks (or their document) whose `metadata.trust` is this word (or one of these words). */\n trust?: AiTrust;\n /** Your own query vector; `query` text is then optional. Needs `model` (the collection's). */\n queryEmbedding?: number[];\n model?: string;\n dims?: number;\n}\n\nexport interface IngestOptions {\n title?: string;\n metadata?: Record<string, unknown>;\n /** Service key only: the user who owns (and alone can read) this document. */\n ownerUserId?: string;\n /** Wait until the document is `ready` or `failed` (default: return at once with `queued`). */\n wait?: boolean | { timeoutMs?: number; intervalMs?: number };\n}\n\n/** One collection: `client.collections.from('handbook')`. */\nexport class CollectionRef {\n constructor(private client: HttpClient, readonly name: string) {}\n\n private path(p = ''): string {\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/collections/${encodeURIComponent(this.name)}${p}`;\n }\n\n /**\n * Add a document; it is chunked and embedded in the background. PDF, HTML and\n * text files are read from storage or a URL.\n */\n async ingest(source: DocumentSource, options: IngestOptions = {}): Promise<ApiResult<AiDocument>> {\n const { wait, ...rest } = options;\n const res = await this.client.request<AiDocument>(this.path('/documents'), { method: 'POST', body: { ...source, ...rest } });\n if (res.error || !wait) return res;\n return this.waitFor(res.data.id, typeof wait === 'object' ? wait : {});\n }\n\n /**\n * Add a document from chunks you embedded yourself (1–500; the body limit is 2 MB).\n * It is `ready` at once; the server calls no provider and charges nothing.\n *\n * ```ts\n * await kb.addChunks([{ content: 'Refunds take 5 days', embedding: vec }], { model: 'nomic-embed-text-v1.5', title: 'FAQ' });\n * ```\n */\n addChunks(chunks: ClientChunk[], options: AddChunksOptions): Promise<ApiResult<AiDocument>> {\n return this.client.request<AiDocument>(this.path('/documents'), { method: 'POST', body: { chunks, ...options } });\n }\n\n /** Poll a document until it is `ready` or `failed`. */\n async waitFor(documentId: string, { timeoutMs = 120_000, intervalMs = 1_000 }: { timeoutMs?: number; intervalMs?: number } = {}): Promise<ApiResult<AiDocument>> {\n const until = Date.now() + timeoutMs;\n for (;;) {\n const res = await this.getDocument(documentId);\n if (res.error || res.data.status === 'ready' || res.data.status === 'failed') return res;\n if (Date.now() > until) return { data: null, error: { message: `Document ${documentId} is still ${res.data.status}`, code: 'TIMEOUT' } };\n await new Promise((r) => setTimeout(r, intervalMs));\n }\n }\n\n /**\n * Hybrid search (vector + keyword). `hybrid: false` is vector only; `filter` matches document metadata.\n * With your own vector: `search({ queryEmbedding, model })` or `search('text', { queryEmbedding, model })`.\n */\n search(query: string | null | undefined, options?: CollectionSearchOptions): Promise<ApiResult<{ results: AiSearchResult[]; model: string | null }>>;\n search(options: CollectionSearchOptions & { query?: string }): Promise<ApiResult<{ results: AiSearchResult[]; model: string | null }>>;\n search(queryOrOptions: string | null | undefined | (CollectionSearchOptions & { query?: string }), options: CollectionSearchOptions = {}): Promise<ApiResult<{ results: AiSearchResult[]; model: string | null }>> {\n return this.client.request(this.path('/search'), { method: 'POST', body: searchBody(queryOrOptions, options) });\n }\n\n listDocuments(options: { status?: AiDocument['status']; limit?: number; offset?: number } = {}): Promise<ApiResult<{ documents: AiDocument[]; total: number }>> {\n return this.client.request(this.path(`/documents${query(options)}`));\n }\n\n getDocument(id: string): Promise<ApiResult<AiDocument>> {\n return this.client.request(this.path(`/documents/${encodeURIComponent(id)}`));\n }\n\n /** Delete a document and its chunks. */\n remove(documentId: string): Promise<ApiResult<{ deleted: boolean }>> {\n return this.client.request(this.path(`/documents/${encodeURIComponent(documentId)}`), { method: 'DELETE' });\n }\n\n stats(): Promise<ApiResult<{ chunks: number; current_model_chunks: number; documents: number; failed: number; model: string | null }>> {\n return this.client.request(this.path('/stats'));\n }\n}\n\n/**\n * Vector collections: documents chunked, embedded and searched with hybrid\n * retrieval under RLS. Creating collections and adding shared documents needs\n * the service key; a collection with `userIngest` lets signed-in users add\n * documents only they can read.\n *\n * ```ts\n * await admin.collections.create({ name: 'handbook' });\n * await admin.collections.from('handbook').ingest({ url: 'https://example.com/guide.pdf' }, { wait: true });\n * const { data } = await client.collections.from('handbook').search('How do refunds work?');\n * ```\n */\nexport class CollectionsClient {\n constructor(private client: HttpClient) {}\n\n private path(p = ''): string {\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/collections${p}`;\n }\n\n from(name: string): CollectionRef {\n return new CollectionRef(this.client, name);\n }\n\n create(options: { name: string; model?: string; provider?: AiProviderId; chunkTokens?: number; chunkOverlap?: number; userIngest?: boolean; metadata?: Record<string, unknown> }): Promise<ApiResult<AiCollection>> {\n return this.client.request(this.path(), { method: 'POST', body: options });\n }\n\n list(): Promise<ApiResult<AiCollection[]>> {\n return this.client.request(this.path());\n }\n\n get(name: string): Promise<ApiResult<AiCollection>> {\n return this.client.request(this.path(`/${encodeURIComponent(name)}`));\n }\n\n /** Changing `model` re-embeds every chunk in the background. */\n update(name: string, patch: { userIngest?: boolean; metadata?: Record<string, unknown>; model?: string; provider?: AiProviderId }): Promise<ApiResult<AiCollection>> {\n return this.client.request(this.path(`/${encodeURIComponent(name)}`), { method: 'PATCH', body: patch });\n }\n\n delete(name: string): Promise<ApiResult<{ deleted: boolean }>> {\n return this.client.request(this.path(`/${encodeURIComponent(name)}`), { method: 'DELETE' });\n }\n}\n\n// ── Threads ───────────────────────────────────────────────────────────────\n\nexport interface AiThread {\n id: string;\n owner_user_id: string | null;\n title: string | null;\n metadata: Record<string, unknown>;\n created_at: string;\n updated_at: string;\n messages?: number;\n cost_usd?: number;\n}\n\nexport type AiThreadPart = AiContentPart | { type: 'image'; storage: { bucket: string; path: string } };\n\nexport interface AiThreadMessage {\n id: string;\n thread_id: string;\n role: 'system' | 'user' | 'assistant' | 'tool';\n content: AiThreadPart[];\n model: string | null;\n provider: string | null;\n tokens_in: number | null;\n tokens_out: number | null;\n cost_usd: number | null;\n /** `streaming` while a run writes it (partial text), `error` if the run failed. */\n status: 'streaming' | 'complete' | 'error';\n error: string | null;\n metadata: Record<string, unknown>;\n created_at: string;\n}\n\nexport interface AiCitation {\n index: number;\n collection: string;\n documentId: string;\n chunkId: string;\n title: string | null;\n sourceRef: string | null;\n snippet: string;\n score: number;\n}\n\nexport interface ThreadRunOptions {\n /** Appended as a user message first. */\n input?: string | AiThreadPart[];\n model?: string;\n provider?: AiProviderId;\n system?: string;\n temperature?: number;\n maxTokens?: number;\n /** true = the user's memories as context; with the service key a `type:id` subject. */\n memory?: boolean | string;\n /** Collections searched for sources the model cites as [n]. */\n collections?: string[];\n /** Built-in tools the model may call. */\n tools?: Array<'memory_search' | 'collections_search'>;\n /** History + context budget in tokens (default 8000). */\n maxContextTokens?: number;\n}\n\nexport type ThreadRunEvent =\n | { type: 'message'; threadId: string; messageId: string }\n | ({ type: 'citation' } & AiCitation)\n | { type: 'delta'; text: string }\n | ({ type: 'tool_call' } & AiToolCall)\n | { type: 'tool_result'; id: string; name: string; result: unknown }\n | { type: 'done'; messageId: string; model: string; provider: string | null; tokensIn: number; tokensOut: number; costUsd: number }\n | { type: 'error'; code?: string; message: string; status?: number; details?: Record<string, unknown> };\n\nexport interface ThreadRunResult {\n messageId: string;\n text: string;\n citations: AiCitation[];\n toolCalls: Array<{ id: string; name: string; result: unknown }>;\n usage: { model: string; provider: string | null; tokensIn: number; tokensOut: number; costUsd: number } | null;\n}\n\n/** One thread: `client.ai.threads.from(id)`. */\nexport class ThreadRef {\n constructor(private client: HttpClient, readonly id: string) {}\n\n private path(p = ''): string {\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/threads/${encodeURIComponent(this.id)}${p}`;\n }\n\n get(): Promise<ApiResult<AiThread>> {\n return this.client.request(this.path());\n }\n\n update(patch: { title?: string | null; metadata?: Record<string, unknown> }): Promise<ApiResult<AiThread>> {\n return this.client.request(this.path(), { method: 'PATCH', body: patch });\n }\n\n delete(): Promise<ApiResult<{ deleted: boolean }>> {\n return this.client.request(this.path(), { method: 'DELETE' });\n }\n\n /** Oldest first: the newest `limit`, or those created after `after` (resume after a reconnect). */\n messages(options: { limit?: number; after?: string } = {}): Promise<ApiResult<AiThreadMessage[]>> {\n return this.client.request(this.path(`/messages${query(options)}`));\n }\n\n /** Add a message without running the model (users may add `user` messages only). */\n append(content: string | AiThreadPart[], options: { role?: 'user' | 'assistant' | 'system'; metadata?: Record<string, unknown> } = {}): Promise<ApiResult<AiThreadMessage>> {\n return this.client.request(this.path('/messages'), { method: 'POST', body: { content, ...options } });\n }\n\n /**\n * Stream the assistant's next message: `message` (its id), `citation`s,\n * `delta`s, tool events, then `done` (or `error`). The reply is saved as it\n * streams, so another device can read it with `messages()` or listen on the\n * realtime channel `broadcast:ai:thread:<id>`. Leaving the loop early cancels.\n */\n async *run(options: ThreadRunOptions = {}, { signal }: { signal?: AbortSignal } = {}): AsyncGenerator<ThreadRunEvent> {\n let res: Response;\n try {\n res = await this.client.authFetch(`${this.client.getBaseUrl()}${this.path('/run')}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'text/event-stream' },\n body: JSON.stringify({ ...options, stream: true }),\n signal,\n redirect: 'error',\n });\n } catch (err) {\n yield { type: 'error', code: 'NETWORK_ERROR', message: err instanceof Error ? err.message : 'Network error' };\n return;\n }\n if (!res.ok || !res.body) {\n const data = await res.json().catch(() => null);\n yield { type: 'error', code: data?.code, message: errorMessage(data, res.status), status: res.status, ...(data && typeof data === 'object' ? { details: data } : {}) };\n return;\n }\n try {\n for await (const raw of sseData(res.body)) yield JSON.parse(raw) as ThreadRunEvent;\n } catch (err) {\n yield { type: 'error', code: 'STREAM_ERROR', message: err instanceof Error ? err.message : 'Stream interrupted' };\n }\n }\n\n /** Run and wait for the finished message (no streaming). */\n runOnce(options: ThreadRunOptions = {}): Promise<ApiResult<ThreadRunResult>> {\n return this.client.request(this.path('/run'), { method: 'POST', body: { ...options, stream: false }, timeout: 180_000 });\n }\n}\n\n/**\n * Threads: persistent conversations with per-message tokens and cost. A\n * signed-in user owns the threads they create; the service key can create\n * threads for any user (`ownerUserId`).\n *\n * ```ts\n * const { data: t } = await client.ai.threads.create({ title: 'Trip ideas' });\n * for await (const ev of client.ai.threads.from(t.id).run({ input: 'Plan a weekend in Oslo', memory: true })) {\n * if (ev.type === 'delta') process.stdout.write(ev.text);\n * }\n * ```\n */\nexport class ThreadsClient {\n constructor(private client: HttpClient) {}\n\n private path(p = ''): string {\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/threads${p}`;\n }\n\n from(id: string): ThreadRef {\n return new ThreadRef(this.client, id);\n }\n\n create(options: { title?: string; metadata?: Record<string, unknown>; ownerUserId?: string } = {}): Promise<ApiResult<AiThread>> {\n return this.client.request(this.path(), { method: 'POST', body: options });\n }\n\n list(options: { limit?: number; offset?: number; ownerUserId?: string } = {}): Promise<ApiResult<{ threads: AiThread[]; total: number }>> {\n return this.client.request(this.path(query(options)));\n }\n\n get(id: string): Promise<ApiResult<AiThread>> {\n return this.from(id).get();\n }\n\n delete(id: string): Promise<ApiResult<{ deleted: boolean }>> {\n return this.from(id).delete();\n }\n}\n\nexport interface AiTrace {\n id: string;\n request_id: string | null;\n thread_id: string | null;\n caller: string;\n kind: 'chat' | 'embed';\n end_user_id: string | null;\n provider: string;\n model: string;\n key_source: 'byok' | 'platform';\n prompt_hash: string | null;\n tokens_in: number;\n tokens_out: number;\n cost_usd: number | null;\n latency_ms: number;\n status: 'ok' | 'error';\n error: string | null;\n created_at: string;\n}\n\n/** `search('text', opts)`, `search(null, opts)` or `search({ query?, ...opts })` → the request body. */\nfunction searchBody<O extends object>(queryOrOptions: string | null | undefined | (O & { query?: string }), options: O): Record<string, unknown> {\n if (queryOrOptions !== null && typeof queryOrOptions === 'object') return { ...queryOrOptions };\n return typeof queryOrOptions === 'string' ? { query: queryOrOptions, ...options } : { ...(options as Record<string, unknown>) };\n}\n\nfunction query(o: Record<string, unknown>): string {\n const q = Object.entries(o).filter(([, v]) => v !== undefined && v !== null).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);\n return q.length ? `?${q.join('&')}` : '';\n}\n","import { errorMessage, type HttpClient } from './client';\r\nimport { sseData } from './sse';\r\nimport { ThreadsClient, type AiTrace } from './ai-data';\r\nimport type { ApiResult } from '../types';\r\n\r\nexport type AiProviderId = 'gemini' | 'openai' | 'anthropic' | 'openai_compatible';\r\n\r\n/** `url`: a `data:image/png;base64,…` URL, or an https URL (OpenAI and Anthropic only). */\r\nexport type AiContentPart = { type: 'text'; text: string } | { type: 'image'; url: string };\r\n\r\nexport interface AiToolCall {\r\n id: string;\r\n name: string;\r\n arguments: Record<string, unknown>;\r\n /** Provider state; send the call back unchanged in the assistant message. */\r\n signature?: string;\r\n}\r\n\r\nexport interface AiTool {\r\n name: string;\r\n description?: string;\r\n /** JSON Schema of the arguments object. */\r\n parameters?: Record<string, unknown>;\r\n}\r\n\r\nexport interface AiMessage {\r\n role: 'system' | 'user' | 'assistant' | 'tool';\r\n content: string | AiContentPart[] | null;\r\n /** assistant: the calls from a previous reply (`toolCalls`). */\r\n toolCalls?: AiToolCall[];\r\n /** tool: the id of the call this result answers. */\r\n toolCallId?: string;\r\n}\r\n\r\nexport interface AiChatOptions {\r\n /** Defaults to the project's default model (Console → Project Settings → AI). */\r\n model?: string;\r\n /** Needed only when the model name does not identify the provider. */\r\n provider?: AiProviderId;\r\n messages: AiMessage[];\r\n temperature?: number;\r\n maxTokens?: number;\r\n tools?: AiTool[];\r\n toolChoice?: 'auto' | 'none' | 'required' | { name: string };\r\n}\r\n\r\nexport interface AiChatResult {\r\n text: string;\r\n /** Calls the model wants made; answer each with a `tool` message and chat again. */\r\n toolCalls: AiToolCall[];\r\n model: string;\r\n provider: AiProviderId;\r\n tokensIn: number;\r\n tokensOut: number;\r\n /** List price; models without a listed price are charged at their provider's top price. */\r\n costUsd: number;\r\n finishReason: string | null;\r\n}\r\n\r\nexport type AiStreamEvent =\r\n | { type: 'delta'; text: string }\r\n | ({ type: 'tool_call' } & AiToolCall)\r\n | { type: 'done'; model: string; tokensIn: number; tokensOut: number; costUsd: number; finishReason: string | null }\r\n | { type: 'error'; code?: string; message: string; status?: number; details?: Record<string, unknown> };\r\n\r\nexport interface AiEmbedResult {\r\n model: string;\r\n provider: AiProviderId;\r\n dimensions: number;\r\n embeddings: number[][];\r\n tokensIn: number;\r\n}\r\n\r\nexport interface AiModel {\r\n id: string;\r\n provider: AiProviderId;\r\n kind: 'chat' | 'embed';\r\n default?: boolean;\r\n /** `credits`: paid from the workspace's prepaid AI credit (no project key for this provider). */\r\n source?: 'credits';\r\n}\r\n\r\n/**\r\n * Whether this project's AI calls can be paid from its workspace's prepaid\r\n * credit (when the project has no key of its own for a provider). A project's\r\n * own key always wins.\r\n */\r\nexport interface AiCredits {\r\n /** There is credit to spend now. */\r\n available: boolean;\r\n /** The workspace has bought credit at some point. */\r\n active: boolean;\r\n balanceUsd: number;\r\n /** null = no monthly cap. */\r\n monthlyCapUsd: number | null;\r\n spentThisMonthUsd: number;\r\n /** Providers the project has its own key for. */\r\n ownKeys: AiProviderId[];\r\n /** Providers the credit can pay for. */\r\n creditProviders: AiProviderId[];\r\n}\r\n\r\n/**\r\n * AI Gateway: chat and embeddings through the project's own provider keys.\r\n * Calls need a signed-in user (per-user caps apply) or a service-role key.\r\n *\r\n * ```ts\r\n * const { data } = await client.ai.chat({ messages: [{ role: 'user', content: 'Hi' }] });\r\n * for await (const ev of client.ai.chatStream({ messages })) {\r\n * if (ev.type === 'delta') process.stdout.write(ev.text);\r\n * }\r\n * ```\r\n */\r\nexport class AiClient {\r\n /** Persistent conversations with streamed, saved replies (`threads.from(id).run()`). */\r\n readonly threads: ThreadsClient;\r\n\r\n constructor(private client: HttpClient) {\r\n this.threads = new ThreadsClient(client);\r\n }\r\n\r\n private path(p: string): string {\r\n return `/api/project/${this.client.getProjectSlug()}/ai/v1/${p}`;\r\n }\r\n\r\n /**\r\n * Base URL of the OpenAI-compatible API (`chat/completions` with `stream`,\r\n * `embeddings`, `models`) for any OpenAI client. Send the project key as the\r\n * `apikey` header and the user's (or device's) access token as the API key:\r\n *\r\n * ```ts\r\n * new OpenAI({ baseURL: client.ai.openaiBaseUrl, apiKey: accessToken, defaultHeaders: { apikey: anonKey } });\r\n * ```\r\n */\r\n get openaiBaseUrl(): string {\r\n return `${this.client.getBaseUrl()}/api/project/${this.client.getProjectSlug()}/ai/openai/v1`;\r\n }\r\n\r\n private body(o: AiChatOptions, stream: boolean) {\r\n return {\r\n model: o.model,\r\n provider: o.provider,\r\n messages: o.messages.map((m) => ({\r\n role: m.role,\r\n content: m.content,\r\n ...(m.toolCalls ? { tool_calls: m.toolCalls } : {}),\r\n ...(m.toolCallId ? { tool_call_id: m.toolCallId } : {}),\r\n })),\r\n temperature: o.temperature,\r\n max_tokens: o.maxTokens,\r\n tools: o.tools,\r\n tool_choice: o.toolChoice,\r\n stream,\r\n };\r\n }\r\n\r\n async chat(options: AiChatOptions): Promise<ApiResult<AiChatResult>> {\r\n // A model reply can take longer than the default request timeout.\r\n return this.client.request<AiChatResult>(this.path('chat'), { method: 'POST', body: this.body(options, false), timeout: 120_000 });\r\n }\r\n\r\n /**\r\n * Stream a reply: `delta` and `tool_call` events, then one `done` (or `error`).\r\n * Errors arrive as an `error` event, never as a throw. `signal`, or leaving\r\n * the loop early, cancels the stream.\r\n */\r\n async *chatStream(options: AiChatOptions, { signal }: { signal?: AbortSignal } = {}): AsyncGenerator<AiStreamEvent> {\r\n let res: Response;\r\n try {\r\n res = await this.client.authFetch(`${this.client.getBaseUrl()}${this.path('chat')}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', Accept: 'text/event-stream' },\r\n body: JSON.stringify(this.body(options, true)),\r\n signal,\r\n redirect: 'error',\r\n });\r\n } catch (err) {\r\n yield { type: 'error', code: 'NETWORK_ERROR', message: err instanceof Error ? err.message : 'Network error' };\r\n return;\r\n }\r\n if (!res.ok || !res.body) {\r\n const data = await res.json().catch(() => null);\r\n yield { type: 'error', code: data?.code, message: errorMessage(data, res.status), status: res.status, ...(data && typeof data === 'object' ? { details: data } : {}) };\r\n return;\r\n }\r\n try {\r\n for await (const raw of sseData(res.body)) yield JSON.parse(raw) as AiStreamEvent;\r\n } catch (err) {\r\n yield { type: 'error', code: 'STREAM_ERROR', message: err instanceof Error ? err.message : 'Stream interrupted' };\r\n }\r\n }\r\n\r\n /** 768-dimension vectors for one string or up to 100. */\r\n async embed(input: string | string[], options: { model?: string; provider?: AiProviderId } = {}): Promise<ApiResult<AiEmbedResult>> {\r\n return this.client.request<AiEmbedResult>(this.path('embeddings'), { method: 'POST', body: { input, ...options } });\r\n }\r\n\r\n /**\r\n * The workspace's prepaid AI credit as this project sees it. When a call is\r\n * paid from credit and none is left, it fails with 402 `CREDITS_EXHAUSTED`\r\n * (see `NestCoreError.isCreditsExhausted`, `reason` and `buyUrl`).\r\n */\r\n async credits(): Promise<ApiResult<AiCredits>> {\r\n return this.client.request<AiCredits>(this.path('credits'));\r\n }\r\n\r\n /** Models the project can call with its saved provider keys (and, `source: 'credits'`, those its workspace's credit pays for). */\r\n async models(): Promise<ApiResult<AiModel[]>> {\r\n return this.client.request<AiModel[]>(this.path('models'));\r\n }\r\n\r\n /** Service key: per-call traces (model, tokens, latency, cost, status), newest first. */\r\n async traces(options: { limit?: number; offset?: number; threadId?: string; status?: 'ok' | 'error' } = {}): Promise<ApiResult<{ traces: AiTrace[]; total: number; retentionDays: number }>> {\r\n const q = Object.entries(options).filter(([, v]) => v !== undefined).map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join('&');\r\n return this.client.request(this.path(`traces${q ? `?${q}` : ''}`));\r\n }\r\n}\r\n","import type { DatabaseClient } from './database';\n\n/** Postgres type → TypeScript type. Unknown types fall back to `unknown`. */\nconst PG_TO_TS: Record<string, string> = {\n int2: 'number', int4: 'number', int8: 'number',\n smallint: 'number', integer: 'number', bigint: 'number', serial: 'number', bigserial: 'number',\n numeric: 'number', decimal: 'number', real: 'number', float4: 'number', float8: 'number',\n 'double precision': 'number',\n bool: 'boolean', boolean: 'boolean',\n text: 'string', varchar: 'string', 'character varying': 'string',\n char: 'string', character: 'string', bpchar: 'string', citext: 'string', uuid: 'string',\n name: 'string',\n date: 'string', timestamp: 'string', timestamptz: 'string',\n 'timestamp with time zone': 'string', 'timestamp without time zone': 'string',\n time: 'string', timetz: 'string', interval: 'string',\n 'time without time zone': 'string', 'time with time zone': 'string',\n json: 'Record<string, unknown>', jsonb: 'Record<string, unknown>',\n bytea: 'string',\n // pgvector columns arrive in their text form, '[0.1,0.2,…]' (node-postgres has no vector parser).\n vector: 'string',\n};\n\nfunction tsType(pgType: string): string {\n const t = (pgType || '').toLowerCase().trim();\n if (!t) return 'unknown';\n if (t.endsWith('[]')) return `${tsType(t.slice(0, -2))}[]`;\n if (t.startsWith('_')) return `${tsType(t.slice(1))}[]`; // pg internal array name, e.g. _text\n return PG_TO_TS[t] ?? 'unknown';\n}\n\nfunction pascalCase(name: string): string {\n return name\n .replace(/(^|[_\\- ])([a-zA-Z0-9])/g, (_m, _s, c) => c.toUpperCase())\n .replace(/[^a-zA-Z0-9]/g, '');\n}\n\nfunction fieldKey(name: string): string {\n return /^[a-zA-Z_$][\\w$]*$/.test(name) ? name : JSON.stringify(name);\n}\n\nexport interface GenerateTypesOptions {\n /** Only include these tables (default: all non-internal). */\n tables?: string[];\n /** Tables to skip (default: `auth_*` and `_*` internal tables). */\n exclude?: RegExp;\n}\n\n\n/**\n * Introspect the project schema and emit TypeScript row interfaces plus a\n * `Database` map. Pair the output with `db.query<MyRow>(...)` or the query\n * builder for end-to-end type safety — NestCore's answer to\n * `supabase gen types typescript`.\n *\n * ```ts\n * import { writeFileSync } from 'fs';\n * writeFileSync('nestcore.types.ts', await generateTypes(client.db));\n * ```\n */\nexport async function generateTypes(db: DatabaseClient, opts: GenerateTypesOptions = {}): Promise<string> {\n const listed = await db.listTables();\n if (listed.error) throw new Error(`generateTypes: could not list tables: ${listed.error.message}`);\n\n const raw = listed.data as unknown;\n let tables: string[] = Array.isArray(raw)\n ? (raw as unknown[]).map((t) => (typeof t === 'string' ? t : (t as { name?: string })?.name)).filter((t): t is string => !!t)\n : ((raw as { tables?: string[] })?.tables ?? []);\n\n const exclude = opts.exclude ?? /^(auth_|_)/;\n tables = opts.tables ? tables.filter((t) => opts.tables!.includes(t)) : tables.filter((t) => !exclude.test(t));\n tables = Array.from(new Set(tables)).sort();\n\n const blocks: string[] = [];\n const dbEntries: string[] = [];\n\n for (const table of tables) {\n const meta = await db.getTableMetadata(table);\n if (meta.error) continue;\n const cols = meta.data.find((t) => t.table_name === table)?.columns ?? [];\n if (cols.length === 0) continue;\n\n const fields = cols.map((c) => {\n const opt = c.is_nullable ? '?' : '';\n const nullable = c.is_nullable ? ' | null' : '';\n return ` ${fieldKey(c.column_name)}${opt}: ${tsType(c.data_type)}${nullable};`;\n });\n const iface = pascalCase(table);\n blocks.push(`export interface ${iface} {\\n${fields.join('\\n')}\\n}`);\n dbEntries.push(` ${JSON.stringify(table)}: ${iface};`);\n }\n\n const header =\n '// Generated by @nestcorehq/node generateTypes — do not edit by hand.\\n' +\n '// Re-run after schema changes to keep types in sync.\\n';\n const dbMap = `export interface Database {\\n${dbEntries.join('\\n')}\\n}`;\n return `${header}\\n${blocks.join('\\n\\n')}\\n\\n${dbMap}\\n`;\n}\n\n// Exported for testing / advanced callers.\nexport const __typegenInternals = { tsType, pascalCase };\n","import type { ApiResult } from '../types';\n\n/**\n * Error thrown by {@link unwrap}. Carries the API error's `code` and HTTP\n * `status` so callers can branch on them — the throwing counterpart to the\n * `{ data, error }` result model (and a match for the Flutter SDK's\n * `NestCoreException`).\n */\nexport class NestCoreError extends Error {\n readonly code?: string;\n readonly status?: number;\n /** The API's error body, e.g. QUOTA_EXCEEDED's `quota`/`plan`/`limit`/`upgradeUrl`. */\n readonly details?: Record<string, unknown>;\n constructor(message: string, opts: { code?: string; status?: number; details?: Record<string, unknown> } = {}) {\n super(message);\n this.name = 'NestCoreError';\n this.code = opts.code;\n this.status = opts.status;\n this.details = opts.details;\n }\n\n /** 423: the project is paused (plan limit or owner) or compromised (kill switch). */\n get isProjectPaused(): boolean {\n return this.code === ProjectErrorCodes.PROJECT_PAUSED;\n }\n\n /** 402 QUOTA_EXCEEDED: where to send the user to upgrade, when the API gave one. */\n get upgradeUrl(): string | undefined {\n const u = this.details?.upgradeUrl;\n return typeof u === 'string' ? u : undefined;\n }\n\n /**\n * 402: an AI call paid from the workspace's prepaid credit found none left\n * (`reason` `balance`) or hit the monthly cap (`spend_cap`). Don't retry:\n * offer `buyUrl`, or adding the project's own provider key.\n */\n get isCreditsExhausted(): boolean {\n return this.code === ProjectErrorCodes.CREDITS_EXHAUSTED;\n }\n\n /**\n * Why the call was refused, when the API said: `balance` | `spend_cap` for\n * CREDITS_EXHAUSTED, the quota reason (e.g. `ai_collections`) for QUOTA_EXCEEDED.\n */\n get reason(): string | undefined {\n const r = this.details?.reason;\n return typeof r === 'string' ? r : undefined;\n }\n\n /** 402 CREDITS_EXHAUSTED: where to buy more AI credit. */\n get buyUrl(): string | undefined {\n const u = this.details?.buyUrl;\n return typeof u === 'string' ? u : undefined;\n }\n}\n\n/** @deprecated Renamed to {@link NestCoreError}; kept so existing `instanceof` checks keep working. */\nexport const OrbitNestError = NestCoreError;\n/** @deprecated Renamed to {@link NestCoreError}. */\nexport type OrbitNestError = NestCoreError;\n\n/**\n * Bridge the `{ data, error }` result model to a throwing style: returns\n * `data` on success, throws {@link NestCoreError} on error. Lets you opt into\n * `try/catch` ergonomics (as the Flutter SDK uses) without changing the default\n * non-throwing behavior of the SDK.\n *\n * ```ts\n * import { unwrap } from '@nestcorehq/js';\n *\n * try {\n * const { rows } = unwrap(await nestcore.db.from('posts').select());\n * } catch (e) {\n * if (e instanceof NestCoreError) console.error(e.code, e.status, e.message);\n * }\n * ```\n */\nexport function unwrap<T>(result: ApiResult<T>): T {\n if (result.error) {\n throw new NestCoreError(result.error.message, {\n code: result.error.code,\n status: result.error.status,\n details: result.error.details as Record<string, unknown> | undefined,\n });\n }\n return result.data as T;\n}\n\n/**\n * `error.code` values for the project auth limits (all HTTP 429). The limits\n * are set per project in Console → Settings → Auth Security.\n */\nexport const AuthLimitCodes = {\n /** Too many wrong codes: codes are invalidated for a while. */\n OTP_RATE_LIMITED: 'OTP_RATE_LIMITED',\n /** Too many codes requested for one email in the last hour. */\n OTP_REQUEST_LIMIT: 'OTP_REQUEST_LIMIT',\n /** Too many wrong passwords: the email is locked for the lockout duration. */\n ACCOUNT_LOCKED: 'ACCOUNT_LOCKED',\n} as const;\n\n/**\n * `error.code` values about the project itself (any endpoint can answer them).\n * Project kinds: an App project is a backend for an app; an Agent project is\n * the private backend of one PC running NestCore Agent.\n */\nexport const ProjectErrorCodes = {\n /**\n * 402: a plan limit was reached. `details` has quota, plan, limit, usage and, for projects, kind/reason/upgradeUrl\n * (`reason: 'ai_collections'` = the plan's collections per project).\n */\n QUOTA_EXCEEDED: 'QUOTA_EXCEEDED',\n /**\n * 402: an AI call paid from prepaid credit found none left. `details`: reason (`balance` | `spend_cap`),\n * balanceUsd, monthlyCapUsd, spentThisMonthUsd, buyUrl. See {@link CreditsExhaustedDetails}.\n */\n CREDITS_EXHAUSTED: 'CREDITS_EXHAUSTED',\n /** 423: the project is paused (downgrade or owner) or compromised (kill switch). Nothing was deleted. */\n PROJECT_PAUSED: 'PROJECT_PAUSED',\n /** 403: not allowed on an Agent project (only the agent schema, agent-* buckets and the agent functions). */\n AGENT_PROJECT_RESTRICTED: 'AGENT_PROJECT_RESTRICTED',\n /** 409: agent-only operation (agent template, agent functions) on an App project. */\n NOT_AN_AGENT_PROJECT: 'NOT_AN_AGENT_PROJECT',\n} as const;\n\n/** `balance`: no credit left; `spend_cap`: the workspace's monthly AI credit cap is reached. */\nexport type CreditsExhaustedReason = 'balance' | 'spend_cap';\n\n/** The body of a 402 `CREDITS_EXHAUSTED` (in `error.details` / `NestCoreError.details`). */\nexport interface CreditsExhaustedDetails {\n code: 'CREDITS_EXHAUSTED';\n reason: CreditsExhaustedReason;\n message: string;\n balanceUsd: number;\n monthlyCapUsd: number | null;\n spentThisMonthUsd: number;\n buyUrl: string;\n}\n"],"mappings":"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","names":["index_exports","__export","AiClient","AnalyticsClient","AuthClient","AuthLimitCodes","CollectionRef","CollectionsClient","DatabaseClient","EnvironmentClient","FunctionsClient","HttpClient","JobsClient","LoggingClient","MemoryClient","NestCoreError","OrbitNestError","PresenceManager","ProjectErrorCodes","RealtimeChannel","RealtimeClient","StorageBucket","StorageClient","ThreadRef","ThreadsClient","createClient","createLocalStorageAdapter","createMemoryAdapter","generateTypes","isMfaChallenge","localStorageAdapter","memoryAdapter","sanitizePath","unwrap","validatePath","validateRequired","__toCommonJS","HttpClient","config","fn","refresh","url","init","asUser","token","send","t","res","fresh","path","options","extraHeaders","accessToken","controller","timeoutId","response","data","errorMessage","err","body","status","fallback","b","m","nested","adminOnlyError","feature","DatabaseClient","client","table","column","query","opts","sql","params","result","col","tableName","options","data","rowId","rows","updates","conditions","policy","p","r","policyName","TableQueryBuilder","SAFE_IDENT","db","name","value","pattern","values","config","vector","n","op","dir","from","to","size","c","where","f","vals","v","cfg","fn","orderClauses","columns","res","row","filters","id","FunctionsClient","client","functionName","options","url","method","controller","timeoutId","response","contentType","data","headers","value","key","errorMessage","err","JobsClient","client","options","adminOnlyError","name","createMemoryAdapter","store","k","v","createLocalStorageAdapter","ls","probe","storage","memoryAdapter","localStorageAdapter","usableLocalStorage","isMfaChallenge","value","SESSION_KEY","LEGACY_SESSION_KEY","REFRESH_MARGIN_S","NO_SESSION","AuthClient","client","options","memoryAdapter","raw","legacyKey","withExpiry","cb","callback","s","staleToken","current","fn","token","credentials","result","challengeToken","code","phone","refreshToken","metadata","rest","user","session","opts","input","id","deviceName","friendlyName","factorId","LoggingClient","client","options","adminOnlyError","functionName","EnvironmentClient","client","adminOnlyError","name","value","options","variables","validateRequired","value","paramName","validatePath","path","required","segments","seg","sanitizePath","encodeStoragePath","path","seg","buildTransformQuery","t","p","MIME_BY_EXT","guessContentType","ext","unwrapEnvelope","result","body","StorageClient","client","bucket","StorageBucket","file","options","invalid","validatePath","formData","type","blob","url","controller","timeoutId","response","data","errorMessage","err","paths","prefix","params","query","baseUrl","projectSlug","publicUrl","qs","PresenceManager","channelName","client","state","cb","newState","prev","key","presences","RealtimeChannel","name","type","opts","event","payload","ws","schema","table","splitTableKey","keys","h","o","status","err","msg","events","normaliseEvents","matchesFilter","CONNECTING","OPEN","BASE_RECONNECT_DELAY_MS","RATE_LIMITED_RECONNECT_DELAY_MS","MAX_RECONNECT_DELAY_MS","WS_CLOSE_RATE_LIMITED","RealtimeClient","url","options","token","userJwt","resolve","reject","WS","protocols","handshakeOptions","ch","ev","code","jitter","delay","c","sameFilter","tableKeys","unsubscribe","channel","list","dot","row","filter","col","v","x","re","ANON_STORAGE_KEY","SESSION_ID_KEY","SESSION_TS_KEY","LEGACY_KEYS","SESSION_TIMEOUT_MS","generateUuid","bytes","hex","b","c","r","getLocalStorage","probe","readKey","store","key","value","legacy","old","detectPlatform","loadOrCreateAnonymousId","id","resolveSessionId","fallback","now","last","sid","AnalyticsClient","client","options","userId","traits","event","properties","screenName","error","msg","stack","keepalive","batch","result","status","entry","sseData","body","reader","decoder","buf","value","done","nl","line","MemoryClient","client","p","content","options","queryOrOptions","searchBody","items","queries","target","query","id","patch","subject","CollectionRef","name","source","wait","rest","res","chunks","documentId","timeoutMs","intervalMs","until","r","CollectionsClient","ThreadRef","signal","err","data","errorMessage","raw","sseData","ThreadsClient","o","q","v","k","AiClient","client","ThreadsClient","p","o","stream","m","options","signal","res","err","data","errorMessage","raw","sseData","input","q","v","k","PG_TO_TS","tsType","pgType","t","pascalCase","name","_m","_s","c","fieldKey","generateTypes","db","opts","listed","raw","tables","exclude","blocks","dbEntries","table","meta","cols","fields","opt","nullable","iface","header","dbMap","NestCoreError","message","opts","ProjectErrorCodes","u","r","OrbitNestError","unwrap","result","AuthLimitCodes","DEFAULT_BASE_URL","decodeProjectSlugFromJwt","token","parts","payload","json","decoded","slug","createClient","config","projectSlug","baseUrl","httpClient","HttpClient","wsUrl","auth","AuthClient","stale","followAuth","realtimeClient","RealtimeClient","s","DatabaseClient","FunctionsClient","JobsClient","LoggingClient","EnvironmentClient","StorageClient","name","AnalyticsClient","AiClient","MemoryClient","CollectionsClient"]}