v1.4.0: per-player snapshot + shared market view with owner info (ISS-112)
CI / build + unit + e2e (pull_request) Successful in 14m29s

This commit is contained in:
2026-09-26 18:37:11 +00:00
parent db77f03ce1
commit fe60bd0482
20 changed files with 315 additions and 89 deletions
+8 -5
View File
@@ -10,7 +10,10 @@ import { subscribe } from '@/lib/sse';
// EventSource cannot set headers, so the session comes from X-Flight-Session OR ?session=
// (requireAuthSse). ponytail: validated at CONNECT only — a session deleted/expired mid-stream keeps
// receiving pushes until it reconnects (private infra, 7-day sessions; per-push re-validation would
// be one indexed lookup per tick if that ever matters).
// be one indexed lookup per tick if that ever matters). v1.4.0 (ISS-112): frames are PER PLAYER — the
// route subscribes under THIS session's player id and the engine projects one snapshot per connected
// player, pushing each frame only to its group; connect + pushed frames share one projection
// (getStateSnapshot + sseFrame), so they can never drift.
const encoder = new TextEncoder();
const HEARTBEAT_MS = 15_000;
@@ -30,12 +33,12 @@ export async function GET(req: Request) {
start(controller) {
// Snapshot FIRST, then subscribe — a broadcast can never land before the initial event, so
// events are strictly ordered by sim clock. A tick in the gap just means the client waits ≤1 s
// for the next push instead of seeing an out-of-order frame. v1.3.0 (ISS-110): the connect
// frame carries THIS session's account row; later pushes come from the shared broadcast
// (local player) until per-player frames land (#112).
// for the next push instead of seeing an out-of-order frame. v1.4.0 (ISS-112): connect frame and
// every later push carry THIS session's player projection — subscribe keys on auth.player.id so
// the tick broadcast only sends this client frames projected for its own account row.
getStateSnapshot(auth.player.id)
.then((snapshot) => {
unsubscribe = subscribe((text) => {
unsubscribe = subscribe(auth.player.id, (text) => {
try {
// broadcast carries the bare snapshot JSON — wrap it in an SSE frame here (the route owns
// wire framing, matching the initial snapshot + heartbeat above; lib/sse stays generic).