@supabase/gotrue-js 1.22.13 → 1.22.14

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.
Files changed (45) hide show
  1. package/dist/main/GoTrueClient.d.ts +3 -8
  2. package/dist/main/GoTrueClient.d.ts.map +1 -1
  3. package/dist/main/GoTrueClient.js +46 -17
  4. package/dist/main/GoTrueClient.js.map +1 -1
  5. package/dist/main/lib/constants.d.ts +6 -1
  6. package/dist/main/lib/constants.d.ts.map +1 -1
  7. package/dist/main/lib/constants.js +7 -2
  8. package/dist/main/lib/constants.js.map +1 -1
  9. package/dist/main/lib/fetch.d.ts.map +1 -1
  10. package/dist/main/lib/fetch.js +4 -0
  11. package/dist/main/lib/fetch.js.map +1 -1
  12. package/dist/main/lib/helpers.d.ts +5 -0
  13. package/dist/main/lib/helpers.d.ts.map +1 -1
  14. package/dist/main/lib/helpers.js +44 -1
  15. package/dist/main/lib/helpers.js.map +1 -1
  16. package/dist/main/lib/types.d.ts +7 -0
  17. package/dist/main/lib/types.d.ts.map +1 -1
  18. package/dist/main/lib/version.d.ts +1 -1
  19. package/dist/main/lib/version.js +1 -1
  20. package/dist/module/GoTrueClient.d.ts +3 -8
  21. package/dist/module/GoTrueClient.d.ts.map +1 -1
  22. package/dist/module/GoTrueClient.js +48 -19
  23. package/dist/module/GoTrueClient.js.map +1 -1
  24. package/dist/module/lib/constants.d.ts +6 -1
  25. package/dist/module/lib/constants.d.ts.map +1 -1
  26. package/dist/module/lib/constants.js +6 -1
  27. package/dist/module/lib/constants.js.map +1 -1
  28. package/dist/module/lib/fetch.d.ts.map +1 -1
  29. package/dist/module/lib/fetch.js +4 -0
  30. package/dist/module/lib/fetch.js.map +1 -1
  31. package/dist/module/lib/helpers.d.ts +5 -0
  32. package/dist/module/lib/helpers.d.ts.map +1 -1
  33. package/dist/module/lib/helpers.js +39 -0
  34. package/dist/module/lib/helpers.js.map +1 -1
  35. package/dist/module/lib/types.d.ts +7 -0
  36. package/dist/module/lib/types.d.ts.map +1 -1
  37. package/dist/module/lib/version.d.ts +1 -1
  38. package/dist/module/lib/version.js +1 -1
  39. package/package.json +1 -1
  40. package/src/GoTrueClient.ts +68 -33
  41. package/src/lib/constants.ts +6 -1
  42. package/src/lib/fetch.ts +5 -0
  43. package/src/lib/helpers.ts +36 -0
  44. package/src/lib/types.ts +11 -0
  45. package/src/lib/version.ts +1 -1
@@ -1,6 +1,20 @@
1
1
  import GoTrueApi from './GoTrueApi'
2
- import { isBrowser, getParameterByName, uuid } from './lib/helpers'
3
- import { GOTRUE_URL, DEFAULT_HEADERS, STORAGE_KEY } from './lib/constants'
2
+ import {
3
+ isBrowser,
4
+ getParameterByName,
5
+ uuid,
6
+ setItemAsync,
7
+ removeItemAsync,
8
+ getItemSynchronously,
9
+ getItemAsync,
10
+ } from './lib/helpers'
11
+ import {
12
+ GOTRUE_URL,
13
+ DEFAULT_HEADERS,
14
+ STORAGE_KEY,
15
+ EXPIRY_MARGIN,
16
+ NETWORK_FAILURE,
17
+ } from './lib/constants'
4
18
  import { polyfillGlobalThis } from './lib/polyfills'
5
19
  import { Fetch } from './lib/fetch'
6
20
 
@@ -16,6 +30,7 @@ import type {
16
30
  UserCredentials,
17
31
  VerifyOTPParams,
18
32
  OpenIDConnectCredentials,
33
+ SupportedStorage,
19
34
  } from './lib/types'
