wip:milestone 0 fixes
Some checks failed
CI/CD Pipeline / unit-tests (push) Failing after 1m16s
CI/CD Pipeline / integration-tests (push) Failing after 2m32s
CI/CD Pipeline / lint (push) Successful in 5m22s
CI/CD Pipeline / e2e-tests (push) Has been skipped
CI/CD Pipeline / build (push) Has been skipped

This commit is contained in:
2026-03-15 12:35:42 +02:00
parent 6708cf28a7
commit cffdf8af86
61266 changed files with 4511646 additions and 1938 deletions

View File

@@ -0,0 +1,589 @@
import type { AuthChangeEvent } from '@supabase/auth-js'
import { FunctionsClient } from '@supabase/functions-js'
import {
PostgrestClient,
type PostgrestFilterBuilder,
type PostgrestQueryBuilder,
} from '@supabase/postgrest-js'
import {
type RealtimeChannel,
type RealtimeChannelOptions,
RealtimeClient,
type RealtimeClientOptions,
} from '@supabase/realtime-js'
import { StorageClient as SupabaseStorageClient } from '@supabase/storage-js'
import {
DEFAULT_AUTH_OPTIONS,
DEFAULT_DB_OPTIONS,
DEFAULT_GLOBAL_OPTIONS,
DEFAULT_REALTIME_OPTIONS,
} from './lib/constants'
import { fetchWithAuth } from './lib/fetch'
import { applySettingDefaults, validateSupabaseUrl } from './lib/helpers'
import { SupabaseAuthClient } from './lib/SupabaseAuthClient'
import type {
Fetch,
GenericSchema,
SupabaseAuthClientOptions,
SupabaseClientOptions,
} from './lib/types'
import { GetRpcFunctionFilterBuilderByArgs } from './lib/rest/types/common/rpc'
/**
* Supabase Client.
*
* An isomorphic Javascript client for interacting with Postgres.
*/
export default class SupabaseClient<
Database = any,
// The second type parameter is also used for specifying db_schema, so we
// support both cases.
// TODO: Allow setting db_schema from ClientOptions.
SchemaNameOrClientOptions extends
| (string & keyof Omit<Database, '__InternalSupabase'>)
| { PostgrestVersion: string } = 'public' extends keyof Omit<Database, '__InternalSupabase'>
? 'public'
: string & keyof Omit<Database, '__InternalSupabase'>,
SchemaName extends string &
keyof Omit<Database, '__InternalSupabase'> = SchemaNameOrClientOptions extends string &
keyof Omit<Database, '__InternalSupabase'>
? SchemaNameOrClientOptions
: 'public' extends keyof Omit<Database, '__InternalSupabase'>
? 'public'
: string & keyof Omit<Omit<Database, '__InternalSupabase'>, '__InternalSupabase'>,
Schema extends Omit<Database, '__InternalSupabase'>[SchemaName] extends GenericSchema
? Omit<Database, '__InternalSupabase'>[SchemaName]
: never = Omit<Database, '__InternalSupabase'>[SchemaName] extends GenericSchema
? Omit<Database, '__InternalSupabase'>[SchemaName]
: never,
ClientOptions extends { PostgrestVersion: string } = SchemaNameOrClientOptions extends string &
keyof Omit<Database, '__InternalSupabase'>
? // If the version isn't explicitly set, look for it in the __InternalSupabase object to infer the right version
Database extends { __InternalSupabase: { PostgrestVersion: string } }
? Database['__InternalSupabase']
: // otherwise default to 12
{ PostgrestVersion: '12' }
: SchemaNameOrClientOptions extends { PostgrestVersion: string }
? SchemaNameOrClientOptions
: never,
> {
/**
* Supabase Auth allows you to create and manage user sessions for access to data that is secured by access policies.
*/
auth: SupabaseAuthClient
realtime: RealtimeClient
/**
* Supabase Storage allows you to manage user-generated content, such as photos or videos.
*/
storage: SupabaseStorageClient
protected realtimeUrl: URL
protected authUrl: URL
protected storageUrl: URL
protected functionsUrl: URL
protected rest: PostgrestClient<Database, ClientOptions, SchemaName>
protected storageKey: string
protected fetch?: Fetch
protected changedAccessToken?: string
protected accessToken?: () => Promise<string | null>
protected headers: Record<string, string>
/**
* Create a new client for use in the browser.
*
* @category Initializing
*
* @param supabaseUrl The unique Supabase URL which is supplied when you create a new project in your project dashboard.
* @param supabaseKey The unique Supabase Key which is supplied when you create a new project in your project dashboard.
* @param options.db.schema You can switch in between schemas. The schema needs to be on the list of exposed schemas inside Supabase.
* @param options.auth.autoRefreshToken Set to "true" if you want to automatically refresh the token before expiring.
* @param options.auth.persistSession Set to "true" if you want to automatically save the user session into local storage.
* @param options.auth.detectSessionInUrl Set to "true" if you want to automatically detects OAuth grants in the URL and signs in the user.
* @param options.realtime Options passed along to realtime-js constructor.
* @param options.storage Options passed along to the storage-js constructor.
* @param options.global.fetch A custom fetch implementation.
* @param options.global.headers Any additional headers to send with each network request.
*
* @example Creating a client
* ```js
* import { createClient } from '@supabase/supabase-js'
*
* // Create a single supabase client for interacting with your database
* const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key')
* ```
*
* @example With a custom domain
* ```js
* import { createClient } from '@supabase/supabase-js'
*
* // Use a custom domain as the supabase URL
* const supabase = createClient('https://my-custom-domain.com', 'publishable-or-anon-key')
* ```
*
* @example With additional parameters
* ```js
* import { createClient } from '@supabase/supabase-js'
*
* const options = {
* db: {
* schema: 'public',
* },
* auth: {
* autoRefreshToken: true,
* persistSession: true,
* detectSessionInUrl: true
* },
* global: {
* headers: { 'x-my-custom-header': 'my-app-name' },
* },
* }
* const supabase = createClient("https://xyzcompany.supabase.co", "publishable-or-anon-key", options)
* ```
*
* @exampleDescription With custom schemas
* By default the API server points to the `public` schema. You can enable other database schemas within the Dashboard.
* Go to [Settings > API > Exposed schemas](/dashboard/project/_/settings/api) and add the schema which you want to expose to the API.
*
* Note: each client connection can only access a single schema, so the code above can access the `other_schema` schema but cannot access the `public` schema.
*
* @example With custom schemas
* ```js
* import { createClient } from '@supabase/supabase-js'
*
* const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key', {
* // Provide a custom schema. Defaults to "public".
* db: { schema: 'other_schema' }
* })
* ```
*
* @exampleDescription Custom fetch implementation
* `supabase-js` uses the [`cross-fetch`](https://www.npmjs.com/package/cross-fetch) library to make HTTP requests,
* but an alternative `fetch` implementation can be provided as an option.
* This is most useful in environments where `cross-fetch` is not compatible (for instance Cloudflare Workers).
*
* @example Custom fetch implementation
* ```js
* import { createClient } from '@supabase/supabase-js'
*
* const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key', {
* global: { fetch: fetch.bind(globalThis) }
* })
* ```
*
* @exampleDescription React Native options with AsyncStorage
* For React Native we recommend using `AsyncStorage` as the storage implementation for Supabase Auth.
*
* @example React Native options with AsyncStorage
* ```js
* import 'react-native-url-polyfill/auto'
* import { createClient } from '@supabase/supabase-js'
* import AsyncStorage from "@react-native-async-storage/async-storage";
*
* const supabase = createClient("https://xyzcompany.supabase.co", "publishable-or-anon-key", {
* auth: {
* storage: AsyncStorage,
* autoRefreshToken: true,
* persistSession: true,
* detectSessionInUrl: false,
* },
* });
* ```
*
* @exampleDescription React Native options with Expo SecureStore
* If you wish to encrypt the user's session information, you can use `aes-js` and store the encryption key in Expo SecureStore.
* The `aes-js` library, a reputable JavaScript-only implementation of the AES encryption algorithm in CTR mode.
* A new 256-bit encryption key is generated using the `react-native-get-random-values` library.
* This key is stored inside Expo's SecureStore, while the value is encrypted and placed inside AsyncStorage.
*
* Please make sure that:
* - You keep the `expo-secure-store`, `aes-js` and `react-native-get-random-values` libraries up-to-date.
* - Choose the correct [`SecureStoreOptions`](https://docs.expo.dev/versions/latest/sdk/securestore/#securestoreoptions) for your app's needs.
* E.g. [`SecureStore.WHEN_UNLOCKED`](https://docs.expo.dev/versions/latest/sdk/securestore/#securestorewhen_unlocked) regulates when the data can be accessed.
* - Carefully consider optimizations or other modifications to the above example, as those can lead to introducing subtle security vulnerabilities.
*
* @example React Native options with Expo SecureStore
* ```ts
* import 'react-native-url-polyfill/auto'
* import { createClient } from '@supabase/supabase-js'
* import AsyncStorage from '@react-native-async-storage/async-storage';
* import * as SecureStore from 'expo-secure-store';
* import * as aesjs from 'aes-js';
* import 'react-native-get-random-values';
*
* // As Expo's SecureStore does not support values larger than 2048
* // bytes, an AES-256 key is generated and stored in SecureStore, while
* // it is used to encrypt/decrypt values stored in AsyncStorage.
* class LargeSecureStore {
* private async _encrypt(key: string, value: string) {
* const encryptionKey = crypto.getRandomValues(new Uint8Array(256 / 8));
*
* const cipher = new aesjs.ModeOfOperation.ctr(encryptionKey, new aesjs.Counter(1));
* const encryptedBytes = cipher.encrypt(aesjs.utils.utf8.toBytes(value));
*
* await SecureStore.setItemAsync(key, aesjs.utils.hex.fromBytes(encryptionKey));
*
* return aesjs.utils.hex.fromBytes(encryptedBytes);
* }
*
* private async _decrypt(key: string, value: string) {
* const encryptionKeyHex = await SecureStore.getItemAsync(key);
* if (!encryptionKeyHex) {
* return encryptionKeyHex;
* }
*
* const cipher = new aesjs.ModeOfOperation.ctr(aesjs.utils.hex.toBytes(encryptionKeyHex), new aesjs.Counter(1));
* const decryptedBytes = cipher.decrypt(aesjs.utils.hex.toBytes(value));
*
* return aesjs.utils.utf8.fromBytes(decryptedBytes);
* }
*
* async getItem(key: string) {
* const encrypted = await AsyncStorage.getItem(key);
* if (!encrypted) { return encrypted; }
*
* return await this._decrypt(key, encrypted);
* }
*
* async removeItem(key: string) {
* await AsyncStorage.removeItem(key);
* await SecureStore.deleteItemAsync(key);
* }
*
* async setItem(key: string, value: string) {
* const encrypted = await this._encrypt(key, value);
*
* await AsyncStorage.setItem(key, encrypted);
* }
* }
*
* const supabase = createClient("https://xyzcompany.supabase.co", "publishable-or-anon-key", {
* auth: {
* storage: new LargeSecureStore(),
* autoRefreshToken: true,
* persistSession: true,
* detectSessionInUrl: false,
* },
* });
* ```
*
* @example With a database query
* ```ts
* import { createClient } from '@supabase/supabase-js'
*
* const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
*
* const { data } = await supabase.from('profiles').select('*')
* ```
*/
constructor(
protected supabaseUrl: string,
protected supabaseKey: string,
options?: SupabaseClientOptions<SchemaName>
) {
const baseUrl = validateSupabaseUrl(supabaseUrl)
if (!supabaseKey) throw new Error('supabaseKey is required.')
this.realtimeUrl = new URL('realtime/v1', baseUrl)
this.realtimeUrl.protocol = this.realtimeUrl.protocol.replace('http', 'ws')
this.authUrl = new URL('auth/v1', baseUrl)
this.storageUrl = new URL('storage/v1', baseUrl)
this.functionsUrl = new URL('functions/v1', baseUrl)
// default storage key uses the supabase project ref as a namespace
const defaultStorageKey = `sb-${baseUrl.hostname.split('.')[0]}-auth-token`
const DEFAULTS = {
db: DEFAULT_DB_OPTIONS,
realtime: DEFAULT_REALTIME_OPTIONS,
auth: { ...DEFAULT_AUTH_OPTIONS, storageKey: defaultStorageKey },
global: DEFAULT_GLOBAL_OPTIONS,
}
const settings = applySettingDefaults(options ?? {}, DEFAULTS)
this.storageKey = settings.auth.storageKey ?? ''
this.headers = settings.global.headers ?? {}
if (!settings.accessToken) {
this.auth = this._initSupabaseAuthClient(
settings.auth ?? {},
this.headers,
settings.global.fetch
)
} else {
this.accessToken = settings.accessToken
this.auth = new Proxy<SupabaseAuthClient>({} as any, {
get: (_, prop) => {
throw new Error(
`@supabase/supabase-js: Supabase Client is configured with the accessToken option, accessing supabase.auth.${String(
prop
)} is not possible`
)
},
})
}
this.fetch = fetchWithAuth(supabaseKey, this._getAccessToken.bind(this), settings.global.fetch)
this.realtime = this._initRealtimeClient({
headers: this.headers,
accessToken: this._getAccessToken.bind(this),
...settings.realtime,
})
if (this.accessToken) {
// Start auth immediately to avoid race condition with channel subscriptions
// Wrap Promise to avoid Firefox extension cross-context Promise access errors
Promise.resolve(this.accessToken())
.then((token) => this.realtime.setAuth(token))
.catch((e) => console.warn('Failed to set initial Realtime auth token:', e))
}
this.rest = new PostgrestClient(new URL('rest/v1', baseUrl).href, {
headers: this.headers,
schema: settings.db.schema,
fetch: this.fetch,
timeout: settings.db.timeout,
urlLengthLimit: settings.db.urlLengthLimit,
})
this.storage = new SupabaseStorageClient(
this.storageUrl.href,
this.headers,
this.fetch,
options?.storage
)
if (!settings.accessToken) {
this._listenForAuthEvents()
}
}
/**
* Supabase Functions allows you to deploy and invoke edge functions.
*/
get functions(): FunctionsClient {
return new FunctionsClient(this.functionsUrl.href, {
headers: this.headers,
customFetch: this.fetch,
})
}
// NOTE: signatures must be kept in sync with PostgrestClient.from
from<
TableName extends string & keyof Schema['Tables'],
Table extends Schema['Tables'][TableName],
>(relation: TableName): PostgrestQueryBuilder<ClientOptions, Schema, Table, TableName>
from<ViewName extends string & keyof Schema['Views'], View extends Schema['Views'][ViewName]>(
relation: ViewName
): PostgrestQueryBuilder<ClientOptions, Schema, View, ViewName>
/**
* Perform a query on a table or a view.
*
* @param relation - The table or view name to query
*/
from(relation: string): PostgrestQueryBuilder<ClientOptions, Schema, any> {
return this.rest.from(relation)
}
// NOTE: signatures must be kept in sync with PostgrestClient.schema
/**
* Select a schema to query or perform an function (rpc) call.
*
* The schema needs to be on the list of exposed schemas inside Supabase.
*
* @param schema - The schema to query
*/
schema<DynamicSchema extends string & keyof Omit<Database, '__InternalSupabase'>>(
schema: DynamicSchema
): PostgrestClient<
Database,
ClientOptions,
DynamicSchema,
Database[DynamicSchema] extends GenericSchema ? Database[DynamicSchema] : any
> {
return this.rest.schema<DynamicSchema>(schema)
}
// NOTE: signatures must be kept in sync with PostgrestClient.rpc
/**
* Perform a function call.
*
* @param fn - The function name to call
* @param args - The arguments to pass to the function call
* @param options - Named parameters
* @param options.head - When set to `true`, `data` will not be returned.
* Useful if you only need the count.
* @param options.get - When set to `true`, the function will be called with
* read-only access mode.
* @param options.count - Count algorithm to use to count rows returned by the
* function. Only applicable for [set-returning
* functions](https://www.postgresql.org/docs/current/functions-srf.html).
*
* `"exact"`: Exact but slow count algorithm. Performs a `COUNT(*)` under the
* hood.
*
* `"planned"`: Approximated but fast count algorithm. Uses the Postgres
* statistics under the hood.
*
* `"estimated"`: Uses exact count for low numbers and planned count for high
* numbers.
*/
rpc<
FnName extends string & keyof Schema['Functions'],
Args extends Schema['Functions'][FnName]['Args'] = never,
FilterBuilder extends GetRpcFunctionFilterBuilderByArgs<
Schema,
FnName,
Args
> = GetRpcFunctionFilterBuilderByArgs<Schema, FnName, Args>,
>(
fn: FnName,
args: Args = {} as Args,
options: {
head?: boolean
get?: boolean
count?: 'exact' | 'planned' | 'estimated'
} = {
head: false,
get: false,
count: undefined,
}
): PostgrestFilterBuilder<
ClientOptions,
Schema,
FilterBuilder['Row'],
FilterBuilder['Result'],
FilterBuilder['RelationName'],
FilterBuilder['Relationships'],
'RPC'
> {
return this.rest.rpc(fn, args, options) as unknown as PostgrestFilterBuilder<
ClientOptions,
Schema,
FilterBuilder['Row'],
FilterBuilder['Result'],
FilterBuilder['RelationName'],
FilterBuilder['Relationships'],
'RPC'
>
}
/**
* Creates a Realtime channel with Broadcast, Presence, and Postgres Changes.
*
* @param {string} name - The name of the Realtime channel.
* @param {Object} opts - The options to pass to the Realtime channel.
*
*/
channel(name: string, opts: RealtimeChannelOptions = { config: {} }): RealtimeChannel {
return this.realtime.channel(name, opts)
}
/**
* Returns all Realtime channels.
*/
getChannels(): RealtimeChannel[] {
return this.realtime.getChannels()
}
/**
* Unsubscribes and removes Realtime channel from Realtime client.
*
* @param {RealtimeChannel} channel - The name of the Realtime channel.
*
*/
removeChannel(channel: RealtimeChannel): Promise<'ok' | 'timed out' | 'error'> {
return this.realtime.removeChannel(channel)
}
/**
* Unsubscribes and removes all Realtime channels from Realtime client.
*/
removeAllChannels(): Promise<('ok' | 'timed out' | 'error')[]> {
return this.realtime.removeAllChannels()
}
private async _getAccessToken() {
if (this.accessToken) {
return await this.accessToken()
}
const { data } = await this.auth.getSession()
return data.session?.access_token ?? this.supabaseKey
}
private _initSupabaseAuthClient(
{
autoRefreshToken,
persistSession,
detectSessionInUrl,
storage,
userStorage,
storageKey,
flowType,
lock,
debug,
throwOnError,
}: SupabaseAuthClientOptions,
headers?: Record<string, string>,
fetch?: Fetch
) {
const authHeaders = {
Authorization: `Bearer ${this.supabaseKey}`,
apikey: `${this.supabaseKey}`,
}
return new SupabaseAuthClient({
url: this.authUrl.href,
headers: { ...authHeaders, ...headers },
storageKey: storageKey,
autoRefreshToken,
persistSession,
detectSessionInUrl,
storage,
userStorage,
flowType,
lock,
debug,
throwOnError,
fetch,
// auth checks if there is a custom authorizaiton header using this flag
// so it knows whether to return an error when getUser is called with no session
hasCustomAuthorizationHeader: Object.keys(this.headers).some(
(key) => key.toLowerCase() === 'authorization'
),
})
}
private _initRealtimeClient(options: RealtimeClientOptions) {
return new RealtimeClient(this.realtimeUrl.href, {
...options,
params: { ...{ apikey: this.supabaseKey }, ...options?.params },
})
}
private _listenForAuthEvents() {
const data = this.auth.onAuthStateChange((event, session) => {
this._handleTokenChanged(event, 'CLIENT', session?.access_token)
})
return data
}
private _handleTokenChanged(
event: AuthChangeEvent,
source: 'CLIENT' | 'STORAGE',
token?: string
) {
if (
(event === 'TOKEN_REFRESHED' || event === 'SIGNED_IN') &&
this.changedAccessToken !== token
) {
this.changedAccessToken = token
this.realtime.setAuth(token)
} else if (event === 'SIGNED_OUT') {
this.realtime.setAuth()
if (source == 'STORAGE') this.auth.signOut()
this.changedAccessToken = undefined
}
}
}

