实现账号绑定、持久登录与跨设备对局恢复
CI / test (ubuntu-latest, node 22) (push) Failing after 33m10s
CI / test (ubuntu-latest, node 24) (push) Failing after 34m41s
CI / tooling (lint, imports, typecheck) (push) Failing after 36m11s
CI / test (windows-latest, node 22) (push) Canceled after 0s
CI / test (windows-latest, node 24) (push) Canceled after 0s

This commit is contained in:
herryyun committed 2026-10-14 23:22:50 +08:00
1 parent 488db49f11
commit 7c25c89992
62 files changed
+6855 -1005

No files matched your search

+12
View File
@@ -746,6 +746,7 @@ export function createIdentity(deps = {}) {
let initPromise = null;
let initialized = false;
let resumeBusy = false;
let explicitClaim = false; // account entry POST supplied the token; do not persist until welcome
let current = null; // token this tab uses (null = let the server create a session)
let resolving = null; // { hashes: Set<hash>, taken: Set<hash> } while init() waits for claims
let owned = sget(session, K_TOKEN);
@@ -811,6 +812,7 @@ export function createIdentity(deps = {}) {
// The original page can reappear after resume's query but before its hello. Keep our old
// session in storage and give back the tentative selection immediately in that interval.
if (current && tokenHash(current) === hash) {
explicitClaim = false;
if (current === owned) {
owned = null; welcomeAt = null; welcomed = false;
sdel(session, K_WELCOME);
@@ -911,6 +913,13 @@ export function createIdentity(deps = {}) {
} catch { return false; }
finally { resumeBusy = false; }
},
/** Select an authenticated account entry token without treating an unacknowledged hello as a saved session. */
stageToken(token) {
if (!isToken(token)) return false;
current = token;
explicitClaim = true;
return true;
},
/** @returns {string} remembered player name ('' if none) */
loadName: () => (sget(local, K_NAME) || '').slice(0, 64),
/** @param {string} name */
@@ -924,6 +933,7 @@ export function createIdentity(deps = {}) {
},
/** A shared candidate must never replace a connected session, including a hello already in flight. */
getTokenClaim() {
if (explicitClaim) return { noReplace: false };
return current === owned && owned
? { noReplace: false, ...(welcomeAt != null ? { claimAt: welcomeAt } : {}) }
: { noReplace: true };
@@ -935,6 +945,7 @@ export function createIdentity(deps = {}) {
/** @param {string} token from `welcome` */
saveToken(token) {
if (!isToken(token)) return;
explicitClaim = false;
if (current !== token) rememberMatch(null);
if (owned !== token || welcomeAt == null) welcomeAt = now();
owned = token;
@@ -946,6 +957,7 @@ export function createIdentity(deps = {}) {
},
/** Forget this tab's token (e.g. the server said the session is invalid). */
clearToken() {
explicitClaim = false;
rememberMatch(null);
const t = current || sget(session, K_TOKEN);
current = null;