Skip to content

Commit

Permalink
feat: navigatorLock check for spec compatibility (#761)
Browse files Browse the repository at this point in the history
It looks like there are some instances where Chrome returns a `null`
lock object from [Navigator LockManager
request](https://developer.mozilla.org/en-US/docs/Web/API/LockManager/request)
even though this should only be the case if `ifAvailable` is set to
`true`. This change logs a warning.
  • Loading branch information
hf committed Sep 6, 2023
1 parent a578667 commit 8de722f
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 10 deletions.
3 changes: 2 additions & 1 deletion src/GoTrueClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
import localStorageAdapter from './lib/local-storage'
import { polyfillGlobalThis } from './lib/polyfills'
import { version } from './lib/version'
import { LockAcquireTimeoutError } from './lib/locks'

import type {
AuthChangeEvent,
Expand Down Expand Up @@ -1935,7 +1936,7 @@ export default class GoTrueClient {
}
})
} catch (e: any) {
if (e.isAcquireTimeout) {
if (e.isAcquireTimeout || e instanceof LockAcquireTimeoutError) {
this._debug('auto refresh token tick lock not available')
} else {
throw e
Expand Down
50 changes: 41 additions & 9 deletions src/lib/locks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ export const internals = {
),
}

export class NavigatorLockAcquireTimeoutError extends Error {
export abstract class LockAcquireTimeoutError extends Error {
public readonly isAcquireTimeout = true

constructor(message: string) {
super(message)
}
}

export class NavigatorLockAcquireTimeoutError extends LockAcquireTimeoutError {}

/**
* Implements a global exclusive lock using the Navigator LockManager API. It
* is available on all browsers released after 2022-03-15 with Safari being the
Expand Down Expand Up @@ -70,6 +72,8 @@ export async function navigatorLock<R>(
}, acquireTimeout)
}

// MDN article: https://developer.mozilla.org/en-US/docs/Web/API/LockManager/request

return await globalThis.navigator.locks.request(
name,
acquireTimeout === 0
Expand All @@ -84,24 +88,52 @@ export async function navigatorLock<R>(
async (lock) => {
if (lock) {
if (internals.debug) {
console.log('@supabase/gotrue-js: navigatorLock: acquired', name)
console.log('@supabase/gotrue-js: navigatorLock: acquired', name, lock.name)
}

try {
return await fn()
} finally {
if (internals.debug) {
console.log('@supabase/gotrue-js: navigatorLock: released', name)
console.log('@supabase/gotrue-js: navigatorLock: released', name, lock.name)
}
}
} else {
if (internals.debug) {
console.log('@supabase/gotrue-js: navigatorLock: not immediately available', name)
}
if (acquireTimeout === 0) {
if (internals.debug) {
console.log('@supabase/gotrue-js: navigatorLock: not immediately available', name)
}

throw new NavigatorLockAcquireTimeoutError(
`Acquiring an exclusive Navigator LockManager lock "${name}" immediately failed`
)
throw new NavigatorLockAcquireTimeoutError(
`Acquiring an exclusive Navigator LockManager lock "${name}" immediately failed`
)
} else {
if (internals.debug) {
try {
const result = await globalThis.navigator.locks.query()

console.log(
'@supabase/gotrue-js: Navigator LockManager state',
JSON.stringify(result, null, ' ')
)
} catch (e: any) {
console.warn(
'@supabase/gotrue-js: Error when querying Navigator LockManager state',
e
)
}
}

// Browser is not following the Navigator LockManager spec, it
// returned a null lock when we didn't use ifAvailable. So we can
// pretend the lock is acquired in the name of backward compatibility
// and user experience and just run the function.
console.warn(
'@supabase/gotrue-js: Navigator LockManager returned a null lock when using #request without ifAvailable set to true, it appears this browser is not following the LockManager spec https://developer.mozilla.org/en-US/docs/Web/API/LockManager/request'
)

return await fn()
}
}
}
)
Expand Down

0 comments on commit 8de722f

Please sign in to comment.