feat(web): resync state on SSE reconnect + always-fresh wake (#54) #59
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab!59
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/54"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #54
Problem
Reconnect ≠ resync: the in-process bus has no replay, so every event emitted while an iOS PWA was backgrounded is lost, and on foreground the stream reconnected but nothing refetched — stale rail/chat until a manual reload. Plus the dead-but-green window:
wake()declined to cycle a socket iOS had already killed for up to ~65s.Changes (web only, no server changes)
sse.ts— resync on reconnect + always-fresh wakeresyncpseudo-event on everyonopenexcept the app session's first (mount fetches cover that one). Never on the wire: absent fromEVENT_TYPES, typedLabEventType | 'resync'on the subscribe surface. Does not reset backoff.online,visibilitychange→visible, plus newpageshow+focuslisteners, removed inclose()) force-cycles viafail(source, true)unless an event (heartbeats count) landed withinFRESH_MS = 10s— the anti-flap guard; a fresh open stampslastEventAt, so app-switcher peeks don't thrash. The 65sSTALE_MSwatchdog stays as the silent-death backstop. Net effect: returning to the app always yields a fresh stream + resync burst.lib/liveResource.ts—createLiveResourcewrapper (new)createResource(both arities, tuple passed through untouched —mutateincluded) plusLiveEventSpec[](type, optionalmatchread fresh per event, optional trailing-edgedebounceMs).resync→ immediate unconditional refetch that cancels pending debounces. All subscriptions/timers torn down viaonCleanup. Centralizes thesolid/reactivityescape the raw handlers carried — migrated call sites shed their disable lines.Migration (hybrid, per issue)
MESSAGES_DEBOUNCE_MS), Repos (incl. OpenCRChip scoped), Credentials, NewRun, History, RepoIssues (3 resources; labels gated on builtin binding inmatch), IssueDetail, RepoLabels, RepoCRs, CRDetail, RepoSettings, ProviderAuthCard (defensive provider match preserved), ParkedSection, AFKStrip (one resource, 3 specs).resync→refetchRun+refetchMessages;stores/cloneProgress.tsuntouched (data-bearing events, never fetches — Repos refetching on resync covers stale progress).Tests (units only — agreed scope)
sse.test.ts(+8): resync on 2nd open not 1st; FRESH_MS wake cycling while nominally connected; no cycle under FRESH_MS;pageshow/focuswakes; close() removes new listeners; backoff not reset by resync; watchdog tests unchanged.lib/liveResource.test.tsx(+10): matching/non-matching/no-match refetch; debounce coalesces; resync unconditional + cancels pending debounce; dispose cleanup; both arities; refetch+mutate passthrough.Acceptance
npm test(478 passed),npm run lint,npm run build,npm run format:checkall green inweb/. No Go changes.🤖 Generated with Claude Code
https://claude.ai/code/session_01Xvfb3MNahsAeR8SH245jWV