@depup/supabase__supabase-js 2.99.2-depup.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.
@@ -0,0 +1,589 @@
1
+ import type { AuthChangeEvent } from '@supabase/auth-js'
2
+ import { FunctionsClient } from '@supabase/functions-js'
3
+ import {
4
+ PostgrestClient,
5
+ type PostgrestFilterBuilder,
6
+ type PostgrestQueryBuilder,
7
+ } from '@supabase/postgrest-js'
8
+ import {
9
+ type RealtimeChannel,
10
+ type RealtimeChannelOptions,
11
+ RealtimeClient,
12
+ type RealtimeClientOptions,
13
+ } from '@supabase/realtime-js'
14
+ import { StorageClient as SupabaseStorageClient } from '@supabase/storage-js'
15
+ import {
16
+ DEFAULT_AUTH_OPTIONS,
17
+ DEFAULT_DB_OPTIONS,
18
+ DEFAULT_GLOBAL_OPTIONS,
19
+ DEFAULT_REALTIME_OPTIONS,
20
+ } from './lib/constants'
21
+ import { fetchWithAuth } from './lib/fetch'
22
+ import { applySettingDefaults, validateSupabaseUrl } from './lib/helpers'
23
+ import { SupabaseAuthClient } from './lib/SupabaseAuthClient'
24
+ import type {
25
+ Fetch,
26
+ GenericSchema,
27
+ SupabaseAuthClientOptions,
28
+ SupabaseClientOptions,
29
+ } from './lib/types'
30
+ import { GetRpcFunctionFilterBuilderByArgs } from './lib/rest/types/common/rpc'
31
+
32
+ /**
33
+ * Supabase Client.
34
+ *
35
+ * An isomorphic Javascript client for interacting with Postgres.
36
+ */
37
+ export default class SupabaseClient<
38
+ Database = any,
39
+ // The second type parameter is also used for specifying db_schema, so we
40
+ // support both cases.
41
+ // TODO: Allow setting db_schema from ClientOptions.
42
+ SchemaNameOrClientOptions extends
43
+ | (string & keyof Omit<Database, '__InternalSupabase'>)
44
+ | { PostgrestVersion: string } = 'public' extends keyof Omit<Database, '__InternalSupabase'>
45
+ ? 'public'
46
+ : string & keyof Omit<Database, '__InternalSupabase'>,
47
+ SchemaName extends string &
48
+ keyof Omit<Database, '__InternalSupabase'> = SchemaNameOrClientOptions extends string &
49
+ keyof Omit<Database, '__InternalSupabase'>
50
+ ? SchemaNameOrClientOptions
51
+ : 'public' extends keyof Omit<Database, '__InternalSupabase'>
52
+ ? 'public'
53
+ : string & keyof Omit<Omit<Database, '__InternalSupabase'>, '__InternalSupabase'>,
54
+ Schema extends Omit<Database, '__InternalSupabase'>[SchemaName] extends GenericSchema
55
+ ? Omit<Database, '__InternalSupabase'>[SchemaName]
56
+ : never = Omit<Database, '__InternalSupabase'>[SchemaName] extends GenericSchema
57
+ ? Omit<Database, '__InternalSupabase'>[SchemaName]
58
+ : never,
59
+ ClientOptions extends { PostgrestVersion: string } = SchemaNameOrClientOptions extends string &
60
+ keyof Omit<Database, '__InternalSupabase'>
61
+ ? // If the version isn't explicitly set, look for it in the __InternalSupabase object to infer the right version
62
+ Database extends { __InternalSupabase: { PostgrestVersion: string } }
63
+ ? Database['__InternalSupabase']
64
+ : // otherwise default to 12
65
+ { PostgrestVersion: '12' }
66
+ : SchemaNameOrClientOptions extends { PostgrestVersion: string }
67
+ ? SchemaNameOrClientOptions
68
+ : never,
69
+ > {
70
+ /**
71
+ * Supabase Auth allows you to create and manage user sessions for access to data that is secured by access policies.
72
+ */
73
+ auth: SupabaseAuthClient
74
+ realtime: RealtimeClient
75
+ /**
76
+ * Supabase Storage allows you to manage user-generated content, such as photos or videos.
77
+ */
78
+ storage: SupabaseStorageClient
79
+
80
+ protected realtimeUrl: URL
81
+ protected authUrl: URL
82
+ protected storageUrl: URL
83
+ protected functionsUrl: URL
84
+ protected rest: PostgrestClient<Database, ClientOptions, SchemaName>
85
+ protected storageKey: string
86
+ protected fetch?: Fetch
87
+ protected changedAccessToken?: string
88
+ protected accessToken?: () => Promise<string | null>
89
+
90
+ protected headers: Record<string, string>
91
+
92
+ /**
93
+ * Create a new client for use in the browser.
94
+ *
95
+ * @category Initializing
96
+ *
97
+ * @param supabaseUrl The unique Supabase URL which is supplied when you create a new project in your project dashboard.
98
+ * @param supabaseKey The unique Supabase Key which is supplied when you create a new project in your project dashboard.
99
+ * @param options.db.schema You can switch in between schemas. The schema needs to be on the list of exposed schemas inside Supabase.
100
+ * @param options.auth.autoRefreshToken Set to "true" if you want to automatically refresh the token before expiring.
101
+ * @param options.auth.persistSession Set to "true" if you want to automatically save the user session into local storage.
102
+ * @param options.auth.detectSessionInUrl Set to "true" if you want to automatically detects OAuth grants in the URL and signs in the user.
103
+ * @param options.realtime Options passed along to realtime-js constructor.
104
+ * @param options.storage Options passed along to the storage-js constructor.
105
+ * @param options.global.fetch A custom fetch implementation.
106
+ * @param options.global.headers Any additional headers to send with each network request.
107
+ *
108
+ * @example Creating a client
109
+ * ```js
110
+ * import { createClient } from '@supabase/supabase-js'
111
+ *
112
+ * // Create a single supabase client for interacting with your database
113
+ * const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key')
114
+ * ```
115
+ *
116
+ * @example With a custom domain
117
+ * ```js
118
+ * import { createClient } from '@supabase/supabase-js'
119
+ *
120
+ * // Use a custom domain as the supabase URL
121
+ * const supabase = createClient('https://my-custom-domain.com', 'publishable-or-anon-key')
122
+ * ```
123
+ *
124
+ * @example With additional parameters
125
+ * ```js
126
+ * import { createClient } from '@supabase/supabase-js'
127
+ *
128
+ * const options = {
129
+ * db: {
130
+ * schema: 'public',
131
+ * },
132
+ * auth: {
133
+ * autoRefreshToken: true,
134
+ * persistSession: true,
135
+ * detectSessionInUrl: true
136
+ * },
137
+ * global: {
138
+ * headers: { 'x-my-custom-header': 'my-app-name' },
139
+ * },
140
+ * }
141
+ * const supabase = createClient("https://xyzcompany.supabase.co", "publishable-or-anon-key", options)
142
+ * ```
143
+ *
144
+ * @exampleDescription With custom schemas
145
+ * By default the API server points to the `public` schema. You can enable other database schemas within the Dashboard.
146
+ * Go to [Settings > API > Exposed schemas](/dashboard/project/_/settings/api) and add the schema which you want to expose to the API.
147
+ *
148
+ * 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.
149
+ *
150
+ * @example With custom schemas
151
+ * ```js
152
+ * import { createClient } from '@supabase/supabase-js'
153
+ *
154
+ * const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key', {
155
+ * // Provide a custom schema. Defaults to "public".
156
+ * db: { schema: 'other_schema' }
157
+ * })
158
+ * ```
159
+ *
160
+ * @exampleDescription Custom fetch implementation
161
+ * `supabase-js` uses the [`cross-fetch`](https://www.npmjs.com/package/cross-fetch) library to make HTTP requests,
162
+ * but an alternative `fetch` implementation can be provided as an option.
163
+ * This is most useful in environments where `cross-fetch` is not compatible (for instance Cloudflare Workers).
164
+ *
165
+ * @example Custom fetch implementation
166
+ * ```js
167
+ * import { createClient } from '@supabase/supabase-js'
168
+ *
169
+ * const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key', {
170
+ * global: { fetch: fetch.bind(globalThis) }
171
+ * })
172
+ * ```
173
+ *
174
+ * @exampleDescription React Native options with AsyncStorage
175
+ * For React Native we recommend using `AsyncStorage` as the storage implementation for Supabase Auth.
176
+ *
177
+ * @example React Native options with AsyncStorage
178
+ * ```js
179
+ * import 'react-native-url-polyfill/auto'
180
+ * import { createClient } from '@supabase/supabase-js'
181
+ * import AsyncStorage from "@react-native-async-storage/async-storage";
182
+ *
183
+ * const supabase = createClient("https://xyzcompany.supabase.co", "publishable-or-anon-key", {
184
+ * auth: {
185
+ * storage: AsyncStorage,
186
+ * autoRefreshToken: true,
187
+ * persistSession: true,
188
+ * detectSessionInUrl: false,
189
+ * },
190
+ * });
191
+ * ```
192
+ *
193
+ * @exampleDescription React Native options with Expo SecureStore
194
+ * If you wish to encrypt the user's session information, you can use `aes-js` and store the encryption key in Expo SecureStore.
195
+ * The `aes-js` library, a reputable JavaScript-only implementation of the AES encryption algorithm in CTR mode.
196
+ * A new 256-bit encryption key is generated using the `react-native-get-random-values` library.
197
+ * This key is stored inside Expo's SecureStore, while the value is encrypted and placed inside AsyncStorage.
198
+ *
199
+ * Please make sure that:
200
+ * - You keep the `expo-secure-store`, `aes-js` and `react-native-get-random-values` libraries up-to-date.
201
+ * - Choose the correct [`SecureStoreOptions`](https://docs.expo.dev/versions/latest/sdk/securestore/#securestoreoptions) for your app's needs.
202
+ * E.g. [`SecureStore.WHEN_UNLOCKED`](https://docs.expo.dev/versions/latest/sdk/securestore/#securestorewhen_unlocked) regulates when the data can be accessed.
203
+ * - Carefully consider optimizations or other modifications to the above example, as those can lead to introducing subtle security vulnerabilities.
204
+ *
205
+ * @example React Native options with Expo SecureStore
206
+ * ```ts
207
+ * import 'react-native-url-polyfill/auto'
208
+ * import { createClient } from '@supabase/supabase-js'
209
+ * import AsyncStorage from '@react-native-async-storage/async-storage';
210
+ * import * as SecureStore from 'expo-secure-store';
211
+ * import * as aesjs from 'aes-js';
212
+ * import 'react-native-get-random-values';
213
+ *
214
+ * // As Expo's SecureStore does not support values larger than 2048
215
+ * // bytes, an AES-256 key is generated and stored in SecureStore, while
216
+ * // it is used to encrypt/decrypt values stored in AsyncStorage.
217
+ * class LargeSecureStore {
218
+ * private async _encrypt(key: string, value: string) {
219
+ * const encryptionKey = crypto.getRandomValues(new Uint8Array(256 / 8));
220
+ *
221
+ * const cipher = new aesjs.ModeOfOperation.ctr(encryptionKey, new aesjs.Counter(1));
222
+ * const encryptedBytes = cipher.encrypt(aesjs.utils.utf8.toBytes(value));
223
+ *
224
+ * await SecureStore.setItemAsync(key, aesjs.utils.hex.fromBytes(encryptionKey));
225
+ *
226
+ * return aesjs.utils.hex.fromBytes(encryptedBytes);
227
+ * }
228
+ *
229
+ * private async _decrypt(key: string, value: string) {
230
+ * const encryptionKeyHex = await SecureStore.getItemAsync(key);
231
+ * if (!encryptionKeyHex) {
232
+ * return encryptionKeyHex;
233
+ * }
234
+ *
235
+ * const cipher = new aesjs.ModeOfOperation.ctr(aesjs.utils.hex.toBytes(encryptionKeyHex), new aesjs.Counter(1));
236
+ * const decryptedBytes = cipher.decrypt(aesjs.utils.hex.toBytes(value));
237
+ *
238
+ * return aesjs.utils.utf8.fromBytes(decryptedBytes);
239
+ * }
240
+ *
241
+ * async getItem(key: string) {
242
+ * const encrypted = await AsyncStorage.getItem(key);
243
+ * if (!encrypted) { return encrypted; }
244
+ *
245
+ * return await this._decrypt(key, encrypted);
246
+ * }
247
+ *
248
+ * async removeItem(key: string) {
249
+ * await AsyncStorage.removeItem(key);
250
+ * await SecureStore.deleteItemAsync(key);
251
+ * }
252
+ *
253
+ * async setItem(key: string, value: string) {
254
+ * const encrypted = await this._encrypt(key, value);
255
+ *
256
+ * await AsyncStorage.setItem(key, encrypted);
257
+ * }
258
+ * }
259
+ *
260
+ * const supabase = createClient("https://xyzcompany.supabase.co", "publishable-or-anon-key", {
261
+ * auth: {
262
+ * storage: new LargeSecureStore(),
263
+ * autoRefreshToken: true,
264
+ * persistSession: true,
265
+ * detectSessionInUrl: false,
266
+ * },
267
+ * });
268
+ * ```
269
+ *
270
+ * @example With a database query
271
+ * ```ts
272
+ * import { createClient } from '@supabase/supabase-js'
273
+ *
274
+ * const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
275
+ *
276
+ * const { data } = await supabase.from('profiles').select('*')
277
+ * ```
278
+ */
279
+ constructor(
280
+ protected supabaseUrl: string,
281
+ protected supabaseKey: string,
282
+ options?: SupabaseClientOptions<SchemaName>
283
+ ) {
284
+ const baseUrl = validateSupabaseUrl(supabaseUrl)
285
+ if (!supabaseKey) throw new Error('supabaseKey is required.')
286
+
287
+ this.realtimeUrl = new URL('realtime/v1', baseUrl)
288
+ this.realtimeUrl.protocol = this.realtimeUrl.protocol.replace('http', 'ws')
289
+ this.authUrl = new URL('auth/v1', baseUrl)
290
+ this.storageUrl = new URL('storage/v1', baseUrl)
291
+ this.functionsUrl = new URL('functions/v1', baseUrl)
292
+
293
+ // default storage key uses the supabase project ref as a namespace
294
+ const defaultStorageKey = `sb-${baseUrl.hostname.split('.')[0]}-auth-token`
295
+ const DEFAULTS = {
296
+ db: DEFAULT_DB_OPTIONS,
297
+ realtime: DEFAULT_REALTIME_OPTIONS,
298
+ auth: { ...DEFAULT_AUTH_OPTIONS, storageKey: defaultStorageKey },
299
+ global: DEFAULT_GLOBAL_OPTIONS,
300
+ }
301
+
302
+ const settings = applySettingDefaults(options ?? {}, DEFAULTS)
303
+
304
+ this.storageKey = settings.auth.storageKey ?? ''
305
+ this.headers = settings.global.headers ?? {}
306
+
307
+ if (!settings.accessToken) {
308
+ this.auth = this._initSupabaseAuthClient(
309
+ settings.auth ?? {},
310
+ this.headers,
311
+ settings.global.fetch
312
+ )
313
+ } else {
314
+ this.accessToken = settings.accessToken
315
+
316
+ this.auth = new Proxy<SupabaseAuthClient>({} as any, {
317
+ get: (_, prop) => {
318
+ throw new Error(
319
+ `@supabase/supabase-js: Supabase Client is configured with the accessToken option, accessing supabase.auth.${String(
320
+ prop
321
+ )} is not possible`
322
+ )
323
+ },
324
+ })
325
+ }
326
+
327
+ this.fetch = fetchWithAuth(supabaseKey, this._getAccessToken.bind(this), settings.global.fetch)
328
+ this.realtime = this._initRealtimeClient({
329
+ headers: this.headers,
330
+ accessToken: this._getAccessToken.bind(this),
331
+ ...settings.realtime,
332
+ })
333
+ if (this.accessToken) {
334
+ // Start auth immediately to avoid race condition with channel subscriptions
335
+ // Wrap Promise to avoid Firefox extension cross-context Promise access errors
336
+ Promise.resolve(this.accessToken())
337
+ .then((token) => this.realtime.setAuth(token))
338
+ .catch((e) => console.warn('Failed to set initial Realtime auth token:', e))
339
+ }
340
+
341
+ this.rest = new PostgrestClient(new URL('rest/v1', baseUrl).href, {
342
+ headers: this.headers,
343
+ schema: settings.db.schema,
344
+ fetch: this.fetch,
345
+ timeout: settings.db.timeout,
346
+ urlLengthLimit: settings.db.urlLengthLimit,
347
+ })
348
+
349
+ this.storage = new SupabaseStorageClient(
350
+ this.storageUrl.href,
351
+ this.headers,
352
+ this.fetch,
353
+ options?.storage
354
+ )
355
+
356
+ if (!settings.accessToken) {
357
+ this._listenForAuthEvents()
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Supabase Functions allows you to deploy and invoke edge functions.
363
+ */
364
+ get functions(): FunctionsClient {
365
+ return new FunctionsClient(this.functionsUrl.href, {
366
+ headers: this.headers,
367
+ customFetch: this.fetch,
368
+ })
369
+ }
370
+
371
+ // NOTE: signatures must be kept in sync with PostgrestClient.from
372
+ from<
373
+ TableName extends string & keyof Schema['Tables'],
374
+ Table extends Schema['Tables'][TableName],
375
+ >(relation: TableName): PostgrestQueryBuilder<ClientOptions, Schema, Table, TableName>
376
+ from<ViewName extends string & keyof Schema['Views'], View extends Schema['Views'][ViewName]>(
377
+ relation: ViewName
378
+ ): PostgrestQueryBuilder<ClientOptions, Schema, View, ViewName>
379
+ /**
380
+ * Perform a query on a table or a view.
381
+ *
382
+ * @param relation - The table or view name to query
383
+ */
384
+ from(relation: string): PostgrestQueryBuilder<ClientOptions, Schema, any> {
385
+ return this.rest.from(relation)
386
+ }
387
+
388
+ // NOTE: signatures must be kept in sync with PostgrestClient.schema
389
+ /**
390
+ * Select a schema to query or perform an function (rpc) call.
391
+ *
392
+ * The schema needs to be on the list of exposed schemas inside Supabase.
393
+ *
394
+ * @param schema - The schema to query
395
+ */
396
+ schema<DynamicSchema extends string & keyof Omit<Database, '__InternalSupabase'>>(
397
+ schema: DynamicSchema
398
+ ): PostgrestClient<
399
+ Database,
400
+ ClientOptions,
401
+ DynamicSchema,
402
+ Database[DynamicSchema] extends GenericSchema ? Database[DynamicSchema] : any
403
+ > {
404
+ return this.rest.schema<DynamicSchema>(schema)
405
+ }
406
+
407
+ // NOTE: signatures must be kept in sync with PostgrestClient.rpc
408
+ /**
409
+ * Perform a function call.
410
+ *
411
+ * @param fn - The function name to call
412
+ * @param args - The arguments to pass to the function call
413
+ * @param options - Named parameters
414
+ * @param options.head - When set to `true`, `data` will not be returned.
415
+ * Useful if you only need the count.
416
+ * @param options.get - When set to `true`, the function will be called with
417
+ * read-only access mode.
418
+ * @param options.count - Count algorithm to use to count rows returned by the
419
+ * function. Only applicable for [set-returning
420
+ * functions](https://www.postgresql.org/docs/current/functions-srf.html).
421
+ *
422
+ * `"exact"`: Exact but slow count algorithm. Performs a `COUNT(*)` under the
423
+ * hood.
424
+ *
425
+ * `"planned"`: Approximated but fast count algorithm. Uses the Postgres
426
+ * statistics under the hood.
427
+ *
428
+ * `"estimated"`: Uses exact count for low numbers and planned count for high
429
+ * numbers.
430
+ */
431
+ rpc<
432
+ FnName extends string & keyof Schema['Functions'],
433
+ Args extends Schema['Functions'][FnName]['Args'] = never,
434
+ FilterBuilder extends GetRpcFunctionFilterBuilderByArgs<
435
+ Schema,
436
+ FnName,
437
+ Args
438
+ > = GetRpcFunctionFilterBuilderByArgs<Schema, FnName, Args>,
439
+ >(
440
+ fn: FnName,
441
+ args: Args = {} as Args,
442
+ options: {
443
+ head?: boolean
444
+ get?: boolean
445
+ count?: 'exact' | 'planned' | 'estimated'
446
+ } = {
447
+ head: false,
448
+ get: false,
449
+ count: undefined,
450
+ }
451
+ ): PostgrestFilterBuilder<
452
+ ClientOptions,
453
+ Schema,
454
+ FilterBuilder['Row'],
455
+ FilterBuilder['Result'],
456
+ FilterBuilder['RelationName'],
457
+ FilterBuilder['Relationships'],
458
+ 'RPC'
459
+ > {
460
+ return this.rest.rpc(fn, args, options) as unknown as PostgrestFilterBuilder<
461
+ ClientOptions,
462
+ Schema,
463
+ FilterBuilder['Row'],
464
+ FilterBuilder['Result'],
465
+ FilterBuilder['RelationName'],
466
+ FilterBuilder['Relationships'],
467
+ 'RPC'
468
+ >
469
+ }
470
+
471
+ /**
472
+ * Creates a Realtime channel with Broadcast, Presence, and Postgres Changes.
473
+ *
474
+ * @param {string} name - The name of the Realtime channel.
475
+ * @param {Object} opts - The options to pass to the Realtime channel.
476
+ *
477
+ */
478
+ channel(name: string, opts: RealtimeChannelOptions = { config: {} }): RealtimeChannel {
479
+ return this.realtime.channel(name, opts)
480
+ }
481
+
482
+ /**
483
+ * Returns all Realtime channels.
484
+ */
485
+ getChannels(): RealtimeChannel[] {
486
+ return this.realtime.getChannels()
487
+ }
488
+
489
+ /**
490
+ * Unsubscribes and removes Realtime channel from Realtime client.
491
+ *
492
+ * @param {RealtimeChannel} channel - The name of the Realtime channel.
493
+ *
494
+ */
495
+ removeChannel(channel: RealtimeChannel): Promise<'ok' | 'timed out' | 'error'> {
496
+ return this.realtime.removeChannel(channel)
497
+ }
498
+
499
+ /**
500
+ * Unsubscribes and removes all Realtime channels from Realtime client.
501
+ */
502
+ removeAllChannels(): Promise<('ok' | 'timed out' | 'error')[]> {
503
+ return this.realtime.removeAllChannels()
504
+ }
505
+
506
+ private async _getAccessToken() {
507
+ if (this.accessToken) {
508
+ return await this.accessToken()
509
+ }
510
+
511
+ const { data } = await this.auth.getSession()
512
+
513
+ return data.session?.access_token ?? this.supabaseKey
514
+ }
515
+
516
+ private _initSupabaseAuthClient(
517
+ {
518
+ autoRefreshToken,
519
+ persistSession,
520
+ detectSessionInUrl,
521
+ storage,
522
+ userStorage,
523
+ storageKey,
524
+ flowType,
525
+ lock,
526
+ debug,
527
+ throwOnError,
528
+ }: SupabaseAuthClientOptions,
529
+ headers?: Record<string, string>,
530
+ fetch?: Fetch
531
+ ) {
532
+ const authHeaders = {
533
+ Authorization: `Bearer ${this.supabaseKey}`,
534
+ apikey: `${this.supabaseKey}`,
535
+ }
536
+ return new SupabaseAuthClient({
537
+ url: this.authUrl.href,
538
+ headers: { ...authHeaders, ...headers },
539
+ storageKey: storageKey,
540
+ autoRefreshToken,
541
+ persistSession,
542
+ detectSessionInUrl,
543
+ storage,
544
+ userStorage,
545
+ flowType,
546
+ lock,
547
+ debug,
548
+ throwOnError,
549
+ fetch,
550
+ // auth checks if there is a custom authorizaiton header using this flag
551
+ // so it knows whether to return an error when getUser is called with no session
552
+ hasCustomAuthorizationHeader: Object.keys(this.headers).some(
553
+ (key) => key.toLowerCase() === 'authorization'
554
+ ),
555
+ })
556
+ }
557
+
558
+ private _initRealtimeClient(options: RealtimeClientOptions) {
559
+ return new RealtimeClient(this.realtimeUrl.href, {
560
+ ...options,
561
+ params: { ...{ apikey: this.supabaseKey }, ...options?.params },
562
+ })
563
+ }
564
+
565
+ private _listenForAuthEvents() {
566
+ const data = this.auth.onAuthStateChange((event, session) => {
567
+ this._handleTokenChanged(event, 'CLIENT', session?.access_token)
568
+ })
569
+ return data
570
+ }
571
+
572
+ private _handleTokenChanged(
573
+ event: AuthChangeEvent,
574
+ source: 'CLIENT' | 'STORAGE',
575
+ token?: string
576
+ ) {
577
+ if (
578
+ (event === 'TOKEN_REFRESHED' || event === 'SIGNED_IN') &&
579
+ this.changedAccessToken !== token
580
+ ) {
581
+ this.changedAccessToken = token
582
+ this.realtime.setAuth(token)
583
+ } else if (event === 'SIGNED_OUT') {
584
+ this.realtime.setAuth()
585
+ if (source == 'STORAGE') this.auth.signOut()
586
+ this.changedAccessToken = undefined
587
+ }
588
+ }
589
+ }
package/src/cors.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Canonical CORS configuration for Supabase Edge Functions
3
+ *
4
+ * This module exports CORS headers that stay synchronized with the Supabase SDK.
5
+ * When new headers are added to the SDK, they are automatically included here,
6
+ * preventing CORS errors in Edge Functions.
7
+ *
8
+ * @example Basic usage
9
+ * ```typescript
10
+ * import { corsHeaders } from '@supabase/supabase-js/cors'
11
+ *
12
+ * Deno.serve(async (req) => {
13
+ * if (req.method === 'OPTIONS') {
14
+ * return new Response('ok', { headers: corsHeaders })
15
+ * }
16
+ *
17
+ * return new Response(
18
+ * JSON.stringify({ data: 'Hello' }),
19
+ * { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
20
+ * )
21
+ * })
22
+ * ```
23
+ *
24
+ * @module cors
25
+ */
26
+
27
+ /**
28
+ * All custom headers sent by the Supabase SDK.
29
+ * These headers need to be included in CORS configuration to prevent preflight failures.
30
+ *
31
+ * Headers:
32
+ * - authorization: Bearer token for authentication
33
+ * - x-client-info: Library version information
34
+ * - apikey: Project API key
35
+ * - content-type: Standard HTTP content type
36
+ */
37
+ const SUPABASE_HEADERS = ['authorization', 'x-client-info', 'apikey', 'content-type'].join(', ')
38
+
39
+ /**
40
+ * All HTTP methods used by the Supabase SDK
41
+ */
42
+ const SUPABASE_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'].join(', ')
43
+
44
+ /**
45
+ * Type representing CORS headers as a record of header names to values
46
+ */
47
+ export type CorsHeaders = Record<string, string>
48
+
49
+ /**
50
+ * Default CORS headers for Supabase Edge Functions.
51
+ *
52
+ * Includes all headers sent by Supabase client libraries and allows all standard HTTP methods.
53
+ * Use this for simple CORS configurations with wildcard origin.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import { corsHeaders } from '@supabase/supabase-js/cors'
58
+ *
59
+ * Deno.serve(async (req) => {
60
+ * if (req.method === 'OPTIONS') {
61
+ * return new Response('ok', { headers: corsHeaders })
62
+ * }
63
+ *
64
+ * return new Response(
65
+ * JSON.stringify({ data: 'Hello' }),
66
+ * { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
67
+ * )
68
+ * })
69
+ * ```
70
+ */
71
+ export const corsHeaders: CorsHeaders = {
72
+ 'Access-Control-Allow-Origin': '*',
73
+ 'Access-Control-Allow-Headers': SUPABASE_HEADERS,
74
+ 'Access-Control-Allow-Methods': SUPABASE_METHODS,
75
+ }