Skip to content

Staging#1043

Merged
MrgSub merged 6 commits intomainfrom
staging
May 22, 2025
Merged

Staging#1043
MrgSub merged 6 commits intomainfrom
staging

Conversation

@MrgSub
Copy link
Collaborator

@MrgSub MrgSub commented May 22, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Reduced the chat message limit for non-pro users from 50 to 20 in the AI chat sidebar.
  • New Features

    • Added support for fetching and providing a default connection ID throughout the app, improving connection management.
    • Introduced a new server-side utility for creating tRPC clients with request header forwarding.
  • Refactor

    • Updated font loading and SVG asset handling in image generation APIs for improved maintainability.
    • Refactored query client and provider logic to use only connection ID, simplifying cache management and removing session dependencies.
    • Updated component and provider signatures to accept and utilize connection ID as a prop.
    • Improved account switching by adding route revalidation after switching accounts.
    • Updated switch component import and class order for consistency.
  • Chores

    • Removed the "Deploy with Vercel" badge from the README.
    • Updated a cache version constant to ensure proper cache management.
    • Removed an unused dependency from the project.

@cloudflare-workers-and-pages
Copy link

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
zero-staging 276e4ac May 22 2025, 08:42 PM

@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 22, 2025

Walkthrough

This update introduces several refactors and feature adjustments. It streamlines font loading in Open Graph API endpoints, updates the cache burst key, and removes the Vercel deploy badge from the README. Query client management is decoupled from session state, relying on connectionId instead. The ServerProviders and QueryProvider components are updated to accept and use connectionId. The usage limit in the AI sidebar is reduced, and import statements are optimized in various components.

Changes

File(s) Change Summary
README.md Removed the "Deploy with Vercel" badge and its link from the README.
apps/mail/app/og-api/create.tsx
apps/mail/app/og-api/home.tsx
Refactored font loading to use a shared loadGoogleFont utility from workers-og with an updated signature; replaced manual SVG asset loading with direct imports in home.tsx.
apps/mail/app/root.tsx Loader now fetches and returns a connectionId using tRPC; Layout component passes connectionId to ServerProviders.
apps/mail/components/ui/ai-sidebar.tsx Reduced the non-pro user chat message limit from 50 to 20 in the usage gauge and tooltip.
apps/mail/components/ui/nav-user.tsx Consolidated import statements; replaced useThreads with useRevalidator; added a revalidate() call after account switching.
apps/mail/components/ui/switch.tsx Changed import source for the switch component; reordered Tailwind CSS class strings for styling.
apps/mail/lib/constants.tsx Updated CACHE_BURST_KEY from 'cache-burst:v0.0.2' to 'cache-burst:v0.0.3'.
apps/mail/lib/trpc.server.ts New module exporting getServerTrpc, a function to create a tRPC client for server-side usage, forwarding request headers and using environment-based backend URL.
apps/mail/package.json Removed @radix-ui/react-switch dependency.
apps/mail/providers/query-provider.tsx Refactored to use only connectionId (not session) for query client creation and cache keys; updated QueryProvider props and logic; added cache trimming for infinite queries on persistence success.
apps/mail/providers/server-providers.tsx Extended ServerProviders to accept and forward a new connectionId prop; removed unused Session import.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Loader (root.tsx)
    participant tRPC Server
    participant ServerProviders
    participant QueryProvider

    Client->>Loader (root.tsx): Request page
    Loader (root.tsx)->>tRPC Server: trpc.connections.getDefault.query()
    tRPC Server-->>Loader (root.tsx): { id: connectionId }
    Loader (root.tsx)-->>Client: { connectionId }
    Client->>ServerProviders: Render with connectionId
    ServerProviders->>QueryProvider: Pass connectionId prop
    QueryProvider->>QueryProvider: Create/get QueryClient for connectionId
Loading

Possibly related PRs

Suggested reviewers

  • ahmetskilinc