View File

@@ -0,0 +1,75 @@
/**
* Canonical CORS configuration for Supabase Edge Functions
*
* This module exports CORS headers that stay synchronized with the Supabase SDK.
* When new headers are added to the SDK, they are automatically included here,
* preventing CORS errors in Edge Functions.
*
* @example Basic usage
* ```typescript
* import { corsHeaders } from '@supabase/supabase-js/cors'
*
* Deno.serve(async (req) => {
* if (req.method === 'OPTIONS') {
* return new Response('ok', { headers: corsHeaders })
* }
*
* return new Response(
* JSON.stringify({ data: 'Hello' }),
* { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
* )
* })
* ```
*
* @module cors
*/
/**
* All custom headers sent by the Supabase SDK.
* These headers need to be included in CORS configuration to prevent preflight failures.
*
* Headers:
* - authorization: Bearer token for authentication
* - x-client-info: Library version information
* - apikey: Project API key
* - content-type: Standard HTTP content type
*/
const SUPABASE_HEADERS = ['authorization', 'x-client-info', 'apikey', 'content-type'].join(', ')
/**
* All HTTP methods used by the Supabase SDK
*/
const SUPABASE_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'].join(', ')
/**
* Type representing CORS headers as a record of header names to values
*/
export type CorsHeaders = Record<string, string>
/**
* Default CORS headers for Supabase Edge Functions.
*
* Includes all headers sent by Supabase client libraries and allows all standard HTTP methods.
* Use this for simple CORS configurations with wildcard origin.
*
* @example
* ```typescript
* import { corsHeaders } from '@supabase/supabase-js/cors'
*
* Deno.serve(async (req) => {
* if (req.method === 'OPTIONS') {
* return new Response('ok', { headers: corsHeaders })
* }
*
* return new Response(
* JSON.stringify({ data: 'Hello' }),
* { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
* )
* })
* ```
*/
export const corsHeaders: CorsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': SUPABASE_HEADERS,
'Access-Control-Allow-Methods': SUPABASE_METHODS,
}

