From 8de722f2d1d2771aa0853a48fce1393175c74c2e Mon Sep 17 00:00:00 2001 From: Stojan Dimitrovski Date: Wed, 6 Sep 2023 13:54:00 +0200 Subject: [PATCH] feat: `navigatorLock` check for spec compatibility (#761) 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. --- src/GoTrueClient.ts | 3 ++- src/lib/locks.ts | 50 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/GoTrueClient.ts b/src/GoTrueClient.ts index 8b011348f..6539f3eef 100644 --- a/src/GoTrueClient.ts +++ b/src/GoTrueClient.ts @@ -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, @@ -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 diff --git a/src/lib/locks.ts b/src/lib/locks.ts index fd8cb2395..f708a5aa8 100644 --- a/src/lib/locks.ts +++ b/src/lib/locks.ts @@ -15,7 +15,7 @@ export const internals = { ), } -export class NavigatorLockAcquireTimeoutError extends Error { +export abstract class LockAcquireTimeoutError extends Error { public readonly isAcquireTimeout = true constructor(message: string) { @@ -23,6 +23,8 @@ export class NavigatorLockAcquireTimeoutError extends Error { } } +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 @@ -70,6 +72,8 @@ export async function navigatorLock( }, acquireTimeout) } + // MDN article: https://developer.mozilla.org/en-US/docs/Web/API/LockManager/request + return await globalThis.navigator.locks.request( name, acquireTimeout === 0 @@ -84,24 +88,52 @@ export async function navigatorLock( 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() + } } } )