Poem

In the garden of code where connections bloom,
A new key whispers, cache swept of gloom.
Fonts load with ease, the sidebar’s trimmed tight,
Switches now sparkle, dependencies light.
The README’s less cluttered, the queries refined—
A rabbit hops forward, leaving bugs behind! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@MrgSub MrgSub merged commit 119ad62 into main May 22, 2025
3 of 4 checks passed
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (1)
apps/mail/providers/query-provider.tsx (1)

82-99: 🛠️ Refactor suggestion

⚠️ Potential issue

Global trpcClient references window, breaking SSR

createTRPCClient is instantiated at module scope, and its
fetch override unconditionally accesses window.location. When this module
is imported during server-side rendering (where window is undefined) the very
first query will throw, crashing the request.

A defensive guard avoids the issue:

-      fetch: (url, options) =>
-        fetch(url, { ...options, credentials: 'include' }).then((res) => {
-          const currentPath = new URL(window.location.href).pathname;
-          const redirectPath = res.headers.get('X-Zero-Redirect');
-          if (!!redirectPath && redirectPath !== currentPath) window.location.href = redirectPath;
-          return res;
-        }),
+      fetch: (url, options) =>
+        fetch(url, { ...options, credentials: 'include' }).then((res) => {
+          if (typeof window !== 'undefined') {
+            const currentPath = new URL(window.location.href).pathname;
+            const redirectPath = res.headers.get('X-Zero-Redirect');
+            if (redirectPath && redirectPath !== currentPath) window.location.href = redirectPath;
+          }
+          return res;
+        }),

Alternatively, instantiate the client lazily in useEffect on the client only.

🧹 Nitpick comments (2)
apps/mail/providers/query-provider.tsx (2)

62-78: Singleton browserQueryClient leaks when multiple connections are opened

browserQueryClient stores a single QueryClient & activeConnectionId.
If a user switches between two connections (A → B → A), the second switch will
reuse the client built for B while the hash function still prefixes keys with
connection A, leading to cache pollution.

Consider caching per connection id instead of mutating a singleton:

-let browserQueryClient = {
-  queryClient: null,
-  activeConnectionId: null,
-} as { queryClient: QueryClient | null; activeConnectionId: string | null };
+const browserQueryClients = new Map<string | null, QueryClient>();

…and update getQueryClient accordingly.

This guarantees isolated caches and avoids subtle cross-connection data
bleeding.


125-142: threadQueryKey misses connectionId component used in custom hash

Because queryKeyHashFn prepends {connectionId} before hashing, look-ups
inside onSuccess should mirror that logic; otherwise the sliced data might not
match the intended queries when multiple connections are active.

-const threadQueryKey = [['mail', 'listThreads'], { type: 'infinite' }];
+const threadQueryKey = [['mail', 'listThreads'], { type: 'infinite' }];
 // (then pass `queryKey` plus meta so the hash function prefixes connectionId)