View File

@@ -0,0 +1,101 @@
import SupabaseClient from './SupabaseClient'
import type { SupabaseClientOptions } from './lib/types'
export * from '@supabase/auth-js'
export type { User as AuthUser, Session as AuthSession } from '@supabase/auth-js'
export type {
PostgrestResponse,
PostgrestSingleResponse,
PostgrestMaybeSingleResponse,
} from '@supabase/postgrest-js'
export { PostgrestError } from '@supabase/postgrest-js'
export type { FunctionInvokeOptions } from '@supabase/functions-js'
export {
FunctionsHttpError,
FunctionsFetchError,
FunctionsRelayError,
FunctionsError,
FunctionRegion,
} from '@supabase/functions-js'
export * from '@supabase/realtime-js'
export { default as SupabaseClient } from './SupabaseClient'
export type {
SupabaseClientOptions,
QueryResult,
QueryData,
QueryError,
DatabaseWithoutInternals,
} from './lib/types'
/**
* Creates a new Supabase Client.
*
* @example
* ```ts
* import { createClient } from '@supabase/supabase-js'
*
* const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
* const { data, error } = await supabase.from('profiles').select('*')
* ```
*/
export const createClient = <
Database = any,
SchemaNameOrClientOptions extends
| (string & keyof Omit<Database, '__InternalSupabase'>)
| { PostgrestVersion: string } = 'public' extends keyof Omit<Database, '__InternalSupabase'>
? 'public'
: string & keyof Omit<Database, '__InternalSupabase'>,
SchemaName extends string &
keyof Omit<Database, '__InternalSupabase'> = SchemaNameOrClientOptions extends string &
keyof Omit<Database, '__InternalSupabase'>
? SchemaNameOrClientOptions
: 'public' extends keyof Omit<Database, '__InternalSupabase'>
? 'public'
: string & keyof Omit<Omit<Database, '__InternalSupabase'>, '__InternalSupabase'>,
>(
supabaseUrl: string,
supabaseKey: string,
options?: SupabaseClientOptions<SchemaName>
): SupabaseClient<Database, SchemaNameOrClientOptions, SchemaName> => {
return new SupabaseClient<Database, SchemaNameOrClientOptions, SchemaName>(
supabaseUrl,
supabaseKey,
options
)
}
// Check for Node.js <= 18 deprecation
function shouldShowDeprecationWarning(): boolean {
// Skip in browser environments
if (typeof window !== 'undefined') {
return false
}
// Skip if process is not available (e.g., Edge Runtime)
// Use dynamic property access to avoid Next.js Edge Runtime static analysis warnings
const _process = (globalThis as any)['process']
if (!_process) {
return false
}
const processVersion = _process['version']
if (processVersion === undefined || processVersion === null) {
return false
}
const versionMatch = processVersion.match(/^v(\d+)\./)
if (!versionMatch) {
return false
}
const majorVersion = parseInt(versionMatch[1], 10)
return majorVersion <= 18
}
if (shouldShowDeprecationWarning()) {
console.warn(
`⚠️ Node.js 18 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. ` +
`Please upgrade to Node.js 20 or later. ` +
`For more information, visit: https://github.com/orgs/supabase/discussions/37217`
)
}

