@supabase/gotrue-js 2.46.2 → 2.47.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.
@@ -28,9 +28,6 @@ import {
28
28
  generatePKCEVerifier,
29
29
  generatePKCEChallenge,
30
30
  supportsLocalStorage,
31
- stackGuard,
32
- isInStackGuard,
33
- stackGuardsSupported,
34
31
  parseParametersFromURL,
35
32
  } from './lib/helpers'
36
33
  import localStorageAdapter from './lib/local-storage'
@@ -154,6 +151,8 @@ export default class GoTrueClient {
154
151
  }
155
152
  protected fetch: Fetch
156
153
  protected lock: LockFunc
154
+ protected lockAcquired = false
155
+ protected pendingInLock: Promise<any>[] = []
157
156
 
158
157
  /**
159
158
  * Used to broadcast state change events to other tabs listening.
@@ -248,12 +247,18 @@ export default class GoTrueClient {
248
247
  * This method is automatically called when instantiating the client, but should also be called
249
248
  * manually when checking for an error from an auth redirect (oauth, magiclink, password recovery, etc).
250
249
  */
251
- initialize(): Promise<InitializeResult> {
250
+ async initialize(): Promise<InitializeResult> {
252
251
  if (this.initializePromise) {
253
- return this.initializePromise
252
+ return await this.initializePromise
254
253
  }
255
254
 
256
- return this._initialize()
255
+ this.initializePromise = (async () => {
256
+ return await this._acquireLock(-1, async () => {
257
+ return await this._initialize()
258
+ })
259
+ })()
260
+
261
+ return await this.initializePromise
257
262
  }
258
263
 
259
264
  /**
@@ -263,71 +268,59 @@ export default class GoTrueClient {
263
268
  * the whole lifetime of the client
264
269
  */
265
270
  private async _initialize(): Promise<InitializeResult> {
266
- if (this.initializePromise) {
267
- throw new Error('Double call of #_initialize()')
268
- }
269
-
270
- this.initializePromise = this._acquireLock(
271
- -1,
272
- async () =>
273
- await stackGuard('_initialize', async () => {
274
- try {
275
- const isPKCEFlow = isBrowser() ? await this._isPKCEFlow() : false
276
- this._debug('#_initialize()', 'begin', 'is PKCE flow', isPKCEFlow)
277
-
278
- if (isPKCEFlow || (this.detectSessionInUrl && this._isImplicitGrantFlow())) {
279
- const { data, error } = await this._getSessionFromURL(isPKCEFlow)
280
- if (error) {
281
- this._debug('#_initialize()', 'error detecting session from URL', error)
282
-
283
- // failed login attempt via url,
284
- // remove old session as in verifyOtp, signUp and signInWith*
285
- await this._removeSession()
271
+ try {
272
+ const isPKCEFlow = isBrowser() ? await this._isPKCEFlow() : false
273
+ this._debug('#_initialize()', 'begin', 'is PKCE flow', isPKCEFlow)
286
274
 
287
- return { error }
288
- }
275
+ if (isPKCEFlow || (this.detectSessionInUrl && this._isImplicitGrantFlow())) {
276
+ const { data, error } = await this._getSessionFromURL(isPKCEFlow)
277
+ if (error) {
278
+ this._debug('#_initialize()', 'error detecting session from URL', error)
289
279
 
290
- const { session, redirectType } = data
280
+ // failed login attempt via url,
281
+ // remove old session as in verifyOtp, signUp and signInWith*
282
+ await this._removeSession()
291
283
 
292
- this._debug(
293
- '#_initialize()',
294
- 'detected session in URL',
295
- session,
296
- 'redirect type',
297
- redirectType
298
- )
284
+ return { error }
285
+ }
299
286
 
300
- await this._saveSession(session)
287
+ const { session, redirectType } = data
301
288
 
302
- setTimeout(async () => {
303
- if (redirectType === 'recovery') {
304
- await this._notifyAllSubscribers('PASSWORD_RECOVERY', session)
305
- } else {
306
- await this._notifyAllSubscribers('SIGNED_IN', session)
307
- }
308
- }, 0)
289
+ this._debug(
290
+ '#_initialize()',
291
+ 'detected session in URL',
292
+ session,
293
+ 'redirect type',
294
+ redirectType
295
+ )
309
296
 
310
- return { error: null }
311
- }
312
- // no login attempt via callback url try to recover session from storage
313
- await this._recoverAndRefresh()
314
- return { error: null }
315
- } catch (error) {
316
- if (isAuthError(error)) {
317
- return { error }
318
- }
297
+ await this._saveSession(session)
319
298
 
320
- return {
321
- error: new AuthUnknownError('Unexpected error during initialization', error),
322
- }
323
- } finally {
324
- await this._handleVisibilityChange()
325
- this._debug('#_initialize()', 'end')
299
+ setTimeout(async () => {
300
+ if (redirectType === 'recovery') {
301
+ await this._notifyAllSubscribers('PASSWORD_RECOVERY', session)
302
+ } else {
303
+ await this._notifyAllSubscribers('SIGNED_IN', session)
326
304
  }
327
- })
328
- )
305
+ }, 0)
329
306
 
330
- return await this.initializePromise
307
+ return { error: null }
308
+ }
309
+ // no login attempt via callback url try to recover session from storage
310
+ await this._recoverAndRefresh()
311
+ return { error: null }
312
+ } catch (error) {
313
+ if (isAuthError(error)) {
314
+ return { error }
315
+ }
316
+
317
+ return {
318
+ error: new AuthUnknownError('Unexpected error during initialization', error),
319
+ }
320
+ } finally {
321
+ await this._handleVisibilityChange()
322
+ this._debug('#_initialize()', 'end')
323
+ }
331
324
  }
332
325
 
333
326
  /**
@@ -490,6 +483,14 @@ export default class GoTrueClient {
490
483
  * Log in an existing user by exchanging an Auth Code issued during the PKCE flow.
491
484
  */
492
485
  async exchangeCodeForSession(authCode: string): Promise<AuthTokenResponse> {
486
+ await this.initializePromise
487
+
488
+ return this._acquireLock(-1, async () => {
489
+ return this._exchangeCodeForSession(authCode)
490
+ })
491
+ }
492
+
493
+ private async _exchangeCodeForSession(authCode: string): Promise<AuthTokenResponse> {
493
494
  const codeVerifier = await getItemAsync(this.storage, `${this.storageKey}-code-verifier`)
494
495
  const { data, error } = await _request(
495
496
  this.fetch,
@@ -726,6 +727,14 @@ export default class GoTrueClient {
726
727
  * Requires the user to be signed-in.
727
728
  */
728
729
  async reauthenticate(): Promise<AuthResponse> {
730
+ await this.initializePromise
731
+
732
+ return await this._acquireLock(-1, async () => {
733
+ return await this._reauthenticate()
734
+ })
735
+ }
736
+
737
+ private async _reauthenticate(): Promise<AuthResponse> {
729
738
  try {
730
739
  return await this._useSession(async (result) => {
731
740
  const {
@@ -799,8 +808,12 @@ export default class GoTrueClient {
799
808
  * The session returned can be null if the session is not detected which can happen in the event a user is not signed-in or has logged out.
800
809
  */
801
810
  async getSession() {
802
- return this._useSession(async (result) => {
803
- return result
811
+ await this.initializePromise
812
+
813
+ return this._acquireLock(-1, async () => {
814
+ return this._useSession(async (result) => {
815
+ return result
816
+ })
804
817
  })
805
818
  }
806
819
 
@@ -811,29 +824,63 @@ export default class GoTrueClient {
811
824
  this._debug('#_acquireLock', 'begin', acquireTimeout)
812
825
 
813
826
  try {
814
- if (!(await stackGuardsSupported())) {
815
- this._debug(
816
- '#_acquireLock',
817
- 'Stack guards not supported, so exclusive locking is not performed as it can lead to deadlocks if the lock is attempted to be recursively acquired (as the recursion cannot be detected).'
827
+ if (this.lockAcquired) {
828
+ const last = this.pendingInLock.length
829
+ ? this.pendingInLock[this.pendingInLock.length - 1]
830
+ : Promise.resolve()
831
+
832
+ const result = (async () => {
833
+ await last
834
+ return await fn()
835
+ })()
836
+
837
+ this.pendingInLock.push(
838
+ (async () => {
839
+ try {
840
+ await result
841
+ } catch (e: any) {
842
+ // we jsut care if it finished
843
+ }
844
+ })()
818
845
  )
819
846
 
820
- return await fn()
821
- }
822
-
823
- if (isInStackGuard('_acquireLock')) {
824
- this._debug('#_acquireLock', 'recursive call')
825
- return await fn()
847
+ return result
826
848
  }
827
849
 
828
850
  return await this.lock(`lock:${this.storageKey}`, acquireTimeout, async () => {
829
851
  this._debug('#_acquireLock', 'lock acquired for storage key', this.storageKey)
830
852
 
831
853
  try {
832
- return await stackGuard('_acquireLock', async () => {
833
- return await fn()
834
- })
854
+ this.lockAcquired = true
855
+
856
+ const result = fn()
857
+
858
+ this.pendingInLock.push(
859
+ (async () => {
860
+ try {
861
+ await result
862
+ } catch (e: any) {
863
+ // we just care if it finished
864
+ }
865
+ })()
866
+ )
867
+
868
+ await result
869
+
870
+ // keep draining the queue until there's nothing to wait on
871
+ while (this.pendingInLock.length) {
872
+ const waitOn = [...this.pendingInLock]
873
+
874
+ await Promise.all(waitOn)
875
+
876
+ this.pendingInLock.splice(0, waitOn.length)
877
+ }
878
+
879
+ return await result
835
880
  } finally {
836
881
  this._debug('#_acquireLock', 'lock released for storage key', this.storageKey)
882
+
883
+ this.lockAcquired = false
837
884
  }
838
885
  })
839
886
  } finally {
@@ -873,23 +920,10 @@ export default class GoTrueClient {
873
920
  this._debug('#_useSession', 'begin')
874
921
 
875
922
  try {
876
- if (isInStackGuard('_useSession')) {
877
- this._debug('#_useSession', 'recursive call')
878
-
879
- // the use of __loadSession here is the only correct use of the function!
880
- const result = await this.__loadSession()
923
+ // the use of __loadSession here is the only correct use of the function!
924
+ const result = await this.__loadSession()
881
925
 
882
- return await fn(result)
883
- }
884
-
885
- return await this._acquireLock(-1, async () => {
886
- return await stackGuard('_useSession', async () => {
887
- // the use of __loadSession here is the only correct use of the function!
888
- const result = await this.__loadSession()
889
-
890
- return await fn(result)
891
- })
892
- })
926
+ return await fn(result)
893
927
  } finally {
894
928
  this._debug('#_useSession', 'end')
895
929
  }
@@ -922,18 +956,10 @@ export default class GoTrueClient {
922
956
  > {
923
957
  this._debug('#__loadSession()', 'begin')
924
958
 
925
- if (this.logDebugMessages && !isInStackGuard('_useSession') && (await stackGuardsSupported())) {
926
- throw new Error('Please use #_useSession()')
927
- }
928
-
929
- if (isInStackGuard('_initialize')) {
930
- this._debug('#__loadSession', '#_initialize recursion detected', new Error().stack)
959
+ if (!this.lockAcquired) {
960
+ this._debug('#__loadSession()', 'used outside of an acquired lock!', new Error().stack)
931
961
  }
932
962
 
933
- // always wait for #_initialize() to finish before loading anything from
934
- // storage
935
- await this.initializePromise
936
-
937
963
  try {
938
964
  let currentSession: Session | null = null
939
965
 
@@ -990,6 +1016,18 @@ export default class GoTrueClient {
990
1016
  * @param jwt Takes in an optional access token jwt. If no jwt is provided, getUser() will attempt to get the jwt from the current session.
991
1017
  */
992
1018
  async getUser(jwt?: string): Promise<UserResponse> {
1019
+ if (jwt) {
1020
+ return await this._getUser(jwt)
1021
+ }
1022
+
1023
+ await this.initializePromise
1024
+
1025
+ return this._acquireLock(-1, async () => {
1026
+ return await this._getUser()
1027
+ })
1028
+ }
1029
+
1030
+ private async _getUser(jwt?: string): Promise<UserResponse> {
993
1031
  try {
994
1032
  if (jwt) {
995
1033
  return await _request(this.fetch, 'GET', `${this.url}/user`, {
@@ -1028,6 +1066,19 @@ export default class GoTrueClient {
1028
1066
  options: {
1029
1067
  emailRedirectTo?: string | undefined
1030
1068
  } = {}
1069
+ ): Promise<UserResponse> {
1070
+ await this.initializePromise
1071
+
1072
+ return await this._acquireLock(-1, async () => {
1073
+ return await this._updateUser(attributes, options)
1074
+ })
1075
+ }
1076
+
1077
+ protected async _updateUser(
1078
+ attributes: UserAttributes,
1079
+ options: {
1080
+ emailRedirectTo?: string | undefined
1081
+ } = {}
1031
1082
  ): Promise<UserResponse> {
1032
1083
  try {
1033
1084
  return await this._useSession(async (result) => {
@@ -1081,6 +1132,17 @@ export default class GoTrueClient {
1081
1132
  async setSession(currentSession: {
1082
1133
  access_token: string
1083
1134
  refresh_token: string
1135
+ }): Promise<AuthResponse> {
1136
+ await this.initializePromise
1137
+
1138
+ return await this._acquireLock(-1, async () => {
1139
+ return await this._setSession(currentSession)
1140
+ })
1141
+ }
1142
+
1143
+ protected async _setSession(currentSession: {
1144
+ access_token: string
1145
+ refresh_token: string
1084
1146
  }): Promise<AuthResponse> {
1085
1147
  try {
1086
1148
  if (!currentSession.access_token || !currentSession.refresh_token) {
@@ -1110,7 +1172,7 @@ export default class GoTrueClient {
1110
1172
  }
1111
1173
  session = refreshedSession
1112
1174
  } else {
1113
- const { data, error } = await this.getUser(currentSession.access_token)
1175
+ const { data, error } = await this._getUser(currentSession.access_token)
1114
1176
  if (error) {
1115
1177
  throw error
1116
1178
  }
@@ -1143,6 +1205,16 @@ export default class GoTrueClient {
1143
1205
  * @param currentSession The current session. If passed in, it must contain a refresh token.
1144
1206
  */
1145
1207
  async refreshSession(currentSession?: { refresh_token: string }): Promise<AuthResponse> {
1208
+ await this.initializePromise
1209
+
1210
+ return await this._acquireLock(-1, async () => {
1211
+ return await this._refreshSession(currentSession)
1212
+ })
1213
+ }
1214
+
1215
+ protected async _refreshSession(currentSession?: {
1216
+ refresh_token: string
1217
+ }): Promise<AuthResponse> {
1146
1218
  try {
1147
1219
  return await this._useSession(async (result) => {
1148
1220
  if (!currentSession) {
@@ -1200,7 +1272,7 @@ export default class GoTrueClient {
1200
1272
 
1201
1273
  if (isPKCEFlow) {
1202
1274
  if (!params.code) throw new AuthPKCEGrantCodeExchangeError('No code detected.')
1203
- const { data, error } = await this.exchangeCodeForSession(params.code)
1275
+ const { data, error } = await this._exchangeCodeForSession(params.code)
1204
1276
  if (error) throw error
1205
1277
 
1206
1278
  const url = new URL(window.location.href)
@@ -1238,7 +1310,7 @@ export default class GoTrueClient {
1238
1310
  const expiresIn = parseInt(expires_in)
1239
1311
  const expires_at = timeNow + expiresIn
1240
1312
 
1241
- const { data, error } = await this.getUser(access_token)
1313
+ const { data, error } = await this._getUser(access_token)
1242
1314
  if (error) throw error
1243
1315
 
1244
1316
  const session: Session = {
@@ -1297,7 +1369,17 @@ export default class GoTrueClient {
1297
1369
  *
1298
1370
  * If using others scope, no `SIGNED_OUT` event is fired!
1299
1371
  */
1300
- async signOut({ scope }: SignOut = { scope: 'global' }): Promise<{ error: AuthError | null }> {
1372
+ async signOut(options: SignOut = { scope: 'global' }): Promise<{ error: AuthError | null }> {
1373
+ await this.initializePromise
1374
+
1375
+ return await this._acquireLock(-1, async () => {
1376
+ return await this._signOut(options)
1377
+ })
1378
+ }
1379
+
1380
+ protected async _signOut(
1381
+ { scope }: SignOut = { scope: 'global' }
1382
+ ): Promise<{ error: AuthError | null }> {
1301
1383
  return await this._useSession(async (result) => {
1302
1384
  const { data, error: sessionError } = result
1303
1385
  if (sessionError) {
@@ -1346,8 +1428,13 @@ export default class GoTrueClient {
1346
1428
  this._debug('#onAuthStateChange()', 'registered callback with id', id)
1347
1429
 
1348
1430
  this.stateChangeEmitters.set(id, subscription)
1431
+ ;(async () => {
1432
+ await this.initializePromise
1349
1433
 
1350
- this._emitInitialSession(id)
1434
+ await this._acquireLock(-1, async () => {
1435
+ this._emitInitialSession(id)
1436
+ })
1437
+ })()
1351
1438
 
1352
1439
  return { data: { subscription } }
1353
1440
  }
@@ -1796,38 +1883,51 @@ export default class GoTrueClient {
1796
1883
  this._debug('#_autoRefreshTokenTick()', 'begin')
1797
1884
 
1798
1885
  try {
1799
- const now = Date.now()
1886
+ await this._acquireLock(0, async () => {
1887
+ try {
1888
+ const now = Date.now()
1800
1889
 
1801
- try {
1802
- return await this._useSession(async (result) => {
1803
- const {
1804
- data: { session },
1805
- } = result
1806
-
1807
- if (!session || !session.refresh_token || !session.expires_at) {
1808
- this._debug('#_autoRefreshTokenTick()', 'no session')
1809
- return
1810
- }
1890
+ try {
1891
+ return await this._useSession(async (result) => {
1892
+ const {
1893
+ data: { session },
1894
+ } = result
1895
+
1896
+ if (!session || !session.refresh_token || !session.expires_at) {
1897
+ this._debug('#_autoRefreshTokenTick()', 'no session')
1898
+ return
1899
+ }
1811
1900
 
1812
- // session will expire in this many ticks (or has already expired if <= 0)
1813
- const expiresInTicks = Math.floor(
1814
- (session.expires_at * 1000 - now) / AUTO_REFRESH_TICK_DURATION
1815
- )
1901
+ // session will expire in this many ticks (or has already expired if <= 0)
1902
+ const expiresInTicks = Math.floor(
1903
+ (session.expires_at * 1000 - now) / AUTO_REFRESH_TICK_DURATION
1904
+ )
1816
1905
 
1817
- this._debug(
1818
- '#_autoRefreshTokenTick()',
1819
- `access token expires in ${expiresInTicks} ticks, a tick lasts ${AUTO_REFRESH_TICK_DURATION}ms, refresh threshold is ${AUTO_REFRESH_TICK_THRESHOLD} ticks`
1820
- )
1906
+ this._debug(
1907
+ '#_autoRefreshTokenTick()',
1908
+ `access token expires in ${expiresInTicks} ticks, a tick lasts ${AUTO_REFRESH_TICK_DURATION}ms, refresh threshold is ${AUTO_REFRESH_TICK_THRESHOLD} ticks`
1909
+ )
1821
1910
 
1822
- if (expiresInTicks <= AUTO_REFRESH_TICK_THRESHOLD) {
1823
- await this._callRefreshToken(session.refresh_token)
1911
+ if (expiresInTicks <= AUTO_REFRESH_TICK_THRESHOLD) {
1912
+ await this._callRefreshToken(session.refresh_token)
1913
+ }
1914
+ })
1915
+ } catch (e: any) {
1916
+ console.error(
1917
+ 'Auto refresh tick failed with error. This is likely a transient error.',
1918
+ e
1919
+ )
1824
1920
  }
1825
- })
1826
- } catch (e: any) {
1827
- console.error('Auto refresh tick failed with error. This is likely a transient error.', e)
1921
+ } finally {
1922
+ this._debug('#_autoRefreshTokenTick()', 'end')
1923
+ }
1924
+ })
1925
+ } catch (e: any) {
1926
+ if (e.isAcquireTimeout) {
1927
+ this._debug('auto refresh token tick lock not available')
1928
+ } else {
1929
+ throw e
1828
1930
  }
1829
- } finally {
1830
- this._debug('#_autoRefreshTokenTick()', 'end')
1831
1931
  }
1832
1932
  }
1833
1933
 
@@ -2102,7 +2202,7 @@ export default class GoTrueClient {
2102
2202
  const {
2103
2203
  data: { user },
2104
2204
  error: userError,
2105
- } = await this.getUser()
2205
+ } = await this._getUser()
2106
2206
  if (userError) {
2107
2207
  return { data: null, error: userError }
2108
2208
  }
@@ -298,143 +298,3 @@ export async function generatePKCEChallenge(verifier: string) {
298
298
  const hashed = await sha256(verifier)
299
299
  return base64urlencode(hashed)
300
300
  }
301
-
302
- const STACK_GUARD_PREFIX = `__stack_guard__`
303
- const STACK_GUARD_SUFFIX = `__`
304
-
305
- // Firefox and WebKit based browsers encode the stack entry differently, but
306
- // they all include the function name. So instead of trying to parse the entry,
307
- // we're only looking for the special string `__stack_guard__${guardName}__`.
308
- // Guard names can only be letters with dashes or underscores.
309
- //
310
- // Example Firefox stack trace:
311
- // ```
312
- // __stack_guard__EXAMPLE__@debugger eval code:1:55
313
- // @debugger eval code:1:3
314
- // ```
315
- //
316
- // Example WebKit/Chrome stack trace:
317
- // ```
318
- // Error
319
- // at Object.__stack_guard__EXAMPLE__ (<anonymous>:1:55)
320
- // at <anonymous>:1:13
321
- // ```
322
- //
323
- const STACK_ENTRY_REGEX = /__stack_guard__([a-zA-Z0-9_-]+)__/
324
-
325
- let STACK_GUARD_CHECKED = false
326
- let STACK_GUARD_CHECK_FN: () => Promise<void> // eslint-disable-line prefer-const
327
-
328
- let STACK_GUARDS_SUPPORTED = false
329
-
330
- /**
331
- * Checks if the current caller of the function is in a {@link
332
- * #stackGuard} of the provided `name`. Works by looking through
333
- * the stack trace of an `Error` object for a special function
334
- * name (generated by {@link #stackGuard}).
335
- *
336
- * @param name The name of the stack guard to check for. Must be `[a-zA-Z0-9_-]` only.
337
- */
338
- export function isInStackGuard(name: string): boolean {
339
- STACK_GUARD_CHECK_FN()
340
-
341
- let error: Error
342
-
343
- try {
344
- throw new Error()
345
- } catch (e: any) {
346
- error = e
347
- }
348
-
349
- const stack = error.stack?.split('\n') ?? []
350
-
351
- for (let i = 0; i < stack.length; i += 1) {
352
- const entry = stack[i]
353
- const match = entry.match(STACK_ENTRY_REGEX)
354
-
355
- if (match && match[1] === name) {
356
- return true
357
- }
358
- }
359
-
360
- return false
361
- }
362
-
363
- /**
364
- * Creates a minification resistant stack guard, i.e. if you
365
- * call {@link #isInStackGuard} from within the `fn` parameter
366
- * function, you will always get `true` otherwise it will be
367
- * `false`.
368
- *
369
- * Works by dynamically defining a function name before calling
370
- * into `fn`, which is then parsed from the stack trace on an
371
- * `Error` object within {@link #isInStackGuard}.
372
- *
373
- * @param name The name of the stack guard. Must be `[a-zA-Z0-9_-]` only.
374
- * @param fn The async/await function to be run within the stack guard.
375
- */
376
- export async function stackGuard<R>(name: string, fn: () => Promise<R>): Promise<R> {
377
- await STACK_GUARD_CHECK_FN()
378
-
379
- const guardName = `${STACK_GUARD_PREFIX}${name}${STACK_GUARD_SUFFIX}`
380
-
381
- const guardFunc: {
382
- [funcName: string]: () => Promise<R>
383
- } = {
384
- // per ECMAScript rules, this defines a new function with the dynamic name
385
- // contained in the `guardName` variable
386
- // this function name shows up in stack traces and is resistant to mangling
387
- // from minification processes as it is determined at runtime
388
- [guardName]: async () => await fn(),
389
- }
390
-
391
- // Safari does not log the name of a dynamically named function unless you
392
- // explicitly set the displayName
393
- Object.assign(guardFunc[guardName], { displayName: guardName })
394
-
395
- return await guardFunc[guardName]()
396
- }
397
-
398
- /**
399
- * Returns if the JavaScript engine supports stack guards. If it doesn't
400
- * certain features that depend on detecting recursive calls should be disabled
401
- * to prevent deadlocks.
402
- */
403
- export async function stackGuardsSupported(): Promise<boolean> {
404
- if (STACK_GUARD_CHECKED) {
405
- return STACK_GUARDS_SUPPORTED
406
- }
407
-
408
- await STACK_GUARD_CHECK_FN()
409
-
410
- return STACK_GUARDS_SUPPORTED
411
- }
412
-
413
- let STACK_GUARD_WARNING_LOGGED = false
414
-
415
- // In certain cases, if this file is transpiled using an ES2015 target, or is
416
- // running in a JS engine that does not support async/await stack traces, this
417
- // function will log a single warning message.
418
- STACK_GUARD_CHECK_FN = async () => {
419
- if (!STACK_GUARD_CHECKED) {
420
- STACK_GUARD_CHECKED = true
421
-
422
- await stackGuard('ENV_CHECK', async () => {
423
- // sleeping for the next tick as Safari loses track of the async/await
424
- // trace beyond this point
425
- await sleep(0)
426
-
427
- const result = isInStackGuard('ENV_CHECK')
428
- STACK_GUARDS_SUPPORTED = result
429
-
430
- if (!result && !STACK_GUARD_WARNING_LOGGED) {
431
- STACK_GUARD_WARNING_LOGGED = true
432
- console.warn(
433
- '@supabase/gotrue-js: Stack guards not supported in this environment. Generally not an issue but may point to a very conservative transpilation environment (use ES2017 or above) that implements async/await with generators, or this is a JavaScript engine that does not support async/await stack traces. Safari is known to not support stack guards.'
434
- )
435
- }
436
-
437
- return result
438
- })
439
- }
440
- }