Better: extract a small helper buildQueryKey(['mail','listThreads']) that
always applies the same hashing rule to avoid divergence.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a863cfd and 276e4ac.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • README.md (0 hunks)
  • apps/mail/app/og-api/create.tsx (2 hunks)
  • apps/mail/app/og-api/home.tsx (1 hunks)
  • apps/mail/app/root.tsx (3 hunks)
  • apps/mail/components/ui/ai-sidebar.tsx (1 hunks)
  • apps/mail/components/ui/nav-user.tsx (3 hunks)
  • apps/mail/components/ui/switch.tsx (2 hunks)
  • apps/mail/lib/constants.tsx (1 hunks)
  • apps/mail/lib/trpc.server.ts (1 hunks)
  • apps/mail/package.json (0 hunks)
  • apps/mail/providers/query-provider.tsx (5 hunks)
  • apps/mail/providers/server-providers.tsx (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/mail/package.json
  • README.md
🧰 Additional context used
🧬 Code Graph Analysis (7)
apps/mail/components/ui/ai-sidebar.tsx (1)
apps/mail/components/ui/gauge.tsx (1)
  • Gauge (1-84)
apps/mail/components/ui/switch.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (53-53)
apps/mail/providers/server-providers.tsx (1)
apps/mail/providers/query-provider.tsx (1)
  • QueryProvider (107-149)
apps/mail/app/og-api/home.tsx (1)
apps/mail/app/og-api/create.tsx (1)
  • loader (4-97)
apps/mail/app/root.tsx (2)
apps/mail/lib/trpc.server.ts (1)
  • getServerTrpc (8-17)
apps/mail/providers/server-providers.tsx (1)
  • ServerProviders (7-20)
apps/mail/lib/trpc.server.ts (1)
apps/server/src/trpc/index.ts (1)
  • AppRouter (31-31)
apps/mail/providers/query-provider.tsx (1)
apps/mail/lib/constants.tsx (1)
  • CACHE_BURST_KEY (13-13)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Workers Builds: zero-staging
  • GitHub Check: Workers Builds: zero-server-staging
🔇 Additional comments (13)
apps/mail/components/ui/switch.tsx (3)

3-3: Package import updated to use consolidated Radix UI package.

The import statement has been updated to use the consolidated radix-ui package instead of the individual @radix-ui/react-switch package. This aligns with the dependency changes in the project and helps standardize the component imports.


14-14: LGTM: CSS class ordering adjustment.

The reordering of Tailwind CSS classes with data-[state=checked]:bg-[#0B84FE] moved to the end maintains the same visual appearance while potentially improving readability.


22-22: LGTM: Improved organization of Tailwind classes.

The class string has been reorganized with better grouping - positioning classes are now together, transform classes are grouped, and theme-specific class (dark:bg-white) is moved to the end. This improves code readability while maintaining the same functionality.

apps/mail/app/og-api/create.tsx (2)

1-1: Good refactoring of font loading functionality

The import now properly leverages the built-in loadGoogleFont function from the workers-og package, eliminating the need for a custom implementation.


20-21: Improved font loading with standardized parameter object

The updated call signature using an object with family and weight properties follows better practices than separate string arguments. This change aligns with the implementation in the home.tsx file, ensuring consistency across OG API endpoints.

apps/mail/app/og-api/home.tsx (3)

1-2: Improved imports with direct asset reference

Good refactoring to:

  1. Import the standard loadGoogleFont utility from workers-og
  2. Use modern asset imports with the ?url suffix for the SVG icon

This eliminates runtime fetching and base64 encoding, which is more efficient.


6-7: Standardized font loading implementation

The updated call signature using an object with family and weight properties matches the implementation in create.tsx, ensuring consistency across OG API endpoints.


14-14: Simplified asset usage

Using the imported SVG URL directly eliminates the need for runtime fetching and base64 encoding, improving performance and reducing potential network failures.

apps/mail/lib/constants.tsx (1)

13-13: Cache burst key incremented to invalidate stale caches.

The version update to v0.0.3 will trigger client cache invalidation, ensuring users get fresh data following the query client refactoring changes.

apps/mail/components/ui/nav-user.tsx (3)

23-23: Consolidated imports from 'react-router'.

The import statement has been cleaned up to group related imports from the same package.


68-68: Using useRevalidator instead of useThreads.

The component now uses the more appropriate useRevalidator hook to access the revalidation function, which aligns with the broader refactoring of connection and session management.


102-103: Added data revalidation step after account switching.

The added revalidate() call ensures that route or query data is refreshed after switching accounts, maintaining data consistency with the new connection context. This complements the cache invalidation strategies implemented elsewhere.

apps/mail/providers/server-providers.tsx (1)

11-12: Decoupled query provider from session state.

The ServerProviders component now accepts a connectionId prop and passes it directly to QueryProvider. This architectural improvement:

  1. Makes the data flow more explicit
  2. Reduces unnecessary recomputation
  3. Aligns with the refactoring in query-provider.tsx to rely solely on connectionId for cache keys

This change is part of a broader effort to simplify cache management and query client behavior across the application.

Also applies to: 16-16

<TooltipTrigger asChild className="md:h-fit md:px-2">
<div>
<Gauge value={50 - chatMessages.remaining!} size="small" showValue={true} />
<Gauge value={20 - chatMessages.remaining!} size="small" showValue={true} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Reduced usage limit for non-pro users.

The maximum allowed chat messages for non-pro users has been reduced from 50 to 20. This change affects both the gauge calculation and the tooltip text.

Note that while this change correctly updates the values in the sidebar, the Gauge component in apps/mail/components/ui/gauge.tsx uses a hardcoded value of 50 for circumference calculations. Consider checking if the gauge visualization will still appear correctly with this new value range:

Also applies to: 157-157


🏁 Script executed:

#!/bin/bash
# Check if Gauge component calculations need updating
cat apps/mail/components/ui/gauge.tsx | grep -n 'value / 50'

Length of output: 126


Gauge max value is still hardcoded; make it dynamic

The Gauge component in apps/mail/components/ui/gauge.tsx still uses a hardcoded 50 for its circumference calculation even though non-pro usage has dropped to 20. We should:

• apps/mail/components/ui/gauge.tsx
– Change
js const valueInCircumference = (value / 50) * circumference;
to
js const valueInCircumference = (value / max) * circumference;
– Add a max prop (defaulting to 50) in the component signature and PropTypes/TS defs.

• apps/mail/components/ui/ai-sidebar.tsx (lines 153 & 157)
– Pass the new max prop to Gauge, e.g.
tsx <Gauge value={20 - chatMessages.remaining!} max={20} size="small" showValue />

This ensures the gauge scales correctly for the new 20-message limit while preserving the old 50-message default elsewhere.

🤖 Prompt for AI Agents
In apps/mail/components/ui/gauge.tsx around the lines where circumference is
calculated, replace the hardcoded divisor 50 with a dynamic max prop by adding a
max prop to the component signature with a default value of 50 and updating the
calculation to use (value / max) * circumference. Then in
apps/mail/components/ui/ai-sidebar.tsx at lines 153 and 157, update the Gauge
component usage to pass max={20} along with the value prop to reflect the new
20-message limit, ensuring the gauge visualization scales correctly.

Comment on lines +41 to +47
const trpc = getServerTrpc(request);

const connectionId = await trpc.connections.getDefault
.query()
.then((res) => res?.id)
.catch(() => null);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Always normalise connectionId to the declared string | null type

res?.id returns undefined when the server responds with no default connection.
Because the loader’s return type specifies connectionId: string | null, returning
undefined breaks the contract and will surface as a type-error or, worse, a
runtime “cannot read … of undefined” further down the tree.

-  const connectionId = await trpc.connections.getDefault
-    .query()
-    .then((res) => res?.id)
-    .catch(() => null);
+  const connectionId = await trpc.connections.getDefault
+    .query()
+    .then((res) => res?.id ?? null)
+    .catch(() => null);

This tiny change guarantees the value is either a string or explicit null.

📝 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.

Suggested change
const trpc = getServerTrpc(request);
const connectionId = await trpc.connections.getDefault
.query()
.then((res) => res?.id)
.catch(() => null);
const trpc = getServerTrpc(request);
const connectionId = await trpc.connections.getDefault
.query()
.then((res) => res?.id ?? null)
.catch(() => null);
🤖 Prompt for AI Agents
In apps/mail/app/root.tsx around lines 41 to 47, the connectionId is assigned
using res?.id which can be undefined if no default connection exists, but the
declared type expects string or null. To fix this, explicitly normalize the
connectionId by returning res?.id if it exists, otherwise null, ensuring the
value always matches the string | null type contract and prevents type errors or
runtime issues.

Comment on lines +1 to +17
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '@zero/server/trpc';
import { env } from 'cloudflare:workers';
import superjson from 'superjson';

const getUrl = () => env.VITE_PUBLIC_BACKEND_URL + '/api/trpc';

export const getServerTrpc = (req: Request) =>
createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: getUrl(),
transformer: superjson,
headers: req.headers,
}),
],
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Mis-placed transformer & fragile header forwarding

  1. In tRPC v10 the transformer must be passed to createTRPCClient, not to
    httpBatchLink. At runtime the current code silently ignores the
    superjson transformer and falls back to JSON.stringify / JSON.parse.

  2. req.headers is a Headers instance; httpBatchLink expects
    HeadersInit | () => HeadersInit. While most runtimes accept the native
    Headers object, Node’s polyfill does not ‑ converting to a plain record
    is safer.

  3. env.VITE_PUBLIC_BACKEND_URL is assumed to be set. If it is missing the
    URL becomes “undefined/api/trpc” – extremely hard to trace in production.

