supabase / auth-js

An isomorphic Javascript library for Supabase Auth.
MIT License
321 stars 153 forks source link

feat: refactor to `_useSession` semantics #726

Closed hf closed 11 months ago

hf commented 12 months ago

getSession() is problematic. The issue comes because whenever the library itself does anything with the session, it may be very quick < 10ms or very slow >= 10s. For example, if a session needs to be refreshed, when you call getSession() the response may be very quick or take very long.

When such long processing is required, a race condition arises between multiple windows or tabs concurrently doing the same processing. In reality, only one such tab / window (in further text "process") needs to do the processing. Therefore, just using a concept like a method call to getSession() is not advisable.

With this change, the internals of the library use a new function called _useSession() which expects a callback to be provided. So long as the callback's promise has not resolved, the session is "being used." In follow up PRs, locking semantics will be used to ensure that only one _useSession callback is active amongst all processes.

The existing getSession() computation is put behind __loadSession which should not be used outside of _useSession(). A debug-mode stack check is performed to make sure that issues like that are caught.

This PR also adds two new utility functions stackGuard and isInStackGuard. These can be used to detect recursive calls or inappropriate use of methods. For example, in this PR a stack guard is used at debug time to make sure that __loadSession is only called within _useSession, as that is the only valid use of the method. In follow up versions this will become a runtime exception.

It works because when using async/await JS engines like browsers and Node will respect stack traces. A stack guard is a special string that shows up in the stack trace of the Error object, which if seen (by isInStackGuard) can be used to detect whether a function was called from the correct parent-caller or whether there is a recursive call.

It's not possible to use plain functions because of JS minification, which mangles the name of virtually all functions, so a trick has to be used to dynamically generate a function name which is the special stack guard string.

github-actions[bot] commented 11 months ago

:tada: This PR is included in version 2.41.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket: