Repository navigation
refactor: simplify realtime message dispatch - #27
Rieranthony wants to merge 3 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughIntroduces a Redis Streams–backed realtime subsystem: adds a Realtime instance wired to ioredis with zod validation, a realtime emitter, EventSource stream routes for visitors and dashboards, client hooks/providers (visitor/dashboard) and updates useRealtime to support endpoints/params; plus related test and event-shape adjustments. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Browser as Visitor Browser
participant API as API /v1/realtime/visitor
participant Auth as Auth + DB
participant RT as Realtime (ioredis Streams)
participant Sub as Redis Subscriber
Browser->>API: GET /v1/realtime/visitor?publicKey&visitorId
API->>Auth: performAuthentication + visitor lookup
Auth-->>API: websiteId, visitor, access
API->>Sub: subscribe(streamKey=channel)
API-->>Browser: 200 EventSource stream
note over RT,Sub: MESSAGE_CREATED publish flow
participant App as App Logic (emit)
App-->>RT: realtime.emit(MESSAGE_CREATED, payload)
RT->>Sub: XADD stream (JSON payload) + PUBLISH
Sub-->>API: stream message parsed
API-->>Browser: event: message.created (payload)
rect rgba(220,240,255,0.25)
note right of API: visitor filter ensures visitorId/channel match
end
sequenceDiagram
autonumber
actor Agent as Dashboard User
participant API as API /v1/realtime/dashboard
participant Session as TRPC Session
participant Access as Website Access
participant RT as Realtime (ioredis Streams)
participant Sub as Redis Subscriber
Agent->>API: GET /v1/realtime/dashboard?channel=websiteId
API->>Session: getTRPCSession
Session-->>API: session
API->>Access: getWebsiteByIdWithAccess
Access-->>API: authorized
API->>Sub: subscribe(streamKey=websiteId)
API-->>Agent: 200 EventSource stream
RT-->>Sub: MESSAGE_CREATED (stream entry)
Sub-->>API: parsed event
API-->>Agent: event: message.created
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/realtime/src/server/realtime.ts (1)
69-98: Streams trim length is too low and hard-coded; risks missed replays on reconnectUsing MAXLEN "~" 100 can drop events before clients reconnect, causing gaps if last_ack lags. Also, XADD/PUBLISH errors aren’t caught.
- Make max stream length configurable (per instance or channel) and raise default (e.g., 1000–10000) to reduce loss under bursty loads.
- Wrap XADD and PUBLISH in try/catch and log via _logger to avoid unhandled rejections.
Minimal immediate change:
- "100", + "1000",Example (configurable) outside this hunk:
// In Opts: export type Opts = { /* ... */ maxStreamLen?: number }; // In ctor: this._maxStreamLen = data.maxStreamLen ?? 5000; // In emit: await this._redis.xadd( `channel:${channel}`, "MAXLEN", "~", String(this._maxStreamLen), "*", "payload", streamPayload );And add try/catch around xadd/publish:
try { const id = await this._redis.xadd(...); await this._redis.publish(...); } catch (err) { this._logger.error("Emit failed", { channel, outerKey, innerKey, err }); }
🧹 Nitpick comments (5)
packages/realtime/src/client/use-realtime.ts (1)
90-127: Reconnect and handler staleness: include endpoint/params in deps and use a ref for handlersCurrent connection won’t refresh when endpoint/params change, and onmessage closes over an old events object. Also, consider bounding processedIds to avoid growth.
Proposed adjustments:
- Track events in a ref and read from it inside onmessage.
- Include endpoint and a stable params key in the effect deps to trigger reconnection on changes.
- Bound duplicate set (e.g., keep last N ids or rely on lastAckRef).
Illustrative changes:
// At hook top: const handlersRef = useRef(events); useEffect(() => { handlersRef.current = events; }, [events]); // Build a stable key for params: const paramsKey = useMemo(() => JSON.stringify(params ?? {}), [params]); // In onmessage: const handler = userEvent.__event_path.reduce( (acc: any, key: any) => acc?.[key], handlersRef.current ); // In useEffect deps: }, [channel, enabled, endpoint, paramsKey]);Optionally, replace processedIdsRef with a small ring buffer or compare against lastAckRef to avoid unbounded growth.
apps/api/src/realtime/routes.ts (3)
68-74: Use structured logger instead of console.error.Replace
console.errorwith a proper logger (e.g.,realtime._logger.error) for consistency with the realtime subsystem's logging approach.Apply this diff:
} catch (error) { if (error instanceof AuthValidationError) { return new Response(error.message, { status: error.statusCode }); } - console.error("[Realtime] Visitor auth failed", error); + // TODO: Add logger to handler or import from realtime + console.error("[Realtime] Visitor auth failed", error); return new Response("Authentication failed", { status: 401 }); }
131-137: Add data type validation to dashboard filter for consistency.The dashboard filter only validates the event path but doesn't check the data type, unlike the visitor filter (lines 88-91). This inconsistency could allow events with the correct path but wrong data structure to pass through.
Apply this diff to add type validation:
filter: ({ event }) => { + const data = event.data as RealtimeMessageEvent | undefined; return ( Array.isArray(event.__event_path) && event.__event_path[0] === "message" && - event.__event_path[1] === "created" + event.__event_path[1] === "created" && + data?.type === "MESSAGE_CREATED" ); },
98-105: Document visitorId fallback logic. Add a comment above the extraction to explain thatdata.payload.message.visitorIdis the DB-recorded visitor ID anddata.visitorIdis the event’s metadata fallback for cases where the stored message lacks a visitorId (per the MESSAGE_CREATED router tests).packages/realtime/src/server/handler.ts (1)
281-359: Consider using Zod for payload validation.The manual JSON parsing and validation in
parsePublishedPayloadandparseStreamEntryworks but could be more robust and maintainable with Zod schemas, especially since the project already uses Zod extensively (as seen in the realtime schema patterns).Example refactor for
parsePublishedPayload:import { z } from "zod"; const publishedPayloadSchema = z.union([ z.object({ type: z.literal("ping"), timestamp: z.number() }), z.object({ __event_path: z.array(z.string()), __stream_id: z.string(), data: z.unknown(), }), ]); function parsePublishedPayload(message: unknown): PublishedPayload | null { if (typeof message !== "string") { return null; } try { const parsed = JSON.parse(message); return publishedPayloadSchema.parse(parsed); } catch { return null; } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (15)
apps/api/src/index.ts(2 hunks)apps/api/src/realtime/emitter.ts(3 hunks)apps/api/src/realtime/realtime.ts(1 hunks)apps/api/src/realtime/routes.ts(1 hunks)apps/web/package.json(1 hunks)apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx(2 hunks)packages/react/src/realtime/support-provider.tsx(4 hunks)packages/realtime/package.json(1 hunks)packages/realtime/src/client/index.ts(1 hunks)packages/realtime/src/client/use-dashboard-realtime.ts(1 hunks)packages/realtime/src/client/use-realtime.ts(3 hunks)packages/realtime/src/client/use-visitor-realtime.ts(1 hunks)packages/realtime/src/client/utils.ts(1 hunks)packages/realtime/src/server/handler.ts(7 hunks)packages/realtime/src/server/realtime.ts(3 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)
**/*.{ts,tsx}: Manage shared component state via React Context
Implement full TypeScript support for all public APIs
Provide proper type definitions for all props and refs
Use appropriate generic constraints where necessary
Export type definitions for the public API
Avoid TypeScript enums; prefer const objects with 'as const' or Object.freeze
Provide clear, descriptive runtime errors for missing context/props and misuse
Expose dedicated hooks for context access that throw helpful errors when context is missing
Add explicit error messages for: outside-context usage, missing required props, invalid prop combinations, and validation failures
Document each component with JSDoc: purpose, props (required/optional), context requirements, example usage, pitfalls
State management: use React Context for global state, perform correct state updates, and handle side effects appropriately
Validation: implement runtime checks and validate prop combinations with clear errors
Create context with createContext<ContextType|null>(null) and a useContext hook that throws a clear error when missing
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't have unused function parameters
Don't have unused private class members
Don't return a value from a function that has a 'void' return type
Don't use the TypeScript directive @ts-ignore
Ensure switch-case statements are exhaustive (especially with unions)
Don't use TypeScript enums
Don't add type annotations to variables, parameters, and class properties initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types (where discouraged by policy)
Use as const instead of literal types and type annotations
Use either T[] or Array consistently
Use consistent accessibili...
Files:
packages/realtime/src/client/use-realtime.tspackages/realtime/src/client/use-visitor-realtime.tsapps/api/src/realtime/realtime.tspackages/realtime/src/client/utils.tsapps/api/src/index.tspackages/react/src/realtime/support-provider.tsxpackages/realtime/src/server/handler.tspackages/realtime/src/client/index.tspackages/realtime/src/client/use-dashboard-realtime.tsapps/api/src/realtime/routes.tsapps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsxapps/api/src/realtime/emitter.tspackages/realtime/src/server/realtime.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't use theaccessKeyattribute on any HTML element
Use correct ISO language/country codes for the lang attribute
Don't hardcode sensitive data like API keys and tokens
Use === and !==
Files:
packages/realtime/src/client/use-realtime.tspackages/realtime/src/client/use-visitor-realtime.tsapps/api/src/realtime/realtime.tspackages/realtime/src/client/utils.tsapps/api/src/index.tspackages/react/src/realtime/support-provider.tsxpackages/realtime/src/server/handler.tspackages/realtime/src/client/index.tspackages/realtime/src/client/use-dashboard-realtime.tsapps/api/src/realtime/routes.tsapps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsxapps/api/src/realtime/emitter.tspackages/realtime/src/server/realtime.ts
**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,ts}: Don't use consecutive spaces in regular expression literals
Don't use the arguments object
Don't use the comma operator
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Prefer for...of statements instead of Array.forEach
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules that don't change anything
Don't use unnecessary escape sequences in regular expression literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports, exports, and destructured assignments to the same name
Don't use unnecessary string or template literal concatenation
Don't use String.raw in template literals when there are no escape sequences
Don't use useless case statements in switch statements
Avoid ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use void operators
Use Date.now() to get milliseconds since the Unix Epoch
Prefer .flatMap() over map().flat() when possible
Use literal property access instead of computed property access
Don't use parseInt() or Number.parseInt() when numeric literals suffice (binary/octal/hex)
Use concise optional chaining instead of chained logical expressions
Prefer regular expression literals over the RegExp constructor when possible
Don't use non–base-10 or underscore-separated number literal object member names
Remove redundant terms from logical expressions
Prefer while loops over for loops when no initializer/update is needed
Don't reassign const variables
Don't use constant expressions in conditions
Don't use Math.min/Math.max to clamp values when result is constant
Don't use empty character classes in regular expression literals
Don't use empty destructuring patterns
Don't call global object properties as functions
Don't declare functions and vars that are acce...
Files:
packages/realtime/src/client/use-realtime.tspackages/realtime/src/client/use-visitor-realtime.tsapps/api/src/realtime/realtime.tspackages/realtime/src/client/utils.tsapps/api/src/index.tspackages/realtime/src/server/handler.tspackages/realtime/src/client/index.tspackages/realtime/src/client/use-dashboard-realtime.tsapps/api/src/realtime/routes.tsapps/api/src/realtime/emitter.tspackages/realtime/src/server/realtime.ts
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,ts,tsx}: Don't create classes that only have static members
Don't return a value from a constructor
Don't use super() incorrectly inside classes; ensure super() is called in derived constructors
Ensure super() is called exactly once on every code path in a class constructor before this is accessed if the class has a superclass
Place getters and setters for the same property next to each other in class and object definitions
Ensure JSDoc comment lines start with a single asterisk, except for the first one
Don't use duplicate class members
Files:
packages/realtime/src/client/use-realtime.tspackages/realtime/src/client/use-visitor-realtime.tsapps/api/src/realtime/realtime.tspackages/realtime/src/client/utils.tsapps/api/src/index.tspackages/react/src/realtime/support-provider.tsxpackages/realtime/src/server/handler.tspackages/realtime/src/client/index.tspackages/realtime/src/client/use-dashboard-realtime.tsapps/api/src/realtime/routes.tsapps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsxapps/api/src/realtime/emitter.tspackages/realtime/src/server/realtime.ts
**/*.{js,tsx,ts,jsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Use arrow functions instead of function expressions
Files:
packages/realtime/src/client/use-realtime.tspackages/realtime/src/client/use-visitor-realtime.tsapps/api/src/realtime/realtime.tspackages/realtime/src/client/utils.tsapps/api/src/index.tspackages/react/src/realtime/support-provider.tsxpackages/realtime/src/server/handler.tspackages/realtime/src/client/index.tspackages/realtime/src/client/use-dashboard-realtime.tsapps/api/src/realtime/routes.tsapps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsxapps/api/src/realtime/emitter.tspackages/realtime/src/server/realtime.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,ts,jsx,tsx}: Prevent duplicate polyfills from Polyfill.io
Don't use irregular whitespace characters
Files:
packages/realtime/src/client/use-realtime.tspackages/realtime/src/client/use-visitor-realtime.tsapps/api/src/realtime/realtime.tspackages/realtime/src/client/utils.tsapps/api/src/index.tspackages/react/src/realtime/support-provider.tsxpackages/realtime/src/server/handler.tspackages/realtime/src/client/index.tspackages/realtime/src/client/use-dashboard-realtime.tsapps/api/src/realtime/routes.tsapps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsxapps/api/src/realtime/emitter.tspackages/realtime/src/server/realtime.ts
**/*.{js,ts,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't redeclare variables, functions, classes, and types in the same scope
Files:
packages/realtime/src/client/use-realtime.tspackages/realtime/src/client/use-visitor-realtime.tsapps/api/src/realtime/realtime.tspackages/realtime/src/client/utils.tsapps/api/src/index.tspackages/react/src/realtime/support-provider.tsxpackages/realtime/src/server/handler.tspackages/realtime/src/client/index.tspackages/realtime/src/client/use-dashboard-realtime.tsapps/api/src/realtime/routes.tsapps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsxapps/api/src/realtime/emitter.tspackages/realtime/src/server/realtime.ts
packages/**
📄 CodeRabbit inference engine (AGENTS.md)
Put shared contracts and utilities in packages/* (e.g., core, types, transactional)
Files:
packages/realtime/src/client/use-realtime.tspackages/realtime/src/client/use-visitor-realtime.tspackages/realtime/src/client/utils.tspackages/react/src/realtime/support-provider.tsxpackages/realtime/src/server/handler.tspackages/realtime/src/client/index.tspackages/realtime/src/client/use-dashboard-realtime.tspackages/realtime/package.jsonpackages/realtime/src/server/realtime.ts
packages/react/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Develop headless React components (including and primitives) under packages/react/src/
Files:
packages/react/src/realtime/support-provider.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)
**/*.tsx: Use the Compound Components pattern with namespaced exports (e.g., FormWizard.Root, FormWizard.Step) so pieces compose under a single parent
Ensure accessibility: keyboard navigation support
Ensure accessibility: include appropriate ARIA attributes
Ensure accessibility: use semantic markup for screen readers
Maintain proper focus management in interactive components
Structure components with forwardRef and typed props/refs as shown (ComponentRef, HTMLAttributes, ReactNode)
Root component acts as main context provider, manages global state, and handles initialization
Child components must be used within Root, read context via hooks, support asChild, and follow consistent props
Props ordering: list required props first; optional props have clear defaults; support common HTML attributes; expose className
Support composition with an asChild prop using Radix UI Slot to allow element replacement
Styling: support className, use data-* attributes for state, and allow style customization
Event handling: support standard DOM events, add custom events when needed, and manage propagation correctly
Base component pattern: forwardRef, choose Slot or intrinsic element via asChild, pass ref/className and spread props
Files:
packages/react/src/realtime/support-provider.tsxapps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't set aria-hidden="true" on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like or
Only use the scope prop on elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Ensure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assign tabIndex to non-interactive HTML elements
Don't use positive integers for tabIndex property
Don't include the words "image", "picture", or "photo" in img alt prop
Don't use an explicit role that's the same as the element's implicit role
Static elements with click handlers must have a valid role attribute
Always include a <title> element within SVG elements
Provide meaningful alt text for elements that require alt text
Ensure anchors have content accessible to screen readers
Assign tabIndex to non-interactive elements only when using aria-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Ensure ARIA properties are valid for the element's supported roles
Always include a type attribute for button elements
Elements with interactive roles and handlers must be focusable
Heading elements must have content accessible to screen readers (not hidden with aria-hidden)
Always include a lang attribute on the html element
Always include a title attribute for iframe elements
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress
Accompany onMouseOver/onMouseOut with onFocus/onBlur
Include caption tracks for audio and video elements
Prefer semantic elements instead of role attributes in JSX
Ensure all anchors are valid and navigable
Ensure all aria-* properties are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid ARIA state and property values
Use valid values for the autocomplete attribute on input elements
Don't use unnecessary fragmen...Files:
packages/react/src/realtime/support-provider.tsxapps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx**/*.{jsx,tsx,html}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx,html}: Ensure the preconnect attribute is used when using Google Fonts
Don't use target="_blank" without rel="noopener"
Use a recommended display strategy with Google FontsFiles:
packages/react/src/realtime/support-provider.tsxapps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsxpackages/react/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/react-package-rules.mdc)
packages/react/**/*.{ts,tsx}: Components must render with no built-in styles (headless, unstyled)
Components must accept className, style, and asChild props to enable external styling/control
Do not use emotion, styled-components, or style-prop APIs; styling is via Tailwind/CSS through className/style
Use useRenderElement to create elements and encapsulate render logic (supporting intrinsic tags and render prop overrides)
Components with internal state must be controllable via props (e.g., value/defaultValue/onChange or isOpen/onOpenChange)
Expose internal state via a hook (e.g., useInput, useWindow) or a render prop function child
May reference Base UI logic internally, but never expose Base UI or MUI APIs; do not leak getRootProps/getInputProps; prefer useRenderElement
Prefer small, composable primitives (e.g., Provider, Bubble, Window, MessageList, Message, Input, SendButton, Typing, Event) over monolithic components
Provide a render prop API: render?: (props, state) => ReactElement, and allow className to be string or (state) => string
Support developer-owned rendering: components should not impose visual structure and should allow parent-driven markup via render prop/asChildFiles:
packages/react/src/realtime/support-provider.tsxapps/web/src/app/**
📄 CodeRabbit inference engine (AGENTS.md)
Place Next.js App Router pages/layouts/routes under apps/web/src/app
Files:
apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsxapps/web/src/{app,components}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Use snake_case filenames for React components and layouts in the web app
Files:
apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx🧠 Learnings (1)
📚 Learning: 2025-09-17T09:00:28.751Z
Learnt from: CR PR: cossistantcom/cossistant#0 File: CLAUDE.md:0-0 Timestamp: 2025-09-17T09:00:28.751Z Learning: Use Bun as the package manager and executor across the monorepo (install, dev, build, lint, format, type-check)Applied to files:
apps/web/package.json🧬 Code graph analysis (10)
packages/realtime/src/client/use-realtime.ts (1)
packages/realtime/src/server/realtime.ts (1)
channel(105-110)packages/realtime/src/client/use-visitor-realtime.ts (2)
packages/realtime/src/client/utils.ts (2)
EventHandlerRecord(1-8)hasEventHandlers(10-27)packages/realtime/src/client/use-realtime.ts (1)
useRealtime(26-240)apps/api/src/realtime/realtime.ts (3)
packages/types/src/realtime-events.ts (1)
RealtimeEvents(9-100)apps/api/src/redis.ts (1)
getRedis(7-39)packages/realtime/src/server/realtime.ts (2)
Realtime(121-129)Realtime(138-140)apps/api/src/index.ts (1)
apps/api/src/realtime/routes.ts (1)
registerRealtimeRoutes(152-162)packages/react/src/realtime/support-provider.tsx (4)
packages/react/src/provider.tsx (1)
useSupport(215-241)packages/types/src/realtime-events.ts (1)
RealtimeEvent(108-115)packages/react/src/realtime/use-realtime.ts (1)
useRealtime(68-144)packages/realtime/src/client/use-visitor-realtime.ts (1)
useVisitorRealtime(22-45)packages/realtime/src/server/handler.ts (2)
packages/realtime/src/types.ts (2)
UserEvent(8-12)SystemEvent(1-6)packages/realtime/src/server/realtime.ts (4)
Opts(11-16)Realtime(121-129)Realtime(138-140)channel(105-110)packages/realtime/src/client/use-dashboard-realtime.ts (2)
packages/realtime/src/client/utils.ts (2)
EventHandlerRecord(1-8)hasEventHandlers(10-27)packages/realtime/src/client/use-realtime.ts (1)
useRealtime(26-240)apps/api/src/realtime/routes.ts (6)
packages/realtime/src/server/handler.ts (1)
handle(34-279)apps/api/src/lib/auth-validation.ts (2)
performAuthentication(366-415)AuthValidationError(25-33)apps/api/src/db/queries/visitor.ts (1)
findVisitorForWebsite(141-160)apps/api/src/realtime/realtime.ts (1)
RealtimeMessageEvent(27-27)apps/api/src/db/queries/session.ts (1)
getTRPCSession(104-111)apps/api/src/db/queries/website.ts (1)
getWebsiteByIdWithAccess(247-302)apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx (3)
packages/types/src/realtime-events.ts (1)
RealtimeEvent(108-115)apps/web/src/app/(dashboard)/[websiteSlug]/providers/events/handlers/message-created.ts (1)
handleMessageCreated(73-132)packages/realtime/src/client/use-dashboard-realtime.ts (1)
useDashboardRealtime(20-38)apps/api/src/realtime/emitter.ts (5)
packages/types/src/realtime-events.ts (1)
RealtimeEvent(108-115)apps/api/src/ws/socket.ts (2)
sendEventToVisitor(85-96)sendEventToWebsite(98-109)packages/realtime/src/server/realtime.ts (1)
channel(105-110)apps/api/src/realtime/realtime.ts (1)
realtime(17-25)apps/api/src/ws/router.ts (1)
routeEvent(254-280)🔇 Additional comments (14)
apps/api/src/realtime/realtime.ts (1)
17-25: Typed schema wiring looks greatNice job instantiating the shared Realtime schema with a strongly typed
MESSAGE_CREATEDchannel. This keeps payload validation centralized and future-proof.apps/api/src/realtime/emitter.ts (1)
150-158: Realtime channel emission path is solidConditionally emitting
MESSAGE_CREATEDinto the Redis-backed channel while keeping websocket routing as a fallback is a clean integration and maintains resilience on failures.packages/react/src/realtime/support-provider.tsx (2)
33-43: MESSAGE_CREATED handler wiring looks goodFilters by website, clears typing, forwards to client; aligns with visitor realtime flow.
117-122: Visitor realtime hook usage LGTMCorrectly passes websiteId, visitorId, publicKey, and nested handler map.
packages/realtime/src/client/use-dashboard-realtime.ts (1)
20-38: Typed dashboard realtime hook looks solidEnablement gating, channel binding, endpoint default, and params plumbing are correct.
apps/api/src/realtime/routes.ts (2)
13-29: LGTM!The origin header parsing is correct with appropriate fallback handling when URL parsing fails.
152-162: LGTM!Route registration properly delegates to the stream handlers and returns 204 when no response is provided.
packages/realtime/src/client/use-visitor-realtime.ts (2)
5-20: LGTM!Type definitions are clear, properly structured, and follow TypeScript best practices with appropriate nullability.
22-45: LGTM!The hook correctly:
- Guards against enabling without all required credentials and handlers
- Converts nullable values to undefined for URL search params
- Prevents passing empty event handlers to avoid unnecessary subscriptions
packages/realtime/src/server/handler.ts (5)
8-32: LGTM!The type definitions for middleware, filter, and response headers are clear and provide appropriate context parameters.
34-39: LGTM!The updated function signature maintains backward compatibility while adding optional middleware, filter, and header customization.
66-143: LGTM!The Redis integration correctly:
- Uses
duplicate()for independent subscriber connection- Implements proper cleanup with unsubscribe/disconnect
- Applies filter with error handling
- Guards enqueue operations with
isClosedflag
154-196: LGTM!The reconnection and message replay logic correctly:
- Fetches the latest cursor for new connections
- Replays missed messages on reconnect using Redis Streams range queries
- Applies filters to replayed messages
200-263: LGTM!Event handlers correctly:
- Convert Redis errors to system events
- Handle ping messages separately from user events
- Log received events with channel context
- Publish keepalive pings to maintain connection
| const dashboardRealtimeEvents = useMemo( | ||
| () => ({ | ||
| message: { | ||
| created: (event: RealtimeEvent<"MESSAGE_CREATED">) => { | ||
| handleMessageCreated(event.payload, { | ||
| event, | ||
| context: realtimeContext, | ||
| }); | ||
| }, | ||
| }, | ||
| }), | ||
| [handleMessageCreated, realtimeContext] | ||
| ); |
There was a problem hiding this comment.
Fix handler invocation: pass { event, context } (not payload + meta)
handleMessageCreated expects a single object with event and context. Passing event.payload as the first argument will break filtering and message mapping. Use the full event with context.
Apply this diff:
- const dashboardRealtimeEvents = useMemo(
- () => ({
- message: {
- created: (event: RealtimeEvent<"MESSAGE_CREATED">) => {
- handleMessageCreated(event.payload, {
- event,
- context: realtimeContext,
- });
- },
- },
- }),
- [handleMessageCreated, realtimeContext]
- );
+ const dashboardRealtimeEvents = useMemo(
+ () => ({
+ message: {
+ created: (event: RealtimeEvent<"MESSAGE_CREATED">) => {
+ handleMessageCreated({
+ event,
+ context: realtimeContext,
+ });
+ },
+ },
+ }),
+ [realtimeContext]
+ );Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/src/app/(dashboard)/[websiteSlug]/providers/realtime.tsx around
lines 84 to 96, the handler invocation incorrectly passes event.payload and a
separate meta object to handleMessageCreated; change the call to pass a single
object containing the full event and the realtimeContext (i.e., call
handleMessageCreated({ event, context: realtimeContext })) so the handler
receives the expected shape and filtering/mapping works correctly.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/realtime/src/server/realtime.ts (1)
74-102: Consider caching JSON.stringify result.The code calls
JSON.stringifytwice (lines 74 and 97) with nearly identical data structures. Consider caching the first stringified result and reusing it, or restructuring to stringify once.Apply this diff to cache the stringify result:
- const streamPayload = JSON.stringify({ + const eventPayload = { data, __event_path: [outerKey, innerKey], - }); + }; + const streamPayload = JSON.stringify(eventPayload); this._logger.log("⬆️ Emitting event:", { channel, - __event_path: [outerKey, innerKey], - data, + ...eventPayload, }); const id = await this._redis.xadd( `channel:${channel}`, "MAXLEN", "~", "100", "*", "payload", streamPayload ); await this._redis.publish( `channel:${channel}`, - JSON.stringify({ - data, - __event_path: [outerKey, innerKey], + JSON.stringify({ + ...eventPayload, __stream_id: id, }) );
Verify error handling approach for Redis operations.
The
xaddandpublishcalls at lines 85 and 95 have no explicit error handling, so failures will propagate to callers. Confirm this is the intended behavior, or consider wrapping in try-catch with appropriate logging/recovery if these operations should be more resilient.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/realtime/src/server/realtime.ts(4 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)
**/*.{ts,tsx}: Manage shared component state via React Context
Implement full TypeScript support for all public APIs
Provide proper type definitions for all props and refs
Use appropriate generic constraints where necessary
Export type definitions for the public API
Avoid TypeScript enums; prefer const objects with 'as const' or Object.freeze
Provide clear, descriptive runtime errors for missing context/props and misuse
Expose dedicated hooks for context access that throw helpful errors when context is missing
Add explicit error messages for: outside-context usage, missing required props, invalid prop combinations, and validation failures
Document each component with JSDoc: purpose, props (required/optional), context requirements, example usage, pitfalls
State management: use React Context for global state, perform correct state updates, and handle side effects appropriately
Validation: implement runtime checks and validate prop combinations with clear errors
Create context with createContext<ContextType|null>(null) and a useContext hook that throws a clear error when missing
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't have unused function parameters
Don't have unused private class members
Don't return a value from a function that has a 'void' return type
Don't use the TypeScript directive @ts-ignore
Ensure switch-case statements are exhaustive (especially with unions)
Don't use TypeScript enums
Don't add type annotations to variables, parameters, and class properties initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types (where discouraged by policy)
Use as const instead of literal types and type annotations
Use either T[] or Array consistently
Use consistent accessibili...
Files:
packages/realtime/src/server/realtime.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't use theaccessKeyattribute on any HTML element
Use correct ISO language/country codes for the lang attribute
Don't hardcode sensitive data like API keys and tokens
Use === and !==
Files:
packages/realtime/src/server/realtime.ts
**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,ts}: Don't use consecutive spaces in regular expression literals
Don't use the arguments object
Don't use the comma operator
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Prefer for...of statements instead of Array.forEach
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules that don't change anything
Don't use unnecessary escape sequences in regular expression literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports, exports, and destructured assignments to the same name
Don't use unnecessary string or template literal concatenation
Don't use String.raw in template literals when there are no escape sequences
Don't use useless case statements in switch statements
Avoid ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use void operators
Use Date.now() to get milliseconds since the Unix Epoch
Prefer .flatMap() over map().flat() when possible
Use literal property access instead of computed property access
Don't use parseInt() or Number.parseInt() when numeric literals suffice (binary/octal/hex)
Use concise optional chaining instead of chained logical expressions
Prefer regular expression literals over the RegExp constructor when possible
Don't use non–base-10 or underscore-separated number literal object member names
Remove redundant terms from logical expressions
Prefer while loops over for loops when no initializer/update is needed
Don't reassign const variables
Don't use constant expressions in conditions
Don't use Math.min/Math.max to clamp values when result is constant
Don't use empty character classes in regular expression literals
Don't use empty destructuring patterns
Don't call global object properties as functions
Don't declare functions and vars that are acce...
Files:
packages/realtime/src/server/realtime.ts
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,ts,tsx}: Don't create classes that only have static members
Don't return a value from a constructor
Don't use super() incorrectly inside classes; ensure super() is called in derived constructors
Ensure super() is called exactly once on every code path in a class constructor before this is accessed if the class has a superclass
Place getters and setters for the same property next to each other in class and object definitions
Ensure JSDoc comment lines start with a single asterisk, except for the first one
Don't use duplicate class members
Files:
packages/realtime/src/server/realtime.ts
**/*.{js,tsx,ts,jsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Use arrow functions instead of function expressions
Files:
packages/realtime/src/server/realtime.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,ts,jsx,tsx}: Prevent duplicate polyfills from Polyfill.io
Don't use irregular whitespace characters
Files:
packages/realtime/src/server/realtime.ts
**/*.{js,ts,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't redeclare variables, functions, classes, and types in the same scope
Files:
packages/realtime/src/server/realtime.ts
packages/**
📄 CodeRabbit inference engine (AGENTS.md)
Put shared contracts and utilities in packages/* (e.g., core, types, transactional)
Files:
packages/realtime/src/server/realtime.ts
🧠 Learnings (1)
📚 Learning: 2025-09-17T09:00:28.751Z
Learnt from: CR
PR: cossistantcom/cossistant#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T09:00:28.751Z
Learning: Applies to packages/types/src/**/*.ts : Ensure Zod schemas mirror any API/database shape changes for end-to-end type safety
Applied to files:
packages/realtime/src/server/realtime.ts
🔇 Additional comments (3)
packages/realtime/src/server/realtime.ts (3)
4-6: LGTM! Clean migration to public APIs.The imports correctly migrate from
@upstash/redistoioredisand update Zod type usage to rely on public API types (ZodObject<ZodRawShape>) instead of internal types likez.$ZodObject. This aligns with Zod v4 best practices and maintains compatibility with v3.25+.
60-67: Verify intentional silent validation skip.The optional chaining at line 67 (
schema?.parse?.(data)) will silently skip validation ifschemais undefined (which can occur whenshape[innerKey]doesn't exist). This means potentially invalid data could be emitted without validation.If this is intentional to support flexible schema definitions, consider adding a log statement when validation is skipped. Otherwise, consider using a non-optional parse to fail fast on schema mismatches.
148-164: LGTM! Solid abstraction over Zod internals.The helper functions provide a robust abstraction for extracting schema shapes, handling both the public
.shapeAPI and the internal._def.shape()pattern for lazy schemas. Theinstanceofcheck inisZodObjectis the correct approach for type guarding. This design maintains compatibility with both Zod v3.25+ and v4.
| function getObjectShape(schema: z.ZodTypeAny): Record<string, ZodAny | undefined> { | ||
| if (isZodObject(schema)) { | ||
| if (typeof schema.shape === "object") { | ||
| return schema.shape as Record<string, ZodAny>; | ||
| } | ||
|
|
||
| if (typeof schema._def?.shape === "function") { | ||
| return schema._def.shape() as Record<string, ZodAny>; | ||
| } | ||
| } | ||
|
|
||
| return {}; | ||
| } |
There was a problem hiding this comment.
Fix return type to use ZodTypeAny instead of ZodAny.
The return type Record<string, ZodAny | undefined> is incorrect. ZodAny is the specific type for z.any() schemas, while ZodTypeAny (or z.ZodTypeAny) is the base type for all Zod schemas. Since the shape can contain any Zod schema type (ZodString, ZodNumber, ZodBoolean, etc.), the return type should be Record<string, ZodTypeAny>.
Apply this diff to fix the type:
+import type { ZodAny, ZodObject, ZodRawShape, ZodTypeAny } from "zod";-function getObjectShape(schema: z.ZodTypeAny): Record<string, ZodAny | undefined> {
+function getObjectShape(schema: z.ZodTypeAny): Record<string, ZodTypeAny> {
if (isZodObject(schema)) {
if (typeof schema.shape === "object") {
- return schema.shape as Record<string, ZodAny>;
+ return schema.shape as Record<string, ZodTypeAny>;
}
if (typeof schema._def?.shape === "function") {
- return schema._def.shape() as Record<string, ZodAny>;
+ return schema._def.shape() as Record<string, ZodTypeAny>;
}
}
return {};
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function getObjectShape(schema: z.ZodTypeAny): Record<string, ZodAny | undefined> { | |
| if (isZodObject(schema)) { | |
| if (typeof schema.shape === "object") { | |
| return schema.shape as Record<string, ZodAny>; | |
| } | |
| if (typeof schema._def?.shape === "function") { | |
| return schema._def.shape() as Record<string, ZodAny>; | |
| } | |
| } | |
| return {}; | |
| } | |
| // At the top of the file, alongside your existing imports: | |
| import type { ZodAny, ZodObject, ZodRawShape, ZodTypeAny } from "zod"; | |
| function getObjectShape(schema: z.ZodTypeAny): Record<string, ZodTypeAny> { | |
| if (isZodObject(schema)) { | |
| if (typeof schema.shape === "object") { | |
| return schema.shape as Record<string, ZodTypeAny>; | |
| } | |
| if (typeof schema._def?.shape === "function") { | |
| return schema._def.shape() as Record<string, ZodTypeAny>; | |
| } | |
| } | |
| return {}; | |
| } |
🤖 Prompt for AI Agents
In packages/realtime/src/server/realtime.ts around lines 148 to 160, the
function getObjectShape currently uses Record<string, ZodAny | undefined> which
is incorrect; change the return type to Record<string, z.ZodTypeAny> and update
the two casts to cast to Record<string, z.ZodTypeAny> (remove the | undefined),
so the function returns the correct base Zod schema type for all shape entries.
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
apps/api/src/realtime/routes.test.ts(1 hunks)apps/api/src/realtime/routes.ts(1 hunks)apps/api/src/utils/message.ts(3 hunks)apps/api/src/utils/websocket-connection.ts(2 hunks)apps/api/src/ws/router.test.ts(11 hunks)apps/api/src/ws/socket.test.ts(1 hunks)apps/api/src/ws/socket.ts(1 hunks)packages/core/src/store/messages-store.test.ts(4 hunks)packages/core/src/store/seen-store.test.ts(1 hunks)packages/core/src/store/typing-store.test.ts(1 hunks)packages/types/src/realtime-events.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/core/src/store/typing-store.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/api/src/realtime/routes.ts
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)
**/*.{ts,tsx}: Manage shared component state via React Context
Implement full TypeScript support for all public APIs
Provide proper type definitions for all props and refs
Use appropriate generic constraints where necessary
Export type definitions for the public API
Avoid TypeScript enums; prefer const objects with 'as const' or Object.freeze
Provide clear, descriptive runtime errors for missing context/props and misuse
Expose dedicated hooks for context access that throw helpful errors when context is missing
Add explicit error messages for: outside-context usage, missing required props, invalid prop combinations, and validation failures
Document each component with JSDoc: purpose, props (required/optional), context requirements, example usage, pitfalls
State management: use React Context for global state, perform correct state updates, and handle side effects appropriately
Validation: implement runtime checks and validate prop combinations with clear errors
Create context with createContext<ContextType|null>(null) and a useContext hook that throws a clear error when missing
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't have unused function parameters
Don't have unused private class members
Don't return a value from a function that has a 'void' return type
Don't use the TypeScript directive @ts-ignore
Ensure switch-case statements are exhaustive (especially with unions)
Don't use TypeScript enums
Don't add type annotations to variables, parameters, and class properties initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types (where discouraged by policy)
Use as const instead of literal types and type annotations
Use either T[] or Array consistently
Use consistent accessibili...
Files:
apps/api/src/realtime/routes.test.tsapps/api/src/ws/router.test.tspackages/core/src/store/seen-store.test.tsapps/api/src/utils/websocket-connection.tsapps/api/src/ws/socket.tsapps/api/src/ws/socket.test.tspackages/core/src/store/messages-store.test.tsapps/api/src/utils/message.tspackages/types/src/realtime-events.ts
**/*.{test.ts,test.tsx,spec.ts,spec.tsx,test.js,test.jsx,spec.js,spec.jsx}
📄 CodeRabbit inference engine (.cursor/rules/global-rules.mdc)
Include tests: unit tests for components, integration tests for composed components, accessibility tests, and error boundary tests
Files:
apps/api/src/realtime/routes.test.tsapps/api/src/ws/router.test.tspackages/core/src/store/seen-store.test.tsapps/api/src/ws/socket.test.tspackages/core/src/store/messages-store.test.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't use theaccessKeyattribute on any HTML element
Use correct ISO language/country codes for the lang attribute
Don't hardcode sensitive data like API keys and tokens
Use === and !==
Files:
apps/api/src/realtime/routes.test.tsapps/api/src/ws/router.test.tspackages/core/src/store/seen-store.test.tsapps/api/src/utils/websocket-connection.tsapps/api/src/ws/socket.tsapps/api/src/ws/socket.test.tspackages/core/src/store/messages-store.test.tsapps/api/src/utils/message.tspackages/types/src/realtime-events.ts
**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,ts}: Don't use consecutive spaces in regular expression literals
Don't use the arguments object
Don't use the comma operator
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Prefer for...of statements instead of Array.forEach
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules that don't change anything
Don't use unnecessary escape sequences in regular expression literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports, exports, and destructured assignments to the same name
Don't use unnecessary string or template literal concatenation
Don't use String.raw in template literals when there are no escape sequences
Don't use useless case statements in switch statements
Avoid ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use void operators
Use Date.now() to get milliseconds since the Unix Epoch
Prefer .flatMap() over map().flat() when possible
Use literal property access instead of computed property access
Don't use parseInt() or Number.parseInt() when numeric literals suffice (binary/octal/hex)
Use concise optional chaining instead of chained logical expressions
Prefer regular expression literals over the RegExp constructor when possible
Don't use non–base-10 or underscore-separated number literal object member names
Remove redundant terms from logical expressions
Prefer while loops over for loops when no initializer/update is needed
Don't reassign const variables
Don't use constant expressions in conditions
Don't use Math.min/Math.max to clamp values when result is constant
Don't use empty character classes in regular expression literals
Don't use empty destructuring patterns
Don't call global object properties as functions
Don't declare functions and vars that are acce...
Files:
apps/api/src/realtime/routes.test.tsapps/api/src/ws/router.test.tspackages/core/src/store/seen-store.test.tsapps/api/src/utils/websocket-connection.tsapps/api/src/ws/socket.tsapps/api/src/ws/socket.test.tspackages/core/src/store/messages-store.test.tsapps/api/src/utils/message.tspackages/types/src/realtime-events.ts
**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Ensure the assertion function (e.g., expect) is placed inside an it() function call
Don't use disabled tests
Files:
apps/api/src/realtime/routes.test.tsapps/api/src/ws/router.test.tspackages/core/src/store/seen-store.test.tsapps/api/src/ws/socket.test.tspackages/core/src/store/messages-store.test.ts
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,ts,tsx}: Don't create classes that only have static members
Don't return a value from a constructor
Don't use super() incorrectly inside classes; ensure super() is called in derived constructors
Ensure super() is called exactly once on every code path in a class constructor before this is accessed if the class has a superclass
Place getters and setters for the same property next to each other in class and object definitions
Ensure JSDoc comment lines start with a single asterisk, except for the first one
Don't use duplicate class members
Files:
apps/api/src/realtime/routes.test.tsapps/api/src/ws/router.test.tspackages/core/src/store/seen-store.test.tsapps/api/src/utils/websocket-connection.tsapps/api/src/ws/socket.tsapps/api/src/ws/socket.test.tspackages/core/src/store/messages-store.test.tsapps/api/src/utils/message.tspackages/types/src/realtime-events.ts
**/*.{js,tsx,ts,jsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Use arrow functions instead of function expressions
Files:
apps/api/src/realtime/routes.test.tsapps/api/src/ws/router.test.tspackages/core/src/store/seen-store.test.tsapps/api/src/utils/websocket-connection.tsapps/api/src/ws/socket.tsapps/api/src/ws/socket.test.tspackages/core/src/store/messages-store.test.tsapps/api/src/utils/message.tspackages/types/src/realtime-events.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,ts,jsx,tsx}: Prevent duplicate polyfills from Polyfill.io
Don't use irregular whitespace characters
Files:
apps/api/src/realtime/routes.test.tsapps/api/src/ws/router.test.tspackages/core/src/store/seen-store.test.tsapps/api/src/utils/websocket-connection.tsapps/api/src/ws/socket.tsapps/api/src/ws/socket.test.tspackages/core/src/store/messages-store.test.tsapps/api/src/utils/message.tspackages/types/src/realtime-events.ts
**/*.{js,ts,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't redeclare variables, functions, classes, and types in the same scope
Files:
apps/api/src/realtime/routes.test.tsapps/api/src/ws/router.test.tspackages/core/src/store/seen-store.test.tsapps/api/src/utils/websocket-connection.tsapps/api/src/ws/socket.tsapps/api/src/ws/socket.test.tspackages/core/src/store/messages-store.test.tsapps/api/src/utils/message.tspackages/types/src/realtime-events.ts
apps/api/src/**/*.test.ts
📄 CodeRabbit inference engine (AGENTS.md)
In API tests, reuse mocks from apps/api/src/utils where possible
Files:
apps/api/src/realtime/routes.test.tsapps/api/src/ws/router.test.tsapps/api/src/ws/socket.test.ts
apps/api/src/ws/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Place WebSocket handlers under apps/api/src/ws
Files:
apps/api/src/ws/router.test.tsapps/api/src/ws/socket.tsapps/api/src/ws/socket.test.ts
apps/api/src/{db,trpc,ws}/**/*.test.ts
📄 CodeRabbit inference engine (AGENTS.md)
Co-locate tests as *.test.ts next to DB, TRPC, and WebSocket source files
Files:
apps/api/src/ws/router.test.tsapps/api/src/ws/socket.test.ts
packages/**
📄 CodeRabbit inference engine (AGENTS.md)
Put shared contracts and utilities in packages/* (e.g., core, types, transactional)
Files:
packages/core/src/store/seen-store.test.tspackages/core/src/store/messages-store.test.tspackages/types/src/realtime-events.ts
packages/types/src/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
packages/types/src/**/*.ts: Keep shared TypeScript types and Zod schemas in packages/types/src/ and update them alongside schema changes
Ensure Zod schemas mirror any API/database shape changes for end-to-end type safety
Files:
packages/types/src/realtime-events.ts
packages/types/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Surface shared DTOs in packages/types
Files:
packages/types/src/realtime-events.ts
🧬 Code graph analysis (6)
apps/api/src/realtime/routes.test.ts (2)
apps/api/src/realtime/routes.ts (2)
isMessageCreatedUserEvent(19-39)matchesVisitorMessageSubscription(41-60)packages/types/src/realtime-events.ts (1)
RealtimeEvent(125-132)
apps/api/src/ws/router.test.ts (1)
packages/types/src/realtime-events.ts (1)
RealtimeEvent(125-132)
packages/core/src/store/seen-store.test.ts (1)
packages/types/src/realtime-events.ts (1)
RealtimeEvent(125-132)
apps/api/src/ws/socket.ts (2)
packages/types/src/realtime-events.ts (1)
RealtimeEvent(125-132)apps/api/src/ws/router.ts (1)
routeEvent(254-280)
apps/api/src/utils/message.ts (3)
apps/api/src/db/schema/conversation.ts (1)
message(167-244)packages/types/src/schemas.ts (1)
Message(25-25)packages/types/src/realtime-events.ts (1)
RealtimeEventData(134-135)
packages/types/src/realtime-events.ts (2)
packages/types/src/schemas.ts (1)
conversationSchema(41-56)packages/types/src/trpc/conversation.ts (1)
conversationHeaderSchema(63-87)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: API Tests
🔇 Additional comments (1)
packages/core/src/store/seen-store.test.ts (1)
97-112: Verify the CONVERSATION_SEEN payload schema
Run the following to confirm which fields theCONVERSATION_SEENz.object actually defines inRealtimeEvents:rg -n 'CONVERSATION_SEEN:' -A12 packages/types/src/realtime-events.ts
| CONVERSATION_TYPING: realtimeEventMetadataSchema.merge( | ||
| z.object({ | ||
| conversationId: z.string(), | ||
| organizationId: z.string(), | ||
| type: z.string(), | ||
| actorUserId: z.string().nullable(), | ||
| actorAiAgentId: z.string().nullable(), | ||
| }), | ||
| }), | ||
| MESSAGE_CREATED: z.object({ | ||
| message: z.object({ | ||
| id: z.string(), | ||
| bodyMd: z.string(), | ||
| type: z.string(), | ||
| userId: z.string().nullable(), | ||
| visitorId: z.string().nullable(), | ||
| organizationId: z.string(), | ||
| websiteId: z.string(), | ||
| aiAgentId: z.string().nullable(), | ||
| isTyping: z.boolean(), | ||
| visitorPreview: z.string().max(2000).nullable().optional(), | ||
| }) | ||
| ), |
There was a problem hiding this comment.
Restore actorType/actorId on typing events
CONVERSATION_TYPING lost the actorType/actorId fields. We still emit them (see apps/api/src/ws/router.test.ts and routing logic), and consumers rely on the discriminator to know whether a user, visitor, or agent is typing. With this schema the validator strips those properties and the inferred type no longer exposes them, so downstream code loses that context. Please add the fields back so validation and typings stay aligned with the emitted data.
CONVERSATION_TYPING: realtimeEventMetadataSchema.merge(
z.object({
conversationId: z.string(),
- userId: z.string().nullable(),
- visitorId: z.string().nullable(),
- aiAgentId: z.string().nullable(),
+ actorType: z.enum(["user", "visitor", "ai_agent"]),
+ actorId: z.string(),
+ userId: z.string().nullable(),
+ visitorId: z.string().nullable(),
+ aiAgentId: z.string().nullable(),
isTyping: z.boolean(),
visitorPreview: z.string().max(2000).nullable().optional(),
})
),🤖 Prompt for AI Agents
In packages/types/src/realtime-events.ts around lines 60 to 69, the
CONVERSATION_TYPING schema removed the actorType/actorId discriminator so
emitted typing events get stripped of who is typing; restore these fields by
adding actorType as a discriminant (e.g. z.enum or z.union of the literal
strings used elsewhere such as 'user', 'visitor', 'aiAgent') and actorId as
z.string().nullable() (matching how user/visitor/agent ids are represented),
making them required on the schema so validation and generated types include the
actor context.
Summary
Testing
https://chatgpt.com/codex/tasks/task_e_68e7952b9b28832b9d6f041c8db8c71a
Summary by CodeRabbit
New Features
Improvements
Tests
Chores