20
35
 
21
36
  polyfillGlobalThis() // Make "globalThis" available
@@ -29,17 +44,6 @@ const DEFAULT_OPTIONS = {
29
44
  headers: DEFAULT_HEADERS,
30
45
  }
31
46
 
32
- type AnyFunction = (...args: any[]) => any
33
- type MaybePromisify<T> = T | Promise<T>
34
-
35
- type PromisifyMethods<T> = {
36
- [K in keyof T]: T[K] extends AnyFunction
37
- ? (...args: Parameters<T[K]>) => MaybePromisify<ReturnType<T[K]>>
38
- : T[K]
39
- }
40
-
41
- type SupportedStorage = PromisifyMethods<Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>>
42
-
43
47
  export default class GoTrueClient {
44
48
  /**
45
49
  * Namespace for the GoTrue API methods.
@@ -61,6 +65,7 @@ export default class GoTrueClient {
61
65
  protected multiTab: boolean
62
66
  protected stateChangeEmitters: Map<string, Subscription> = new Map()
63
67
  protected refreshTokenTimer?: ReturnType<typeof setTimeout>
68
+ protected networkRetries: number = 0
64
69
 
65
70
  /**
66
71
  * Create a new client for use in the browser.
@@ -101,6 +106,7 @@ export default class GoTrueClient {
101
106
  this._recoverSession()
102
107
  this._recoverAndRefresh()
103
108
  this._listenForMultiTabEvents()
109
+ this._handleVisibilityChange()
104
110
 
105
111
  if (settings.detectSessionInUrl && isBrowser() && !!getParameterByName('access_token')) {
106
112
  // Handle the OAuth redirect
@@ -610,16 +616,12 @@ export default class GoTrueClient {
610
616
  */
611
617
  private _recoverSession() {
612
618
  try {
613
- const json = isBrowser() && this.localStorage?.getItem(STORAGE_KEY)
614
- if (!json || typeof json !== 'string') {
615
- return null
616
- }
617
-
618
- const data = JSON.parse(json)
619
+ const data = getItemSynchronously(this.localStorage, STORAGE_KEY)
620
+ if (!data) return null
619
621
  const { currentSession, expiresAt } = data
620
622
  const timeNow = Math.round(Date.now() / 1000)
621
623
 
622
- if (expiresAt >= timeNow && currentSession?.user) {
624
+ if (expiresAt >= timeNow + EXPIRY_MARGIN && currentSession?.user) {
623
625
  this._saveSession(currentSession)
624
626
  this._notifyAllSubscribers('SIGNED_IN')
625
627
  }
@@ -634,22 +636,31 @@ export default class GoTrueClient {
634
636
  */
635
637
  private async _recoverAndRefresh() {
636
638
  try {
637
- const json = isBrowser() && (await this.localStorage.getItem(STORAGE_KEY))
638
- if (!json) {
639
- return null
640
- }
641
-
642
- const data = JSON.parse(json)
639
+ const data = await getItemAsync(this.localStorage, STORAGE_KEY)
640
+ if (!data) return null
643
641
  const { currentSession, expiresAt } = data
644
642
  const timeNow = Math.round(Date.now() / 1000)
645
643
 
646
- if (expiresAt < timeNow) {
644
+ if (expiresAt < timeNow + EXPIRY_MARGIN) {
647
645
  if (this.autoRefreshToken && currentSession.refresh_token) {
646
+ this.networkRetries++
648
647
  const { error } = await this._callRefreshToken(currentSession.refresh_token)
649
648
  if (error) {
650
649
  console.log(error.message)
650
+ if (
651
+ error.message === NETWORK_FAILURE.ERROR_MESSAGE &&
652
+ this.networkRetries < NETWORK_FAILURE.MAX_RETRIES
653
+ ) {
654
+ if (this.refreshTokenTimer) clearTimeout(this.refreshTokenTimer)
655
+ this.refreshTokenTimer = setTimeout(
656
+ () => this._recoverAndRefresh(),
657
+ NETWORK_FAILURE.RETRY_INTERVAL ** this.networkRetries * 100 // exponential backoff
658
+ )
659
+ return
660
+ }
651
661
  await this._removeSession()
652
662
  }
663
+ this.networkRetries = 0
653
664
  } else {
654
665
  this._removeSession()
655
666
  }
@@ -703,7 +714,7 @@ export default class GoTrueClient {
703
714
  if (expiresAt) {
704
715
  const timeNow = Math.round(Date.now() / 1000)
705
716
  const expiresIn = expiresAt - timeNow
706
- const refreshDurationBeforeExpires = expiresIn > 60 ? 60 : 0.5
717
+ const refreshDurationBeforeExpires = expiresIn > EXPIRY_MARGIN ? EXPIRY_MARGIN : 0.5
707
718
  this._startAutoRefreshToken((expiresIn - refreshDurationBeforeExpires) * 1000)
708
719
  }
709
720
 
@@ -716,14 +727,14 @@ export default class GoTrueClient {
716
727
 
717
728
  private _persistSession(currentSession: Session) {
718
729
  const data = { currentSession, expiresAt: currentSession.expires_at }
719
- isBrowser() && this.localStorage.setItem(STORAGE_KEY, JSON.stringify(data))
730
+ setItemAsync(this.localStorage, STORAGE_KEY, data)
720
731
  }
721
732
 
722
733
  private async _removeSession() {
723
734
  this.currentSession = null
724
735
  this.currentUser = null
725
736
  if (this.refreshTokenTimer) clearTimeout(this.refreshTokenTimer)
726
- isBrowser() && (await this.localStorage.removeItem(STORAGE_KEY))
737
+ removeItemAsync(this.localStorage, STORAGE_KEY)
727
738
  }
728
739
 
729
740
  /**
@@ -734,7 +745,16 @@ export default class GoTrueClient {
734
745
  if (this.refreshTokenTimer) clearTimeout(this.refreshTokenTimer)
735
746
  if (value <= 0 || !this.autoRefreshToken) return
736
747
 
737
- this.refreshTokenTimer = setTimeout(() => this._callRefreshToken(), value)
748
+ this.refreshTokenTimer = setTimeout(async () => {
749
+ this.networkRetries++
750
+ const { error } = await this._callRefreshToken()
751
+ if (!error) this.networkRetries = 0
752
+ if (
753
+ error?.message === NETWORK_FAILURE.ERROR_MESSAGE &&
754
+ this.networkRetries < NETWORK_FAILURE.MAX_RETRIES
755
+ )
756
+ this._startAutoRefreshToken(NETWORK_FAILURE.RETRY_INTERVAL ** this.networkRetries * 100) // exponential backoff
757
+ }, value)
738
758
  if (typeof this.refreshTokenTimer.unref === 'function') this.refreshTokenTimer.unref()
739
759
  }
740
760
 
@@ -743,7 +763,6 @@ export default class GoTrueClient {
743
763
  */
744
764
  private _listenForMultiTabEvents() {
745
765
  if (!this.multiTab || !isBrowser() || !window?.addEventListener) {
746
- // console.debug('Auth multi-tab support is disabled.')
747
766
  return false
748
767
  }
749
768
 
@@ -752,7 +771,7 @@ export default class GoTrueClient {
752
771
  if (e.key === STORAGE_KEY) {
753
772
  const newSession = JSON.parse(String(e.newValue))
754
773
  if (newSession?.currentSession?.access_token) {
755
- this._recoverAndRefresh()
774
+ this._saveSession(newSession.currentSession)
756
775
  this._notifyAllSubscribers('SIGNED_IN')
757
776
  } else {
758
777
  this._removeSession()
@@ -764,4 +783,20 @@ export default class GoTrueClient {
764
783
  console.error('_listenForMultiTabEvents', error)
765
784
  }
766
785
  }
786
+
787
+ private _handleVisibilityChange() {
788
+ if (!this.multiTab || !isBrowser() || !window?.addEventListener) {
789
+ return false
790
+ }
791
+
792
+ try {
793
+ window?.addEventListener('visibilitychange', () => {
794
+ if (document.visibilityState === 'visible') {
795
+ this._recoverAndRefresh()
796
+ }
797
+ })
798
+ } catch (error) {
799
+ console.error('_handleVisibilityChange', error)
800
+ }
801
+ }
767
802
  }
@@ -2,7 +2,12 @@ import { version } from './version'
2
2
  export const GOTRUE_URL = 'http://localhost:9999'
3
3
  export const AUDIENCE = ''
4
4
  export const DEFAULT_HEADERS = { 'X-Client-Info': `gotrue-js/${version}` }
5
- export const EXPIRY_MARGIN = 60 * 1000
5
+ export const EXPIRY_MARGIN = 10 // in seconds
6
+ export const NETWORK_FAILURE = {
7
+ ERROR_MESSAGE: 'Request Failed',
8
+ MAX_RETRIES: 10,
9
+ RETRY_INTERVAL: 2, // in deciseconds
10
+ }
6
11
  export const STORAGE_KEY = 'supabase.auth.token'
7
12
  export const COOKIE_OPTIONS = {
8
13
  name: 'sb',
package/src/lib/fetch.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { NETWORK_FAILURE } from './constants'
2
+
1
3
  export type Fetch = typeof fetch
2
4
 
3
5
  export interface FetchOptions {
@@ -13,6 +15,9 @@ const _getErrorMessage = (err: any): string =>
13
15
  err.msg || err.message || err.error_description || err.error || JSON.stringify(err)
14
16
 
15
17
  const handleError = (error: any, reject: any) => {
18
+ if (!error?.status) {
19
+ return reject({ message: NETWORK_FAILURE.ERROR_MESSAGE })
20
+ }
16
21
  if (typeof error.json !== 'function') {
17
22
  return reject(error)
18
23
  }
@@ -1,4 +1,5 @@
1
1
  import crossFetch from 'cross-fetch'
2
+ import { SupportedStorage } from './types'
2
3
 
3
4
  export function expiresAt(expiresIn: number) {
4
5
  const timeNow = Math.round(Date.now() / 1000)
@@ -39,3 +40,38 @@ export const resolveFetch = (customFetch?: Fetch): Fetch => {
39
40
  }
40
41
  return (...args) => _fetch(...args)
41
42
  }
43
+
44
+ // LocalStorage helpers
45
+ export const setItemAsync = async (
46
+ storage: SupportedStorage,
47
+ key: string,
48
+ data: any
49
+ ): Promise<void> => {
50
+ isBrowser() && (await storage?.setItem(key, JSON.stringify(data)))
51
+ }
52
+
53
+ export const getItemAsync = async (storage: SupportedStorage, key: string): Promise<any | null> => {
54
+ const value = isBrowser() && (await storage?.getItem(key))
55
+ if (!value) return null
56
+ try {
57
+ return JSON.parse(value)
58
+ } catch {
59
+ return value
60
+ }
61
+ }
62
+
63
+ export const getItemSynchronously = (storage: SupportedStorage, key: string): any | null => {
64
+ const value = isBrowser() && storage?.getItem(key)
65
+ if (!value || typeof value !== 'string') {
66
+ return null
67
+ }
68
+ try {
69
+ return JSON.parse(value)
70
+ } catch {
71
+ return value
72
+ }
73
+ }
74
+
75
+ export const removeItemAsync = async (storage: SupportedStorage, key: string): Promise<void> => {
76
+ isBrowser() && (await storage?.removeItem(key))
77
+ }
package/src/lib/types.ts CHANGED
@@ -209,3 +209,14 @@ export interface OpenIDConnectCredentials {
209
209
  client_id?: string
210
210
  issuer?: string
211
211
  }
212
+
213
+ type AnyFunction = (...args: any[]) => any
214
+ type MaybePromisify<T> = T | Promise<T>
215
+
216
+ type PromisifyMethods<T> = {
217
+ [K in keyof T]: T[K] extends AnyFunction
218
+ ? (...args: Parameters<T[K]>) => MaybePromisify<ReturnType<T[K]>>
219
+ : T[K]
220
+ }
221
+
222
+ export type SupportedStorage = PromisifyMethods<Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>>
@@ -1,2 +1,2 @@
1
1
  // generated by genversion
2
- export const version = '1.22.13'
2
+ export const version = '1.22.14'