Prevent terminal auth spinner deadlock
This commit is contained in:
@@ -124,6 +124,45 @@ export async function runTests() {
|
||||
"terminal auth bootstrap should accept an authenticated cookie profile immediately",
|
||||
);
|
||||
|
||||
const slowDegradedSession = deferred<{ data: { session: { access_token: string } | null } }>();
|
||||
const timedCookieResult = await loadTerminalAuthProfile({
|
||||
hasSupabasePublicEnv: true,
|
||||
getSession: () => slowDegradedSession.promise,
|
||||
timeoutMs: 1,
|
||||
loadAuthProfile: (accessToken) => {
|
||||
assert(!accessToken, "timeout fallback should use the settled cookie profile when bearer session is still pending");
|
||||
return Promise.resolve({
|
||||
authenticated: true,
|
||||
user_id: "cookie-user",
|
||||
subscription_active: null,
|
||||
degraded_auth_profile: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
assert(
|
||||
timedCookieResult.user_id === "cookie-user" &&
|
||||
timedCookieResult.subscription_active === null,
|
||||
"terminal auth bootstrap timeout must resolve with a known degraded cookie profile instead of spinning forever",
|
||||
);
|
||||
|
||||
const neverCookie = deferred<TerminalAuthProfilePayload>();
|
||||
const neverSession = deferred<{ data: { session: { access_token: string } | null } }>();
|
||||
let timeoutRejected = false;
|
||||
try {
|
||||
await loadTerminalAuthProfile({
|
||||
hasSupabasePublicEnv: true,
|
||||
getSession: () => neverSession.promise,
|
||||
timeoutMs: 1,
|
||||
loadAuthProfile: () => neverCookie.promise,
|
||||
});
|
||||
} catch (error) {
|
||||
timeoutRejected = String(error).includes("Terminal auth bootstrap timeout");
|
||||
}
|
||||
assert(
|
||||
timeoutRejected,
|
||||
"terminal auth bootstrap must reject instead of leaving the loading screen unresolved when no profile request settles",
|
||||
);
|
||||
|
||||
const delayedBearerSession = deferred<{ data: { session: { access_token: string } } }>();
|
||||
let coldStartSettled = false;
|
||||
const coldStartResultPromise = loadTerminalAuthProfile({
|
||||
|
||||
@@ -17,6 +17,7 @@ type LoadTerminalAuthProfileOptions = {
|
||||
accessToken?: string | null,
|
||||
options?: { preferSnapshot?: boolean },
|
||||
) => Promise<TerminalAuthProfilePayload>;
|
||||
timeoutMs?: number;
|
||||
};
|
||||
|
||||
type SettledProfile =
|
||||
@@ -87,11 +88,14 @@ export async function loadTerminalAuthProfile({
|
||||
getSession,
|
||||
hasSupabasePublicEnv,
|
||||
loadAuthProfile,
|
||||
timeoutMs = 6500,
|
||||
}: LoadTerminalAuthProfileOptions) {
|
||||
let resolvedAuthenticated = false;
|
||||
let resolveAuthenticated:
|
||||
| ((payload: TerminalAuthProfilePayload) => void)
|
||||
| null = null;
|
||||
let latestCookiePayload: TerminalAuthProfilePayload | null = null;
|
||||
let latestBearerPayload: TerminalAuthProfilePayload | null = null;
|
||||
|
||||
const authenticatedProfile = new Promise<TerminalAuthProfilePayload>((resolve) => {
|
||||
resolveAuthenticated = resolve;
|
||||
@@ -105,6 +109,7 @@ export async function loadTerminalAuthProfile({
|
||||
|
||||
const cookieProfile = settleProfile(
|
||||
loadAuthProfile(null, { preferSnapshot: true }).then((payload) => {
|
||||
latestCookiePayload = payload;
|
||||
resolveIfAuthenticated(payload);
|
||||
return payload;
|
||||
}),
|
||||
@@ -120,6 +125,7 @@ export async function loadTerminalAuthProfile({
|
||||
).trim();
|
||||
if (!accessToken) return null;
|
||||
const payload = await loadAuthProfile(accessToken, { preferSnapshot: true });
|
||||
latestBearerPayload = payload;
|
||||
resolveIfAuthenticated(payload);
|
||||
return payload;
|
||||
})(),
|
||||
@@ -129,5 +135,20 @@ export async function loadTerminalAuthProfile({
|
||||
([cookieResult, bearerResult]) => firstKnownProfile(cookieResult, bearerResult),
|
||||
);
|
||||
|
||||
return Promise.race([authenticatedProfile, fallbackProfile]);
|
||||
const timeoutProfile = new Promise<TerminalAuthProfilePayload>((resolve, reject) => {
|
||||
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return;
|
||||
globalThis.setTimeout(() => {
|
||||
if (latestBearerPayload) {
|
||||
resolve(latestBearerPayload);
|
||||
return;
|
||||
}
|
||||
if (latestCookiePayload) {
|
||||
resolve(latestCookiePayload);
|
||||
return;
|
||||
}
|
||||
reject(new Error("Terminal auth bootstrap timeout"));
|
||||
}, timeoutMs);
|
||||
});
|
||||
|
||||
return Promise.race([authenticatedProfile, fallbackProfile, timeoutProfile]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user