View File

@@ -0,0 +1,8 @@
import { AuthClient } from '@supabase/auth-js'
import { SupabaseAuthClientOptions } from './types'
export class SupabaseAuthClient extends AuthClient {
constructor(options: SupabaseAuthClientOptions) {
super(options)
}
}

View File

@@ -0,0 +1,35 @@
// constants.ts
import { RealtimeClientOptions } from '@supabase/realtime-js'
import { SupabaseAuthClientOptions } from './types'
import { version } from './version'
let JS_ENV = ''
// @ts-ignore
if (typeof Deno !== 'undefined') {
JS_ENV = 'deno'
} else if (typeof document !== 'undefined') {
JS_ENV = 'web'
} else if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
JS_ENV = 'react-native'
} else {
JS_ENV = 'node'
}
export const DEFAULT_HEADERS = { 'X-Client-Info': `supabase-js-${JS_ENV}/${version}` }
export const DEFAULT_GLOBAL_OPTIONS = {
headers: DEFAULT_HEADERS,
}
export const DEFAULT_DB_OPTIONS = {
schema: 'public',
}
export const DEFAULT_AUTH_OPTIONS: SupabaseAuthClientOptions = {
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true,
flowType: 'implicit',
}
export const DEFAULT_REALTIME_OPTIONS: RealtimeClientOptions = {}

View File

@@ -0,0 +1,36 @@
type Fetch = typeof fetch
export const resolveFetch = (customFetch?: Fetch): Fetch => {
if (customFetch) {
return (...args: Parameters<Fetch>) => customFetch(...args)
}
return (...args: Parameters<Fetch>) => fetch(...args)
}
export const resolveHeadersConstructor = () => {
return Headers
}
export const fetchWithAuth = (
supabaseKey: string,
getAccessToken: () => Promise<string | null>,
customFetch?: Fetch
): Fetch => {
const fetch = resolveFetch(customFetch)
const HeadersConstructor = resolveHeadersConstructor()
return async (input, init) => {
const accessToken = (await getAccessToken()) ?? supabaseKey
let headers = new HeadersConstructor(init?.headers)
if (!headers.has('apikey')) {
headers.set('apikey', supabaseKey)
}
if (!headers.has('Authorization')) {
headers.set('Authorization', `Bearer ${accessToken}`)
}
return fetch(input, { ...init, headers })
}
}

View File

@@ -0,0 +1,98 @@
// helpers.ts
import { SupabaseClientOptions } from './types'
export function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == 'x' ? r : (r & 0x3) | 0x8
return v.toString(16)
})
}
export function ensureTrailingSlash(url: string): string {
return url.endsWith('/') ? url : url + '/'
}
export const isBrowser = () => typeof window !== 'undefined'
export function applySettingDefaults<
Database = any,
SchemaName extends string & keyof Database = 'public' extends keyof Database
? 'public'
: string & keyof Database,
>(
options: SupabaseClientOptions<SchemaName>,
defaults: SupabaseClientOptions<any>
): Required<SupabaseClientOptions<SchemaName>> {
const {
db: dbOptions,
auth: authOptions,
realtime: realtimeOptions,
global: globalOptions,
} = options
const {
db: DEFAULT_DB_OPTIONS,
auth: DEFAULT_AUTH_OPTIONS,
realtime: DEFAULT_REALTIME_OPTIONS,
global: DEFAULT_GLOBAL_OPTIONS,
} = defaults
const result: Required<SupabaseClientOptions<SchemaName>> = {
db: {
...DEFAULT_DB_OPTIONS,
...dbOptions,
},
auth: {
...DEFAULT_AUTH_OPTIONS,
...authOptions,
},
realtime: {
...DEFAULT_REALTIME_OPTIONS,
...realtimeOptions,
},
storage: {},
global: {
...DEFAULT_GLOBAL_OPTIONS,
...globalOptions,
headers: {
...(DEFAULT_GLOBAL_OPTIONS?.headers ?? {}),
...(globalOptions?.headers ?? {}),
},
},
accessToken: async () => '',
}
if (options.accessToken) {
result.accessToken = options.accessToken
} else {
// hack around Required<>
delete (result as any).accessToken
}
return result
}
/**
* Validates a Supabase client URL
*
* @param {string} supabaseUrl - The Supabase client URL string.
* @returns {URL} - The validated base URL.
* @throws {Error}
*/
export function validateSupabaseUrl(supabaseUrl: string): URL {
const trimmedUrl = supabaseUrl?.trim()
if (!trimmedUrl) {
throw new Error('supabaseUrl is required.')
}
if (!trimmedUrl.match(/^https?:\/\//i)) {
throw new Error('Invalid supabaseUrl: Must be a valid HTTP or HTTPS URL.')
}
try {
return new URL(ensureTrailingSlash(trimmedUrl))
} catch {
throw Error('Invalid supabaseUrl: Provided URL is malformed.')
}
}

View File

@@ -0,0 +1,66 @@
/**
* AUTO-GENERATED FILE - DO NOT EDIT
*
* This file is automatically synchronized from @supabase/postgrest-js
* Source: packages/core/postgrest-js/src/types/common/
*
* To update this file, modify the source in postgrest-js and run:
* npm run codegen
*/
// Types that are shared between supabase-js and postgrest-js
export type Fetch = typeof fetch
export type GenericRelationship = {
foreignKeyName: string
columns: string[]
isOneToOne?: boolean
referencedRelation: string
referencedColumns: string[]
}
export type GenericTable = {
Row: Record<string, unknown>
Insert: Record<string, unknown>
Update: Record<string, unknown>
Relationships: GenericRelationship[]
}
export type GenericUpdatableView = {
Row: Record<string, unknown>
Insert: Record<string, unknown>
Update: Record<string, unknown>
Relationships: GenericRelationship[]
}
export type GenericNonUpdatableView = {
Row: Record<string, unknown>
Relationships: GenericRelationship[]
}
export type GenericView = GenericUpdatableView | GenericNonUpdatableView
export type GenericSetofOption = {
isSetofReturn?: boolean | undefined
isOneToOne?: boolean | undefined
isNotNullable?: boolean | undefined
to: string
from: string
}
export type GenericFunction = {
Args: Record<string, unknown> | never
Returns: unknown
SetofOptions?: GenericSetofOption
}
export type GenericSchema = {
Tables: Record<string, GenericTable>
Views: Record<string, GenericView>
Functions: Record<string, GenericFunction>
}
export type ClientServerOptions = {
PostgrestVersion?: string
}

View File

@@ -0,0 +1,158 @@
/**
* AUTO-GENERATED FILE - DO NOT EDIT
*
* This file is automatically synchronized from @supabase/postgrest-js
* Source: packages/core/postgrest-js/src/types/common/
*
* To update this file, modify the source in postgrest-js and run:
* npm run codegen
*/
import type { GenericFunction, GenericSchema, GenericSetofOption } from './common'
// Functions matching utils
type IsMatchingArgs<
FnArgs extends GenericFunction['Args'],
PassedArgs extends GenericFunction['Args'],
> = [FnArgs] extends [Record<PropertyKey, never>]
? PassedArgs extends Record<PropertyKey, never>
? true
: false
: keyof PassedArgs extends keyof FnArgs
? PassedArgs extends FnArgs
? true
: false
: false
type MatchingFunctionArgs<
Fn extends GenericFunction,
Args extends GenericFunction['Args'],
> = Fn extends { Args: infer A extends GenericFunction['Args'] }
? IsMatchingArgs<A, Args> extends true
? Fn
: never
: false
type FindMatchingFunctionByArgs<
FnUnion,
Args extends GenericFunction['Args'],
> = FnUnion extends infer Fn extends GenericFunction ? MatchingFunctionArgs<Fn, Args> : false
// Types for working with database schemas
type TablesAndViews<Schema extends GenericSchema> = Schema['Tables'] & Exclude<Schema['Views'], ''>
// Utility types for working with unions
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
? I
: never
type LastOf<T> =
UnionToIntersection<T extends any ? () => T : never> extends () => infer R ? R : never
type IsAny<T> = 0 extends 1 & T ? true : false
type ExactMatch<T, S> = [T] extends [S] ? ([S] extends [T] ? true : false) : false
type ExtractExactFunction<Fns, Args> = Fns extends infer F
? F extends GenericFunction
? ExactMatch<F['Args'], Args> extends true
? F
: never
: never
: never
type IsNever<T> = [T] extends [never] ? true : false
type RpcFunctionNotFound<FnName> = {
Row: any
Result: {
error: true
} & "Couldn't infer function definition matching provided arguments"
RelationName: FnName
Relationships: null
}
type CrossSchemaError<TableRef extends string> = {
error: true
} & `Function returns SETOF from a different schema ('${TableRef}'). Use .overrideTypes<YourReturnType>() to specify the return type explicitly.`
export type GetRpcFunctionFilterBuilderByArgs<
Schema extends GenericSchema,
FnName extends string & keyof Schema['Functions'],
Args,
> = {
0: Schema['Functions'][FnName]
// If the Args is exactly never (function call without any params)
1: IsAny<Schema> extends true
? any
: IsNever<Args> extends true
? // This is for retro compatibility, if the funcition is defined with an single return and an union of Args
// we fallback to the last function definition matched by name
IsNever<ExtractExactFunction<Schema['Functions'][FnName], Args>> extends true
? LastOf<Schema['Functions'][FnName]>
: ExtractExactFunction<Schema['Functions'][FnName], Args>
: Args extends Record<PropertyKey, never>
? LastOf<Schema['Functions'][FnName]>
: // Otherwise, we attempt to match with one of the function definition in the union based
// on the function arguments provided
Args extends GenericFunction['Args']
? // This is for retro compatibility, if the funcition is defined with an single return and an union of Args
// we fallback to the last function definition matched by name
IsNever<
LastOf<FindMatchingFunctionByArgs<Schema['Functions'][FnName], Args>>
> extends true
? LastOf<Schema['Functions'][FnName]>
: // Otherwise, we use the arguments based function definition narrowing to get the right value
LastOf<FindMatchingFunctionByArgs<Schema['Functions'][FnName], Args>>
: // If we can't find a matching function by args, we try to find one by function name
ExtractExactFunction<Schema['Functions'][FnName], Args> extends GenericFunction
? ExtractExactFunction<Schema['Functions'][FnName], Args>
: any
}[1] extends infer Fn
? // If we are dealing with an non-typed client everything is any
IsAny<Fn> extends true
? { Row: any; Result: any; RelationName: FnName; Relationships: null }
: // Otherwise, we use the arguments based function definition narrowing to get the right value
Fn extends GenericFunction
? {
Row: Fn['SetofOptions'] extends GenericSetofOption
? Fn['SetofOptions']['to'] extends keyof TablesAndViews<Schema>
? TablesAndViews<Schema>[Fn['SetofOptions']['to']]['Row']
: // Cross-schema fallback: use Returns type when table is not in current schema
Fn['Returns'] extends any[]
? Fn['Returns'][number] extends Record<string, unknown>
? Fn['Returns'][number]
: CrossSchemaError<Fn['SetofOptions']['to'] & string>
: Fn['Returns'] extends Record<string, unknown>
? Fn['Returns']
: CrossSchemaError<Fn['SetofOptions']['to'] & string>
: Fn['Returns'] extends any[]
? Fn['Returns'][number] extends Record<string, unknown>
? Fn['Returns'][number]
: never
: Fn['Returns'] extends Record<string, unknown>
? Fn['Returns']
: never
Result: Fn['SetofOptions'] extends GenericSetofOption
? Fn['SetofOptions']['isSetofReturn'] extends true
? Fn['SetofOptions']['isOneToOne'] extends true
? Fn['Returns'][]
: Fn['Returns']
: Fn['Returns']
: Fn['Returns']
RelationName: Fn['SetofOptions'] extends GenericSetofOption
? Fn['SetofOptions']['to']
: FnName
Relationships: Fn['SetofOptions'] extends GenericSetofOption
? Fn['SetofOptions']['to'] extends keyof Schema['Tables']
? Schema['Tables'][Fn['SetofOptions']['to']]['Relationships']
: Fn['SetofOptions']['to'] extends keyof Schema['Views']
? Schema['Views'][Fn['SetofOptions']['to']]['Relationships']
: null
: null
}
: // If we failed to find the function by argument, we still pass with any but also add an overridable
Fn extends false
? RpcFunctionNotFound<FnName>
: RpcFunctionNotFound<FnName>
: RpcFunctionNotFound<FnName>

View File

@@ -0,0 +1,173 @@
import { GoTrueClientOptions } from '@supabase/auth-js'
import { RealtimeClientOptions } from '@supabase/realtime-js'
import { PostgrestError } from '@supabase/postgrest-js'
import type { StorageClientOptions } from '@supabase/storage-js'
import type {
GenericSchema,
GenericRelationship,
GenericTable,
GenericUpdatableView,
GenericNonUpdatableView,
GenericView,
GenericFunction,
} from './rest/types/common/common'
export type {
GenericSchema,
GenericRelationship,
GenericTable,
GenericUpdatableView,
GenericNonUpdatableView,
GenericView,
GenericFunction,
}
export interface SupabaseAuthClientOptions extends GoTrueClientOptions {}
export type Fetch = typeof fetch
export type SupabaseClientOptions<SchemaName> = {
/**
* The Postgres schema which your tables belong to. Must be on the list of exposed schemas in Supabase. Defaults to `public`.
*/
db?: {
schema?: SchemaName
/**
* Optional timeout in milliseconds for PostgREST requests.
* When set, requests will automatically abort after this duration to prevent indefinite hangs.
*
* @example
* ```ts
* const supabase = createClient(url, key, {
* db: { timeout: 30000 } // 30 second timeout
* })
* ```
*/
timeout?: number
/**
* Maximum URL length in characters before warnings/errors are triggered.
* Defaults to 8000 characters. Used to provide helpful hints when URLs
* exceed server limits.
*
* @example
* ```ts
* const supabase = createClient(url, key, {
* db: { urlLengthLimit: 10000 } // Custom limit
* })
* ```
*/
urlLengthLimit?: number
}
auth?: {
/**
* Automatically refreshes the token for logged-in users. Defaults to true.
*/
autoRefreshToken?: boolean
/**
* Optional key name used for storing tokens in local storage.
*/
storageKey?: string
/**
* Whether to persist a logged-in session to storage. Defaults to true.
*/
persistSession?: boolean
/**
* Detect a session from the URL. Used for OAuth login callbacks. Defaults to true.
*
* Can be set to a function to provide custom logic for determining if a URL contains
* a Supabase auth callback. The function receives the current URL and parsed parameters,
* and should return true if the URL should be processed as a Supabase auth callback.
*
* This is useful when your app uses other OAuth providers (e.g., Facebook Login) that
* also return access_token in the URL fragment, which would otherwise be incorrectly
* intercepted by Supabase Auth.
*
* @example
* ```ts
* detectSessionInUrl: (url, params) => {
* // Ignore Facebook OAuth redirects
* if (url.pathname === '/facebook/redirect') return false
* // Use default detection for other URLs
* return Boolean(params.access_token || params.error_description)
* }
* ```
*/
detectSessionInUrl?: boolean | ((url: URL, params: { [parameter: string]: string }) => boolean)
/**
* A storage provider. Used to store the logged-in session.
*/
storage?: SupabaseAuthClientOptions['storage']
/**
* A storage provider to store the user profile separately from the session.
* Useful when you need to store the session information in cookies,
* without bloating the data with the redundant user object.
*
* @experimental
*/
userStorage?: SupabaseAuthClientOptions['userStorage']
/**
* OAuth flow to use - defaults to implicit flow. PKCE is recommended for mobile and server-side applications.
*/
flowType?: SupabaseAuthClientOptions['flowType']
/**
* If debug messages for authentication client are emitted. Can be used to inspect the behavior of the library.
*/
debug?: SupabaseAuthClientOptions['debug']
/**
* Provide your own locking mechanism based on the environment. By default no locking is done at this time.
*
* @experimental
*/
lock?: SupabaseAuthClientOptions['lock']
/**
* If there is an error with the query, throwOnError will reject the promise by
* throwing the error instead of returning it as part of a successful response.
*/
throwOnError?: SupabaseAuthClientOptions['throwOnError']
}
/**
* Options passed to the realtime-js instance
*/
realtime?: RealtimeClientOptions
storage?: StorageClientOptions
global?: {
/**
* A custom `fetch` implementation.
*/
fetch?: Fetch
/**
* Optional headers for initializing the client.
*/
headers?: Record<string, string>
}
/**
* Optional function for using a third-party authentication system with
* Supabase. The function should return an access token or ID token (JWT) by
* obtaining it from the third-party auth SDK. Note that this
* function may be called concurrently and many times. Use memoization and
* locking techniques if this is not supported by the SDKs.
*
* When set, the `auth` namespace of the Supabase client cannot be used.
* Create another client if you wish to use Supabase Auth and third-party
* authentications concurrently in the same application.
*/
accessToken?: () => Promise<string | null>
}
/**
* Helper types for query results.
*/
export type QueryResult<T> = T extends PromiseLike<infer U> ? U : never
export type QueryData<T> = T extends PromiseLike<{ data: infer U }> ? Exclude<U, null> : never
export type QueryError = PostgrestError
/**
* Strips internal Supabase metadata from Database types.
* Useful for libraries defining generic constraints on Database types.
*
* @example
* ```typescript
* type CleanDB = DatabaseWithoutInternals<Database>
* ```
*/
export type DatabaseWithoutInternals<DB> = Omit<DB, '__InternalSupabase'>

View File

@@ -0,0 +1,7 @@
// Generated automatically during releases by scripts/update-version-files.ts
// This file provides runtime access to the package version for:
// - HTTP request headers (e.g., X-Client-Info header for API requests)
// - Debugging and support (identifying which version is running)
// - Telemetry and logging (version reporting in errors/analytics)
// - Ensuring build artifacts match the published package version
export const version = '2.99.1'