75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
export type SessionStartOrigin = "unknown" | "remote" | "fresh-local" | "already-running-fallback"
|
|
|
|
interface ReconcileArgs {
|
|
remoteStart: number
|
|
localStart: number
|
|
origin: SessionStartOrigin
|
|
}
|
|
|
|
interface ReconcileResult {
|
|
localStart: number
|
|
origin: SessionStartOrigin
|
|
}
|
|
|
|
/**
|
|
* Reconcilia o início da sessão local com o timestamp vindo do servidor.
|
|
* Mantém a proteção contra timestamps remotos defasados (que geravam tempo inflado),
|
|
* mas permite substituir o fallback local quando sabemos que já existia uma sessão em andamento.
|
|
*/
|
|
export function reconcileLocalSessionStart({ remoteStart, localStart, origin }: ReconcileArgs): ReconcileResult {
|
|
const sanitizedRemote = Number(remoteStart) || 0
|
|
const sanitizedLocal = Number(localStart) || 0
|
|
|
|
if (sanitizedRemote <= 0) {
|
|
return { localStart: sanitizedLocal, origin }
|
|
}
|
|
|
|
if (sanitizedLocal <= 0) {
|
|
return { localStart: sanitizedRemote, origin: "remote" }
|
|
}
|
|
|
|
if (sanitizedRemote >= sanitizedLocal) {
|
|
return { localStart: sanitizedRemote, origin: "remote" }
|
|
}
|
|
|
|
if (origin === "already-running-fallback") {
|
|
return { localStart: sanitizedRemote, origin: "remote" }
|
|
}
|
|
|
|
return { localStart: sanitizedLocal, origin }
|
|
}
|
|
|
|
interface OffsetArgs {
|
|
currentOffset: number
|
|
localNow: number
|
|
serverNow?: number | null
|
|
}
|
|
|
|
/**
|
|
* Calcula o deslocamento entre o relógio do navegador e o do servidor.
|
|
* Usamos o menor número de suposições possível: basta termos uma leitura de serverNow
|
|
* para alinhar o relógio local.
|
|
*/
|
|
export function deriveServerOffset({ currentOffset, localNow, serverNow }: OffsetArgs): number {
|
|
if (typeof serverNow !== "number" || Number.isNaN(serverNow)) {
|
|
return currentOffset
|
|
}
|
|
const nextOffset = localNow - serverNow
|
|
if (!Number.isFinite(nextOffset)) {
|
|
return currentOffset
|
|
}
|
|
if (!Number.isFinite(currentOffset) || currentOffset === 0) {
|
|
return nextOffset
|
|
}
|
|
const MAX_REASONABLE_JUMP = 5 * 60 * 1000 // 5 minutos
|
|
if (Math.abs(nextOffset - currentOffset) > MAX_REASONABLE_JUMP) {
|
|
return nextOffset
|
|
}
|
|
return nextOffset
|
|
}
|
|
|
|
export function toServerTimestamp(localNow: number, offset: number): number {
|
|
if (!Number.isFinite(localNow)) return localNow
|
|
if (!Number.isFinite(offset)) return localNow
|
|
return localNow - offset
|
|
}
|