-const getUrl = () => env.VITE_PUBLIC_BACKEND_URL + '/api/trpc';
+const getUrl = () => {
+  if (!env.VITE_PUBLIC_BACKEND_URL)
+    throw new Error('VITE_PUBLIC_BACKEND_URL is not defined in the worker env');
+  return `${env.VITE_PUBLIC_BACKEND_URL}/api/trpc`;
+};

 export const getServerTrpc = (req: Request) =>
-  createTRPCClient<AppRouter>({
-    links: [
-      httpBatchLink({
-        url: getUrl(),
-        transformer: superjson,
-        headers: req.headers,
-      }),
-    ],
-  });
+  createTRPCClient<AppRouter>({
+    transformer: superjson,
+    links: [
+      httpBatchLink({
+        url: getUrl(),
+        headers: () => Object.fromEntries(req.headers),
+      }),
+    ],
+  });

These tweaks restore superjson serialisation, harden header handling, and fail
fast when the backend URL is missing.

📝 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.

Suggested change
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '@zero/server/trpc';
import { env } from 'cloudflare:workers';
import superjson from 'superjson';
const getUrl = () => env.VITE_PUBLIC_BACKEND_URL + '/api/trpc';
export const getServerTrpc = (req: Request) =>
createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: getUrl(),
transformer: superjson,
headers: req.headers,
}),
],
});
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '@zero/server/trpc';
import { env } from 'cloudflare:workers';
import superjson from 'superjson';
const getUrl = () => {
if (!env.VITE_PUBLIC_BACKEND_URL)
throw new Error('VITE_PUBLIC_BACKEND_URL is not defined in the worker env');
return `${env.VITE_PUBLIC_BACKEND_URL}/api/trpc`;
};
export const getServerTrpc = (req: Request) =>
createTRPCClient<AppRouter>({
transformer: superjson,
links: [
httpBatchLink({
url: getUrl(),
headers: () => Object.fromEntries(req.headers),
}),
],
});
🤖 Prompt for AI Agents
In apps/mail/lib/trpc.server.ts lines 1 to 17, move the superjson transformer
from the httpBatchLink options to the createTRPCClient options to ensure it is
applied correctly. Convert req.headers from a Headers instance to a plain object
before passing it to httpBatchLink to avoid compatibility issues with Node
polyfills. Add a check to verify that env.VITE_PUBLIC_BACKEND_URL is defined and
throw an error or fail fast if it is missing to prevent constructing an invalid
URL.

@coderabbitai coderabbitai bot mentioned this pull request Jun 19, 2025
9 tasks
@coderabbitai coderabbitai bot mentioned this pull request Jul 2, 2025
34 tasks
@coderabbitai coderabbitai bot mentioned this pull request Jul 14, 2025
34 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants