-
Notifications
You must be signed in to change notification settings - Fork 2
SSE Frontend Implementation #238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great. We came up with one suggestion yesterday to handle the following event scenarios:
- To have an event that's just a signal for the client to do something (e.g. simple action like force logout)
- To have an event that the client just consumes the payload passively (e.g. simple event with a text message like a notification)
- To have an event that the client uses to update a component that display an Entity type via the payload (e.g. a specific action was added to the list of actions, or a specific action was updated)
I think I got these right, but my memory was a little fuzzy. Hope your written notes from our sync call yesterday captured it and can more accurately reflect what we discussed.
And here are some more general things to consider about the design and as you implement with Claude:
Connection State During Auth Transitions
useEffect(() => {
if (!isLoggedIn) {
eventSourceRef.current?.close();
// ...
return;
}
// Create new connection...
}, [isLoggedIn, ...]);Circular Import Risk
Looking at the imports:
use-sse-connection.ts
→ imports useSseConnectionStore from sse-provider.tsx
sse-provider.tsx
→ imports useSseConnection from use-sse-connection.ts
This is a circular dependency. It may work due to how the exports are structured, but:
- Could cause issues with hot module reloading
- Could cause issues with tree-shaking
- Makes the code harder to reason about
Possible fix: Extract useSseConnectionStore and context to a separate file (sse-connection-context.ts).
Scenarios to consider:
| Scenario | What happens? |
|---|---|
| Page load while logged in | Connection created immediately? Or after hydration? |
| Login completes | Does isLoggedIn update synchronously trigger connection? |
| Token refresh | Does auth state flicker? Could cause disconnect/reconnect? |
| Logout while reconnecting | Race between cleanup and reconnection? |
The logoutCleanupRegistry integration helps, but the interplay with React's effect timing deserves attention.
Missing Reconnection State
Phase 2 mentions Reconnecting as a state:
States: Disconnected, Connecting, Connected, Reconnecting, Error
But the actual enum only has four states:
export enum SseConnectionState {
Disconnected = "Disconnected",
Connecting = "Connecting",
Connected = "Connected",
Error = "Error", // No Reconnecting!
}Native EventSource auto-reconnects silently. How do you know if you're in a reconnection vs. first connection? The onerror fires, but then... does onopen fire again on successful reconnect?
If you want accurate UI states, this needs clarification.
| <AuthStoreProvider> | ||
| <OrganizationStateStoreProvider> | ||
| <CoachingRelationshipStateStoreProvider> | ||
| <SessionCleanupProvider> | ||
| <SWRConfig | ||
| value={{ | ||
| revalidateIfStale: true, | ||
| focusThrottleInterval: 10000, | ||
| provider: () => new Map(), | ||
| }} | ||
| > | ||
| <SseProvider> | ||
| {children} | ||
| </SseProvider> | ||
| </SWRConfig> | ||
| </SessionCleanupProvider> | ||
| </CoachingRelationshipStateStoreProvider> | ||
| </OrganizationStateStoreProvider> | ||
| </AuthStoreProvider> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Provider Dependency Chain
The nesting order in providers.tsx:
<AuthStoreProvider> // 1. Auth state
...
<SWRConfig> // 2. SWR cache
<SseProvider> // 3. SSE (needs both auth AND SWR)
{children}
</SseProvider>
</SWRConfig>
...
</AuthStoreProvider>The concern: SseProvider calls useAuthStore() and useSWRConfig().
- Does useAuthStore work correctly when called from inside SseProvider during initial render?
- What's the initialization sequence when the app first loads?
It's worth tracing through the mount sequence mentally or with a quick prototype.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a note on this to the plan in b6c963a
I worked with Claude for a while on it and decided that I didn't want to touch auth logic as part of this effort as I was afraid that it would lead to regression and potentially just a headache of scope creep. Let me know what you think
Upgrade next and eslint-config-next from 15.4.7 to 15.4.8 to address CVE-2025-66478, a critical (CVSS 10.0) remote code execution vulnerability in the React Flight protocol. Upgrade react and react-dom from 19.1.0 to 19.1.2 to address CVE-2025-55182, the upstream React vulnerability that CVE-2025-66478 is based on.
- Move Members link to be a collapsible sub-item under Organization settings - Only show Organization settings menu for Admin and SuperAdmin users - Regular users can no longer see the Members link in the sidebar - Regular users visiting the members page now see a 404 error - Add comprehensive tests for sidebar permission logic - Add tests for members page access control
- Add explicit check for users not in the organization (no_access status) - Extract access control logic into testable shouldDenyMembersPageAccess function - Add comprehensive unit tests for the access control function
Docker image tags must be lowercase, but branch names with uppercase characters (like Claude-generated branch IDs) were causing build failures.
Add note clarifying that AuthStoreProvider's deferred initialization causes two render cycles at root level, but SseProvider only mounts once when AuthStoreContext is available. This guarantees safe access to hydrated auth state when establishing SSE connection. Addresses PR feedback on provider dependency chain analysis. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1. Extract SSE connection context to prevent circular imports - New file: src/lib/contexts/sse-connection-context.tsx - Separates context/hook from provider (matches auth pattern) - Fixes circular dependency between sse-provider.tsx and use-sse-connection.ts 2. Add Reconnecting state to connection state enum - Distinguishes initial connection from reconnection attempts - Enables accurate UI feedback for users 3. Update connection hook to check EventSource.readyState - Detects network errors (auto-reconnect) vs HTTP errors (permanent failure) - Sets Reconnecting state when browser attempts reconnection - Closes connection on permanent failures (401, 403, 500, etc.) 4. Update all import paths to use extracted context - use-sse-connection.ts, use-sse-event-handler.ts, sse-provider.tsx - Connection indicator component includes Reconnecting state Addresses PR feedback on circular imports and reconnection state tracking. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Update OverarchingGoal interface to use ts-luxon DateTime instead of string for date fields (status_changed_at, completed_at, created_at, updated_at). This aligns with the Action and Agreement types for consistency across the codebase. Also extend transformEntityDates helper to handle the additional date fields (status_changed_at, completed_at) used by OverarchingGoal. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Create strongly-typed SSE event definitions using discriminated unions that match the backend's Rust serialization format. Includes: - Action events (action_created, action_updated, action_deleted) - Agreement events (agreement_created, agreement_updated, agreement_deleted) - Overarching goal events (overarching_goal_created, overarching_goal_updated, overarching_goal_deleted) - System events (force_logout) TypeScript automatically narrows event types based on the 'type' property, providing compile-time safety and IntelliSense support. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Create Zustand store for SSE connection state tracking with: - Connection states: Disconnected, Connecting, Connected, Reconnecting, Error - Error tracking with timestamps and attempt numbers - Event tracking (lastConnectedAt, lastEventAt) - DevTools integration Extract context to prevent circular imports between provider and hooks, following the same pattern as AuthStoreProvider. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Implement core SSE infrastructure: - use-sse-connection: Manages native EventSource connection with automatic reconnection, connection state tracking, and integration with logout cleanup registry. Follows browser standards for SSE. - use-sse-event-handler: Type-safe event handler hook using handler ref pattern to prevent listener re-registration. Includes automatic date transformation via transformEntityDates and event tracking. Both hooks integrate with the SSE connection store for centralized state management. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Implement automatic SWR cache revalidation when SSE events arrive. Handles all data change events (action, agreement, and overarching_goal created/updated/deleted) by triggering cache invalidation. This approach requires zero changes to existing components - SWR automatically refetches data when caches are invalidated, maintaining consistency with REST API patterns and providing automatic optimistic updates. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Implement force_logout event handling by reusing the existing useLogoutUser hook. When a force_logout event is received, triggers the full logout sequence (cleanup registry execution, cache clearing, session deletion, and redirect) automatically. This ensures consistent logout behavior whether initiated by the user or by the server via SSE. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Create SseProvider component that composes all SSE hooks and manages the connection store lifecycle. Follows the same pattern as AuthStoreProvider with store creation using useRef. Integrate SseProvider into the root Providers component, nested within SWRConfig to ensure proper access to SWR's mutate functionality for cache invalidation. The provider automatically: - Establishes SSE connection when user is logged in - Invalidates SWR caches on data change events - Handles force logout system events - Cleans up connection on logout 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Update mutate filter to check both string and array keys - Add explicit revalidate: true option to force immediate refetch - Handle SWR's array key format: [url, params] Previously, the filter only checked for string keys, but SWR uses array keys for parameterized requests like [url, sessionId]. This caused SSE events to trigger cache invalidation without actually revalidating any data, as no keys matched the filter. This fix ensures real-time UI updates when SSE events arrive.
- Change context default from undefined to null for better type safety - Extract SSE connection manager into separate component - Use lazy initialization for store creation with useRef check - Add explicit StoreApi type annotations This improves React's hooks rules compliance and ensures the store is only created once per provider instance.
Description
describe the intent of your changes here
GitHub Issue: [Closes|Fixes|Resolves] #your GitHub issue number here
Changes
Screenshots / Videos Showing UI Changes (if applicable)
Testing Strategy
describe how you or someone else can test and verify the changes
Concerns
describe any concerns that might be worth mentioning or discussing