This commit is contained in:
2026-08-07 14:47:00 +08:00
parent 54fe05d864
commit 0bd872fdbf
12 changed files with 461 additions and 36 deletions
+37
View File
@@ -0,0 +1,37 @@
export interface CrossTabRefreshOptions<T> {
lockName: string
observedRefreshToken: string
currentRefreshToken: () => string
reuseCurrentSession: () => Promise<T>
refresh: (refreshToken: string) => Promise<T>
}
export async function coordinateCrossTabRefresh<T>(options: CrossTabRefreshOptions<T>): Promise<T> {
const run = async () => {
const currentRefreshToken = options.currentRefreshToken()
if (currentRefreshToken && currentRefreshToken !== options.observedRefreshToken) {
return options.reuseCurrentSession()
}
return options.refresh(currentRefreshToken || options.observedRefreshToken)
}
if (typeof navigator === 'undefined' || !navigator.locks) {
try {
return await run()
} catch (error) {
if (!options.currentRefreshToken()) throw error
// Web Locks requires a secure context. On plain HTTP, briefly allow another tab's
// successful rotation to reach shared storage before treating this session as expired.
for (let attempt = 0; attempt < 20; attempt += 1) {
await new Promise((resolve) => setTimeout(resolve, 50))
const currentRefreshToken = options.currentRefreshToken()
if (!currentRefreshToken) throw error
if (currentRefreshToken && currentRefreshToken !== options.observedRefreshToken) {
return options.reuseCurrentSession()
}
}
throw error
}
}
return navigator.locks.request(options.lockName, run)
}