Skip to content

98 fix deleting projects for members#106

Merged
InfinityBowman merged 6 commits into
mainfrom
98-fix-deleting-projects-for-members
Dec 19, 2025
Merged

98 fix deleting projects for members#106
InfinityBowman merged 6 commits into
mainfrom
98-fix-deleting-projects-for-members

Conversation

@InfinityBowman
Copy link
Copy Markdown
Owner

@InfinityBowman InfinityBowman commented Dec 19, 2025

Summary by CodeRabbit

  • New Features

    • Notifications for "Removed from Project" and "Project Deleted"
  • Improvements

    • Access‑denied toasts that auto‑navigate to the dashboard
    • Stronger project-local data cleanup when projects are removed or access revoked
    • Hardened connection handling with error throttling and clearer failure handling
    • Duplicate-PDF upload prevention and surfacing of real upload errors
    • Smoother toast animations and CSS performance tweaks
  • Bug Fixes

    • Immediate disconnects for revoked membership and project deletions
  • Documentation

    • Linting guidance updated to prefer using the MCP

✏️ Tip: You can customize this high-level summary in your review settings.

@InfinityBowman InfinityBowman linked an issue Dec 19, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 19, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds server- and client-side handling for project removal/deletion: Durable Object disconnect endpoints and forced WebSocket closes, client access-denied detection that triggers cleanup and redirects, IndexedDB/local-data cleanup APIs, notification dispatch for removed/deleted events, plus PDF duplicate guards and minor UI/toast/style updates.

Changes

Cohort / File(s) Summary
Docs & UI styling
\.github/copilot-instructions.md, packages/ui/src/zag/Toast.jsx
Minor doc guidance update; toast root now applies inline animation-related CSS custom properties and transition/will-change hints.
Notification content & UI handling
packages/web/src/components/NotificationToast.jsx, packages/web/src/components/project-ui/ProjectDashboard.jsx, packages/web/src/components/project-ui/ProjectView.jsx
Added removed-from-project and project-deleted notification branches; dashboard/view now call cleanup and show toasts; ProjectView adds access-denied effect and redirect.
Checklist UI access-denied handling
packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx, packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
Watch connection state for ACCESS_DENIED_ERRORS; show toast and navigate to /dashboard on match; PDF upload error messages surfaced from err.message.
Connection logic & cleanup (useProject)
packages/web/src/primitives/useProject/connection.js, packages/web/src/primitives/useProject/index.js
Added CLOSE_REASONS constants, consecutive-error tracking with throttled logging and onAccessDenied callback; added exported cleanupProjectLocalData(projectId) to destroy Yjs/IndexedDB and clear caches; wired onAccessDenied to invoke cleanup; registered cleanup with projectStore.
Project store stale cleanup & tests
packages/web/src/stores/projectStore.js, packages/web/src/primitives/__tests__/useProject.test.js
Added registerStaleProjectCleanup(callback) API; project-list reconciliation now invokes registered cleanup for cached projects removed from server; test mocks updated to include new mock functions.
Client PDF upload guards
packages/web/src/stores/projectActionsStore/pdfs.js, packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
Pre-upload duplicate filename check (throws if duplicate); surfaced backend error messages on upload failures.
Workers: disconnects, notifications & PDF guard
packages/workers/src/durable-objects/ProjectDoc.js, packages/workers/src/routes/members.js, packages/workers/src/routes/projects.js, packages/workers/src/routes/pdfs.js, packages/workers/src/config/constants.js
Durable Object: added disconnectUser, disconnectAll, handleDisconnectAll and internal /disconnect-all route; member/project deletion flows force disconnects and send internal removed-from-project / project-deleted notifications; projects deletion triggers DO disconnect-all and R2 PDF cleanup; PDF upload route HEAD-check returns 409 if key exists; added FILE_ALREADY_EXISTS error code.

Sequence Diagram(s)

sequenceDiagram
    participant Admin as Admin (actor)
    participant API as API Server (members/projects routes)
    participant ProjectDO as ProjectDoc (Durable Object)
    participant Notify as Internal Notify API
    participant Client as Client App (browser)
    participant Store as useProject/store (IndexedDB/Yjs)

    Admin->>API: DELETE /projects/:id or DELETE /members/:userId
    API->>ProjectDO: POST /internal/disconnect-all or remove-member (force disconnect)
    activate ProjectDO
    ProjectDO->>ProjectDO: disconnectAll / disconnectUser (close ws with reason)
    ProjectDO->>Client: Close WebSocket for affected sessions (reason: project-deleted / membership-revoked)
    deactivate ProjectDO

    API->>Notify: POST internal notify for affected users (type: removed-from-project / project-deleted)
    Notify->>Client: Deliver notification payloads

    Client->>Client: on WebSocket close -> match CLOSE_REASONS / ACCESS_DENIED_ERRORS
    Client->>Store: call cleanupProjectLocalData(projectId)
    activate Store
    Store->>Store: destroy Yjs connection, unregister providers
    Store->>Store: delete IndexedDB (prefixed DB) and clear caches
    Store-->>Client: cleanup complete
    deactivate Store

    Client->>Client: show toast ("Removed from Project" / "Project Deleted"), navigate to /dashboard (replace)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Files/areas needing focused review:
    • packages/web/src/primitives/useProject/connection.js — error counting, throttling, and onAccessDenied invocation.
    • packages/web/src/primitives/useProject/index.js — safe destruction of Yjs providers and IndexedDB deletion edge cases.
    • packages/web/src/stores/projectStore.js — reconciliation and async cleanup invocation semantics.
    • packages/workers/src/durable-objects/ProjectDoc.js — WebSocket close semantics and internal endpoint.
    • packages/workers/src/routes/pdfs.js & packages/workers/src/config/constants.js — HEAD check and error-code mapping.

Possibly related PRs

Poem

🐰 I thumped when sockets closed and projects slipped away,
I scrubbed the IndexedDB and chased the crumbs today.
A toast popped up — "Removed" or "Deleted" — then I hopped along to roam,
I closed the tunnels, cleaned the nest, and guided every rabbit home. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.16% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix deleting projects for members' directly corresponds to the core changes: handling project deletion notifications, cleanup of local data, and member disconnection on project deletion.

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a933dcf and 4eed74e.

📒 Files selected for processing (4)
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (3 hunks)
  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (2 hunks)
  • packages/web/src/components/project-ui/ProjectView.jsx (2 hunks)
  • packages/web/src/constants/errors.js (1 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Dec 19, 2025

Deploying with  Cloudflare Workers  Cloudflare Workers

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

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
corates 4eed74e Commit Preview URL Dec 19 2025, 02:51 PM

Copy link
Copy Markdown

@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: 1

♻️ Duplicate comments (1)
packages/workers/src/routes/members.js (1)

363-391: Verify authUser property availability from Better-Auth.

Similar to the project deletion flow, this notification uses authUser.name || authUser.email at line 383. Ensure that the user object returned by the authentication middleware consistently provides these properties.

Note: This is the same concern raised in packages/workers/src/routes/projects.js line 291. A single verification script there will cover both cases.

🧹 Nitpick comments (7)
packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (1)

37-51: Consolidate duplicated ACCESS_DENIED_ERRORS constant.

The ACCESS_DENIED_ERRORS array is duplicated verbatim in three files:

  • ChecklistYjsWrapper.jsx (lines 38-43)
  • ReconciliationWrapper.jsx (lines 40-45)
  • ProjectView.jsx (lines 62-67)

Extract this to a shared constants file or utility module to maintain consistency and simplify future updates.

Proposed refactor

Create a shared constants file:

// packages/web/src/constants/errors.js
export const ACCESS_DENIED_ERRORS = [
  'This project has been deleted',
  'You have been removed from this project',
  'You are not a member of this project',
  'Unable to connect to project. It may have been deleted or you may not have access.',
];

Then import in each component:

+import { ACCESS_DENIED_ERRORS } from '@/constants/errors.js';

-  const ACCESS_DENIED_ERRORS = [
-    'This project has been deleted',
-    'You have been removed from this project',
-    'You are not a member of this project',
-    'Unable to connect to project. It may have been deleted or you may not have access.',
-  ];
packages/web/src/components/project-ui/ProjectDashboard.jsx (1)

47-63: Async notification handling looks correct.

The handler appropriately awaits cleanupProjectLocalData before showing toasts, ensuring proper sequencing.

Consider adding defensive handling for notification.projectName in case it's undefined:

Optional defensive check
       } else if (notification.type === 'removed-from-project') {
         await cleanupProjectLocalData(notification.projectId);
         showToast.info(
           'Removed from Project',
-          `You were removed from "${notification.projectName}"`,
+          `You were removed from "${notification.projectName || 'a project'}"`,
         );
       } else if (notification.type === 'project-deleted') {
         await cleanupProjectLocalData(notification.projectId);
-        showToast.info('Project Deleted', `"${notification.projectName}" was deleted`);
+        showToast.info('Project Deleted', `"${notification.projectName || 'A project'}" was deleted`);
       }
packages/web/src/components/NotificationToast.jsx (1)

20-35: New notification types are correctly structured.

The content shapes for removed-from-project and project-deleted are appropriate - no action buttons since the project is no longer accessible.

Consider defensive handling for potentially undefined fields:

Optional defensive handling
     if (notification.type === 'removed-from-project') {
       return {
         title: 'Removed from Project',
-        message: `You've been removed from "${notification.projectName}"`,
+        message: `You've been removed from "${notification.projectName || 'a project'}"`,
         action: null,
         actionLabel: null,
       };
     }
     if (notification.type === 'project-deleted') {
       return {
         title: 'Project Deleted',
-        message: `"${notification.projectName}" has been deleted by ${notification.deletedBy}`,
+        message: notification.deletedBy
+          ? `"${notification.projectName || 'A project'}" has been deleted by ${notification.deletedBy}`
+          : `"${notification.projectName || 'A project'}" has been deleted`,
         action: null,
         actionLabel: null,
       };
     }
packages/web/src/primitives/useProject/index.js (1)

26-30: Good practice centralizing the IndexedDB prefix.

The INDEXEDDB_PREFIX constant is correctly defined and matches the usage pattern at line 239. Consider using the constant in the IndexeddbPersistence constructor for consistency:

Suggested consistency fix
     // Set up IndexedDB persistence for offline support
     connectionEntry.indexeddbProvider = new IndexeddbPersistence(
-      `corates-project-${projectId}`,
+      `${INDEXEDDB_PREFIX}${projectId}`,
       ydoc,
     );
packages/web/src/primitives/useProject/connection.js (2)

142-160: The reason.includes('member') check may be too permissive.

The condition reason.includes('member') at line 145 could match unintended strings (e.g., "remember", "membership renewal"). Consider checking for exact matches or more specific substrings:

Suggested refinement
       // Handle membership rejection (1008 = Policy Violation) or generic "not a member"
       if (
         event.code === 1008 ||
-        reason.includes('member') ||
+        reason.includes('not a member') ||
+        reason.includes('not-a-member') ||
         reason === CLOSE_REASONS.NOT_A_MEMBER
       ) {

183-198: Consider adding 'connection-failed' to CLOSE_REASONS for consistency.

The onAccessDenied callback at line 195 uses the string 'connection-failed', which is not defined in CLOSE_REASONS. For consistency and type safety, consider adding it to the constant:

Suggested addition
 export const CLOSE_REASONS = {
   PROJECT_DELETED: 'project-deleted',
   MEMBERSHIP_REVOKED: 'membership-revoked',
   NOT_A_MEMBER: 'not-a-member',
+  CONNECTION_FAILED: 'connection-failed',
 };

Then update line 195:

         if (onAccessDenied) {
-          onAccessDenied({ reason: 'connection-failed' });
+          onAccessDenied({ reason: CLOSE_REASONS.CONNECTION_FAILED });
         }
packages/workers/src/durable-objects/ProjectDoc.js (1)

579-590: Consider using a more semantically appropriate close code.

The implementation uses close code 1000 (Normal Closure) for project deletion, which typically indicates a graceful, expected closure. For a server-initiated forced disconnect due to project deletion, consider:

  • 1001 (Going Away) - Server is going away or resource is being terminated
  • 1008 (Policy Violation) - Consistent with the disconnectUser implementation

Close code 1000 may be intentional to avoid client reconnection attempts, but a more specific code would better communicate the nature of the disconnection.

Proposed change to use close code 1001
   disconnectAll(reason = 'project-deleted') {
-    const closeCode = 1000; // Normal closure
+    const closeCode = 1001; // Going Away
     this.sessions.forEach((sessionData, ws) => {
       if (ws.readyState === 1) {
         ws.close(closeCode, reason);
       }
     });
   }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a7884bf and 52a1d2e.

📒 Files selected for processing (14)
  • .github/copilot-instructions.md (1 hunks)
  • packages/ui/src/zag/Toast.jsx (1 hunks)
  • packages/web/src/components/NotificationToast.jsx (1 hunks)
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (1 hunks)
  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (2 hunks)
  • packages/web/src/components/project-ui/ProjectDashboard.jsx (2 hunks)
  • packages/web/src/components/project-ui/ProjectView.jsx (2 hunks)
  • packages/web/src/primitives/__tests__/useProject.test.js (1 hunks)
  • packages/web/src/primitives/useProject/connection.js (5 hunks)
  • packages/web/src/primitives/useProject/index.js (4 hunks)
  • packages/web/src/stores/projectStore.js (3 hunks)
  • packages/workers/src/durable-objects/ProjectDoc.js (4 hunks)
  • packages/workers/src/routes/members.js (1 hunks)
  • packages/workers/src/routes/projects.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (14)
**/*

📄 CodeRabbit inference engine (.cursorrules)

Do not use emojis in code, comments, documentation, or commit messages

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/workers/src/routes/projects.js
  • packages/web/src/components/NotificationToast.jsx
  • packages/workers/src/routes/members.js
  • packages/web/src/primitives/useProject/connection.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/primitives/__tests__/useProject.test.js
  • packages/ui/src/zag/Toast.jsx
  • packages/workers/src/durable-objects/ProjectDoc.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/stores/projectStore.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.cursorrules)

packages/web/src/**/*.{jsx,tsx,js,ts}: For UI icons, use the solid-icons library or SVGs only. Do not use emojis
Import stores directly where needed instead of passing values through multiple components
When you need to compute a value based on props or state in SolidJS, use createMemo to ensure it updates reactively
For complex state or state objects in SolidJS, use Solid's createStore for better performance and reactivity
You may create reusable logic in 'primitives' (hooks) that can be shared across components to keep components clean and focused on rendering

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/NotificationToast.jsx
  • packages/web/src/primitives/useProject/connection.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/primitives/__tests__/useProject.test.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/stores/projectStore.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
packages/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

packages/**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Keep files small, focused, and modular. If a file exceeds a high number of lines, consider refactoring by extracting sub-modules into a folder with index.jsx and helper components, moving complex logic into separate utility files or primitives, or splitting large forms into section components
Each file should handle one coherent responsibility
Use Zod for schema and input validation

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/workers/src/routes/projects.js
  • packages/web/src/components/NotificationToast.jsx
  • packages/workers/src/routes/members.js
  • packages/web/src/primitives/useProject/connection.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/primitives/__tests__/useProject.test.js
  • packages/ui/src/zag/Toast.jsx
  • packages/workers/src/durable-objects/ProjectDoc.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/stores/projectStore.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/components/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

packages/web/src/components/**/*.{jsx,tsx}: Use responsive design principles for UI components
Use Zag.js for UI components and design system
Zag components exist in packages/web/src/components/zag/* and should be reused; check the README.md in that folder for a list of existing components before adding new components and when debugging
Components should receive at most 1–5 props, and only for local configuration, not shared state
If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context
Components should be lean and focused and should not implement business logic; move business logic into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/NotificationToast.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/**/*.{jsx,tsx,js,ts,css}

📄 CodeRabbit inference engine (.cursorrules)

Ensure browser compatibility for all frontend code (Safari is usually problematic)

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/NotificationToast.jsx
  • packages/web/src/primitives/useProject/connection.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/primitives/__tests__/useProject.test.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/stores/projectStore.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/components/**/*.{jsx,tsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Group related components in subdirectories with an index.js barrel export

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/NotificationToast.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

packages/web/src/**/*.{jsx,tsx}: Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Do not destructure props in SolidJS components as it breaks reactivity. Instead, access props directly from the props object or wrap them in a function to ensure they are always up-to-date

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/NotificationToast.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
**/*.{js,jsx,ts,tsx,json,md,css,html}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use emojis in code, comments, documentation, or commit messages

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/workers/src/routes/projects.js
  • packages/web/src/components/NotificationToast.jsx
  • packages/workers/src/routes/members.js
  • packages/web/src/primitives/useProject/connection.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/primitives/__tests__/useProject.test.js
  • packages/ui/src/zag/Toast.jsx
  • packages/workers/src/durable-objects/ProjectDoc.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/stores/projectStore.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/web/src/**/*.{js,jsx,ts,tsx}: For UI icons, use the solid-icons library or SVGs only. Do not use emojis
Ensure browser compatibility for all frontend code (Safari is usually problematic)
Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Use createMemo to compute values based on props or state to ensure reactive updates
Create reusable logic in primitives (hooks) that can be shared across components to keep components clean and focused on rendering

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/NotificationToast.jsx
  • packages/web/src/primitives/useProject/connection.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/primitives/__tests__/useProject.test.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/stores/projectStore.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,ts,tsx}: Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Keep files small, focused, and modular. Extract sub-modules into a folder with index.jsx and helper components, move complex logic into separate utility files or primitives, and split large forms into section components
Each file should handle one coherent responsibility

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/workers/src/routes/projects.js
  • packages/web/src/components/NotificationToast.jsx
  • packages/workers/src/routes/members.js
  • packages/web/src/primitives/useProject/connection.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/primitives/__tests__/useProject.test.js
  • packages/ui/src/zag/Toast.jsx
  • packages/workers/src/durable-objects/ProjectDoc.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/stores/projectStore.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/components/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/web/src/components/**/*.{js,jsx,ts,tsx}: Use responsive design principles for UI components
Use Zag.js for UI components and design system
Reuse Zag components from packages/web/src/components/zag/* and check the README.md in that folder for existing components before adding new components
Components should receive at most 1–5 props, and only for local configuration, not shared state. If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context
Do not destructure props in SolidJS components. Instead, access props directly from the props object or wrap them in a function to ensure they are always up-to-date
Components should be lean and focused on rendering. Do not implement business logic; move that into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/NotificationToast.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/components/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Group related components in subdirectories with an index.js barrel export

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/NotificationToast.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/ProjectView.jsx
packages/workers/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursorrules)

packages/workers/**/*.{js,ts}: Use Drizzle ORM for database interactions and migrations
Use Better-Auth for authentication and user management

packages/workers/**/*.{js,ts}: Use Zod for schema and input validation
Use Drizzle ORM for database interactions and migrations
Use Better-Auth for authentication and user management

Files:

  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/durable-objects/ProjectDoc.js
packages/web/src/stores/**/*.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Solid's createStore for complex state or state objects to ensure better performance and reactivity

Files:

  • packages/web/src/stores/projectStore.js
🧠 Learnings (9)
📚 Learning: 2025-12-19T12:38:38.880Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T12:38:38.880Z
Learning: Use the Corates MCP tools to explore local documentation sources for Better-Auth, Drizzle, Icons, and Zag documentation

Applied to files:

  • .github/copilot-instructions.md
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Use the Corates MCP for documentation about Better-Auth, Drizzle, Icons, and Zag

Applied to files:

  • .github/copilot-instructions.md
📚 Learning: 2025-12-19T12:38:38.880Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T12:38:38.880Z
Learning: Applies to packages/web/src/components/**/*.{js,jsx,ts,tsx} : Use Zag.js for UI components and design system

Applied to files:

  • .github/copilot-instructions.md
  • packages/ui/src/zag/Toast.jsx
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/components/**/*.{jsx,tsx} : Use Zag.js for UI components and design system

Applied to files:

  • .github/copilot-instructions.md
  • packages/ui/src/zag/Toast.jsx
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/components/**/*.{jsx,tsx} : Zag components exist in `packages/web/src/components/zag/*` and should be reused; check the README.md in that folder for a list of existing components before adding new components and when debugging

Applied to files:

  • .github/copilot-instructions.md
📚 Learning: 2025-12-19T12:38:38.880Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T12:38:38.880Z
Learning: Applies to packages/web/src/components/**/*.{js,jsx,ts,tsx} : Reuse Zag components from `packages/web/src/components/zag/*` and check the README.md in that folder for existing components before adding new components

Applied to files:

  • .github/copilot-instructions.md
📚 Learning: 2025-12-19T12:38:38.880Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T12:38:38.880Z
Learning: Applies to packages/web/src/stores/**/*.{js,ts} : Use Solid's `createStore` for complex state or state objects to ensure better performance and reactivity

Applied to files:

  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx,js,ts} : For complex state or state objects in SolidJS, use Solid's `createStore` for better performance and reactivity

Applied to files:

  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx,js,ts} : When you need to compute a value based on props or state in SolidJS, use `createMemo` to ensure it updates reactively

Applied to files:

  • packages/web/src/primitives/useProject/index.js
🧬 Code graph analysis (6)
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (15)
packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (3)
  • ACCESS_DENIED_ERRORS (38-43)
  • connectionState (35-35)
  • navigate (16-16)
packages/web/src/components/project-ui/ProjectView.jsx (3)
  • ACCESS_DENIED_ERRORS (62-67)
  • connectionState (44-44)
  • navigate (33-33)
packages/web/src/primitives/useProject/index.js (1)
  • connectionState (158-158)
packages/web/src/primitives/useProjectData.js (1)
  • connectionState (30-30)
packages/web/src/components/project-ui/ProjectDashboard.jsx (1)
  • navigate (14-14)
packages/web/src/components/project-ui/reconcile-tab/ReconcileTab.jsx (1)
  • navigate (16-16)
packages/web/src/components/NotFoundPage.jsx (1)
  • navigate (6-6)
packages/web/src/components/Navbar.jsx (1)
  • navigate (11-11)
packages/web/src/components/auth-ui/CompleteProfile.jsx (1)
  • navigate (25-25)
packages/web/src/components/auth-ui/TwoFactorVerify.jsx (1)
  • navigate (13-13)
packages/web/src/components/checklist-ui/ChecklistsDashboard.jsx (1)
  • navigate (10-10)
packages/web/src/components/checklist-ui/CreateLocalChecklist.jsx (1)
  • navigate (15-15)
packages/web/src/components/checklist-ui/LocalChecklistView.jsx (1)
  • navigate (20-20)
packages/web/src/components/sidebar/ProjectTreeItem.jsx (1)
  • navigate (13-13)
packages/web/src/components/sidebar/Sidebar.jsx (1)
  • navigate (18-18)
packages/workers/src/routes/members.js (1)
packages/workers/src/routes/projects.js (17)
  • c (85-85)
  • c (177-177)
  • projectId (48-48)
  • projectId (87-87)
  • projectId (175-175)
  • projectId (226-226)
  • project (245-249)
  • db (49-49)
  • db (84-84)
  • db (176-176)
  • db (227-227)
  • userSessionId (281-281)
  • userSession (282-282)
  • authUser (47-47)
  • authUser (83-83)
  • authUser (174-174)
  • authUser (225-225)
packages/web/src/primitives/useProject/connection.js (1)
packages/web/src/stores/projectStore.js (1)
  • projectStore (538-538)
packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (8)
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (3)
  • ACCESS_DENIED_ERRORS (40-45)
  • connectionState (37-37)
  • navigate (17-17)
packages/web/src/components/project-ui/ProjectView.jsx (3)
  • ACCESS_DENIED_ERRORS (62-67)
  • connectionState (44-44)
  • navigate (33-33)
packages/web/src/primitives/useProject/index.js (1)
  • connectionState (158-158)
packages/web/src/primitives/useProjectData.js (1)
  • connectionState (30-30)
packages/ui/src/zag/Toast.jsx (2)
  • showToast (131-149)
  • showToast (131-149)
packages/web/src/components/Navbar.jsx (1)
  • navigate (11-11)
packages/web/src/components/auth-ui/CheckEmail.jsx (1)
  • navigate (19-19)
packages/web/src/components/checklist-ui/ChecklistsDashboard.jsx (1)
  • navigate (10-10)
packages/web/src/stores/projectStore.js (1)
packages/web/src/components/project-ui/ProjectDashboard.jsx (1)
  • projects (27-27)
packages/web/src/components/project-ui/ProjectDashboard.jsx (2)
packages/web/src/primitives/useProject/index.js (1)
  • cleanupProjectLocalData (97-136)
packages/ui/src/zag/Toast.jsx (2)
  • showToast (131-149)
  • showToast (131-149)
⏰ 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). (2)
  • GitHub Check: Workers Builds: corates
  • GitHub Check: Workers Builds: corates-workers-prod
🔇 Additional comments (21)
.github/copilot-instructions.md (1)

43-43: Documentation guidance addition is clear and consistent.

The appended linting guidance aligns well with the existing MCP-centric approach encouraged throughout the document. This reinforces the unified tooling recommendation and provides helpful direction to Copilot.

packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (1)

39-53: Access-denied redirect logic is correctly implemented.

The pattern matches the implementation in other views. The duplication of ACCESS_DENIED_ERRORS was noted in the previous file review - once consolidated, this file should import from the shared location.

packages/web/src/components/project-ui/ProjectView.jsx (1)

60-77: Access-denied handling is well-documented and correctly implemented.

The comments clearly explain when these errors occur. The implementation is consistent with the other views. Once the ACCESS_DENIED_ERRORS constant is consolidated (as noted earlier), this file should import from the shared location.

packages/web/src/stores/projectStore.js (2)

412-426: Stale project reconciliation logic is well-designed.

The approach correctly:

  1. Compares server project list against local cache
  2. Triggers async cleanup without blocking the fetch
  3. Handles cleanup errors gracefully with logging

The fire-and-forget pattern (onStaleProjectCleanup(cachedId).catch(...)) is appropriate here since cleanup failure shouldn't prevent the user from seeing their project list.


540-547: Callback registration pattern avoids circular dependencies.

The registerStaleProjectCleanup export is a clean solution for injecting the cleanup function from useProject without creating circular imports. The callback is registered at module load time when useProject is first imported, ensuring early initialization and preventing circular dependency issues.

packages/ui/src/zag/Toast.jsx (1)

63-72: LGTM - Animation styling with CSS variables.

The CSS variable bindings for toast animations are well-implemented. The will-change property is appropriately used to hint browser optimization, and the cubic-bezier timing function provides smooth easing. The individual translate CSS property is widely supported as of August 2022, so fallbacks are only necessary if the project specifically targets Safari versions earlier than 14.1.

packages/web/src/primitives/__tests__/useProject.test.js (1)

41-45: LGTM - Mock additions align with new cleanup API.

The added mocks for clearProject, removeProjectFromList, and registerStaleProjectCleanup correctly mirror the new projectStore API surface needed for the stale project cleanup workflow.

packages/web/src/primitives/useProject/index.js (3)

117-131: IndexedDB deletion handles edge cases appropriately.

The onblocked handler correctly logs a warning and continues, which is the right approach since other browser tabs may have open connections. The cleanup flow remains resilient even if IndexedDB deletion fails.


138-140: Module-level registration is acceptable but couples import order.

The registration at module load time works because projectStore is imported before this code executes. This coupling is acceptable given the explicit import, but be aware that refactoring imports could break this.


265-278: LGTM - onAccessDenied handler properly integrates with cleanup workflow.

The async handler correctly awaits cleanupProjectLocalData to ensure all local data is purged when access is denied. This handles removal, deletion, and unauthorized access scenarios consistently.

packages/web/src/primitives/useProject/connection.js (3)

10-18: LGTM - Well-defined close reason constants.

The CLOSE_REASONS export provides type-safe constants that match backend close reasons, enabling consistent handling across the frontend.


86-91: LGTM - Error count reset on successful connection.

Resetting consecutiveErrors when status === 'connected' ensures transient network issues don't accumulate towards the MAX_CONSECUTIVE_ERRORS threshold.


30-38: LGTM - Error tracking initialization and threshold are reasonable.

The consecutiveErrors counter and MAX_CONSECUTIVE_ERRORS = 5 threshold provide a good balance between resilience (allowing temporary network issues) and user experience (not endlessly retrying when access is permanently denied).

packages/workers/src/routes/projects.js (3)

244-255: LGTM! Pre-deletion data fetch is correctly placed.

Fetching the project name and member list before deletion ensures the data is available for notifications after the cascade delete removes them from the database.


257-272: LGTM! Proper error isolation for DO disconnection.

The try/catch ensures that if the Durable Object disconnection fails, the project deletion still proceeds. This is the correct approach since the WebSocket connections will eventually time out anyway.


277-300: No action required. Better-Auth's user schema requires the name field alongside email and emailVerified, so authUser.name is guaranteed to exist. The authUser.name || authUser.email fallback is defensive coding that adds no risk.

packages/workers/src/routes/members.js (1)

358-358: LGTM! Comment accurately reflects the enhanced behavior.

The updated comment now indicates that the sync operation also forces disconnection, which aligns with the disconnectUser call added in ProjectDoc.js line 198.

packages/workers/src/durable-objects/ProjectDoc.js (4)

64-66: LGTM! Internal endpoint registration follows existing patterns.

The disconnect-all endpoint is correctly placed within the internal request handling block and follows the same pattern as other internal endpoints.


197-198: LGTM! Disconnect call properly integrated into member removal flow.

The forced disconnection happens after the membership is removed from the Y.Doc, ensuring the data state is consistent before closing the WebSocket connection.


215-223: LGTM! Clean handler implementation.

The handler properly delegates to the disconnectAll helper and returns an appropriate success response.


565-577: LGTM! Proper WebSocket disconnection with appropriate close code.

The use of close code 1008 (Policy Violation) is appropriate for membership revocation, and the implementation correctly filters sessions by user ID and connection state.

Comment thread packages/web/src/primitives/useProject/index.js
Copy link
Copy Markdown

@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: 1

♻️ Duplicate comments (1)
packages/web/src/primitives/useProject/index.js (1)

97-116: Previous concern addressed: connection state is now reset.

The force-release cleanup path now properly resets the connection state via projectStore.setConnectionState(projectId, { connected: false, synced: false }) on line 115, matching the behavior of the regular releaseConnection function on line 87.

🧹 Nitpick comments (5)
packages/workers/src/routes/pdfs.js (1)

205-207: Minor: Relocate comment for clarity.

The comment "// Store in R2" on line 205 is separated from the actual PUT operation on line 207 by a blank line. Consider placing it directly above line 207 for better readability.

🔎 Suggested adjustment
-    // Store in R2
-
+    // Store in R2
     await c.env.PDF_BUCKET.put(key, pdfData, {
packages/workers/src/routes/projects.js (2)

305-328: Consider parallelizing notifications for better performance.

Notifications are sent sequentially in a loop. Since each notification is independent and errors are already caught individually, sending them in parallel would reduce latency.

Suggested parallel notification approach
     // Send notifications to all members (except the one who deleted)
+    const notificationPromises = members
+      .filter(member => member.userId !== authUser.id)
+      .map(async member => {
+        try {
+          const userSessionId = c.env.USER_SESSION.idFromName(member.userId);
+          const userSession = c.env.USER_SESSION.get(userSessionId);
+          await userSession.fetch(
+            new Request('https://internal/notify', {
+              method: 'POST',
+              headers: { 'Content-Type': 'application/json' },
+              body: JSON.stringify({
+                type: 'project-deleted',
+                projectId,
+                projectName: project?.name || 'Unknown Project',
+                deletedBy: authUser.name || authUser.email,
+                timestamp: Date.now(),
+              }),
+            }),
+          );
+        } catch (err) {
+          console.error('Failed to send deletion notification to user:', member.userId, err);
+        }
+      });
+
+    await Promise.allSettled(notificationPromises);
-    for (const member of members) {
-      if (member.userId !== authUser.id) {
-        try {
-          const userSessionId = c.env.USER_SESSION.idFromName(member.userId);
-          const userSession = c.env.USER_SESSION.get(userSessionId);
-          await userSession.fetch(
-            new Request('https://internal/notify', {
-              method: 'POST',
-              headers: { 'Content-Type': 'application/json' },
-              body: JSON.stringify({
-                type: 'project-deleted',
-                projectId,
-                projectName: project?.name || 'Unknown Project',
-                deletedBy: authUser.name || authUser.email,
-                timestamp: Date.now(),
-              }),
-            }),
-          );
-        } catch (err) {
-          console.error('Failed to send deletion notification to user:', member.userId, err);
-        }
-      }
-    }

280-292: Use batch delete to improve performance.

R2 supports deleting up to 1000 keys in a single call. The current implementation deletes objects individually via Promise.all, which is less efficient than using batch delete.

Replace the individual delete calls with a single batch delete:

       do {
         const listed = await c.env.PDF_BUCKET.list({ prefix, cursor });

         if (listed.objects.length > 0) {
           // Delete objects in batches
           const keysToDelete = listed.objects.map(obj => obj.key);
-          await Promise.all(keysToDelete.map(key => c.env.PDF_BUCKET.delete(key)));
+          await c.env.PDF_BUCKET.delete(keysToDelete);
           deletedCount += keysToDelete.length;
         }

         cursor = listed.truncated ? listed.cursor : undefined;
       } while (cursor);
packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx (2)

39-56: Consider adding client-side file size validation.

While the function correctly validates the file type and handles errors, adding a file size check before upload would improve UX by preventing users from attempting to upload excessively large files.

Optional: Add file size validation
 const handleFileSelect = async e => {
   const file = e.target.files?.[0];
   if (!file || file.type !== 'application/pdf') {
     showToast.error('Invalid File', 'Please select a PDF file');
     return;
   }
+
+  // Optional: Add file size limit (e.g., 50MB)
+  const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB in bytes
+  if (file.size > MAX_FILE_SIZE) {
+    showToast.error('File Too Large', 'Please select a PDF smaller than 50MB');
+    return;
+  }

   setUploading(true);

78-81: TODO: Metadata editing not yet implemented.

The metadata editing functionality is marked as TODO and currently only logs to the console.

Would you like me to help implement the metadata edit modal or open a new issue to track this feature?

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52a1d2e and a933dcf.

📒 Files selected for processing (7)
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (2 hunks)
  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx (1 hunks)
  • packages/web/src/primitives/useProject/index.js (4 hunks)
  • packages/web/src/stores/projectActionsStore/pdfs.js (1 hunks)
  • packages/workers/src/config/constants.js (1 hunks)
  • packages/workers/src/routes/pdfs.js (1 hunks)
  • packages/workers/src/routes/projects.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (14)
**/*

📄 CodeRabbit inference engine (.cursorrules)

Do not use emojis in code, comments, documentation, or commit messages

Files:

  • packages/workers/src/routes/pdfs.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/config/constants.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/stores/projectActionsStore/pdfs.js
packages/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

packages/**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Keep files small, focused, and modular. If a file exceeds a high number of lines, consider refactoring by extracting sub-modules into a folder with index.jsx and helper components, moving complex logic into separate utility files or primitives, or splitting large forms into section components
Each file should handle one coherent responsibility
Use Zod for schema and input validation

Files:

  • packages/workers/src/routes/pdfs.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/config/constants.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/stores/projectActionsStore/pdfs.js
packages/workers/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursorrules)

packages/workers/**/*.{js,ts}: Use Drizzle ORM for database interactions and migrations
Use Better-Auth for authentication and user management

packages/workers/**/*.{js,ts}: Use Zod for schema and input validation
Use Drizzle ORM for database interactions and migrations
Use Better-Auth for authentication and user management

Files:

  • packages/workers/src/routes/pdfs.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/config/constants.js
**/*.{js,jsx,ts,tsx,json,md,css,html}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use emojis in code, comments, documentation, or commit messages

Files:

  • packages/workers/src/routes/pdfs.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/config/constants.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/stores/projectActionsStore/pdfs.js
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,ts,tsx}: Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Keep files small, focused, and modular. Extract sub-modules into a folder with index.jsx and helper components, move complex logic into separate utility files or primitives, and split large forms into section components
Each file should handle one coherent responsibility

Files:

  • packages/workers/src/routes/pdfs.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/config/constants.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/stores/projectActionsStore/pdfs.js
packages/web/src/**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.cursorrules)

packages/web/src/**/*.{jsx,tsx,js,ts}: For UI icons, use the solid-icons library or SVGs only. Do not use emojis
Import stores directly where needed instead of passing values through multiple components
When you need to compute a value based on props or state in SolidJS, use createMemo to ensure it updates reactively
For complex state or state objects in SolidJS, use Solid's createStore for better performance and reactivity
You may create reusable logic in 'primitives' (hooks) that can be shared across components to keep components clean and focused on rendering

Files:

  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/stores/projectActionsStore/pdfs.js
packages/web/src/**/*.{jsx,tsx,js,ts,css}

📄 CodeRabbit inference engine (.cursorrules)

Ensure browser compatibility for all frontend code (Safari is usually problematic)

Files:

  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/stores/projectActionsStore/pdfs.js
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/web/src/**/*.{js,jsx,ts,tsx}: For UI icons, use the solid-icons library or SVGs only. Do not use emojis
Ensure browser compatibility for all frontend code (Safari is usually problematic)
Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Use createMemo to compute values based on props or state to ensure reactive updates
Create reusable logic in primitives (hooks) that can be shared across components to keep components clean and focused on rendering

Files:

  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/stores/projectActionsStore/pdfs.js
packages/web/src/components/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

packages/web/src/components/**/*.{jsx,tsx}: Use responsive design principles for UI components
Use Zag.js for UI components and design system
Zag components exist in packages/web/src/components/zag/* and should be reused; check the README.md in that folder for a list of existing components before adding new components and when debugging
Components should receive at most 1–5 props, and only for local configuration, not shared state
If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context
Components should be lean and focused and should not implement business logic; move business logic into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns

Files:

  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
packages/web/src/components/**/*.{jsx,tsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Group related components in subdirectories with an index.js barrel export

Files:

  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

packages/web/src/**/*.{jsx,tsx}: Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Do not destructure props in SolidJS components as it breaks reactivity. Instead, access props directly from the props object or wrap them in a function to ensure they are always up-to-date

Files:

  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
packages/web/src/components/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/web/src/components/**/*.{js,jsx,ts,tsx}: Use responsive design principles for UI components
Use Zag.js for UI components and design system
Reuse Zag components from packages/web/src/components/zag/* and check the README.md in that folder for existing components before adding new components
Components should receive at most 1–5 props, and only for local configuration, not shared state. If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context
Do not destructure props in SolidJS components. Instead, access props directly from the props object or wrap them in a function to ensure they are always up-to-date
Components should be lean and focused on rendering. Do not implement business logic; move that into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns

Files:

  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
packages/web/src/components/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Group related components in subdirectories with an index.js barrel export

Files:

  • packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
packages/web/src/stores/**/*.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Solid's createStore for complex state or state objects to ensure better performance and reactivity

Files:

  • packages/web/src/stores/projectActionsStore/pdfs.js
🧠 Learnings (3)
📚 Learning: 2025-12-19T12:38:38.880Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T12:38:38.880Z
Learning: Applies to packages/web/src/stores/**/*.{js,ts} : Use Solid's `createStore` for complex state or state objects to ensure better performance and reactivity

Applied to files:

  • packages/web/src/primitives/useProject/index.js
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx,js,ts} : For complex state or state objects in SolidJS, use Solid's `createStore` for better performance and reactivity

Applied to files:

  • packages/web/src/primitives/useProject/index.js
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx,js,ts} : When you need to compute a value based on props or state in SolidJS, use `createMemo` to ensure it updates reactively

Applied to files:

  • packages/web/src/primitives/useProject/index.js
🧬 Code graph analysis (2)
packages/web/src/primitives/useProject/index.js (2)
packages/web/src/stores/projectStore.js (2)
  • projectStore (538-538)
  • registerStaleProjectCleanup (545-547)
packages/web/src/primitives/useProject/connection.js (1)
  • createConnectionManager (30-270)
packages/web/src/stores/projectActionsStore/pdfs.js (2)
packages/web/src/stores/projectActionsStore/index.js (2)
  • study (112-112)
  • pdf (114-114)
packages/workers/src/routes/pdfs.js (6)
  • projectId (25-25)
  • projectId (68-68)
  • projectId (96-96)
  • projectId (237-237)
  • projectId (285-285)
  • file (126-126)
⏰ 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). (2)
  • GitHub Check: Workers Builds: corates
  • GitHub Check: Workers Builds: corates-workers-prod
🔇 Additional comments (12)
packages/web/src/stores/projectActionsStore/pdfs.js (1)

87-92: Client-side duplicate check improves UX.

The pre-upload duplicate check provides immediate user feedback before initiating the upload. The error message is clear and actionable.

Note: A race condition exists between this check and the actual upload (another user could upload a file with the same name in between), but this is acceptable since the server-side check at the workers endpoint is authoritative and will catch any duplicates.

packages/workers/src/routes/pdfs.js (1)

192-203: Server-side duplicate check prevents file overwrites.

The HEAD request before storage ensures existing files are not silently overwritten. The error response is consistent with the client-side check and provides clear guidance.

The additional R2 HEAD operation adds minimal latency and is necessary for data integrity.

packages/workers/src/config/constants.js (1)

102-102: LGTM!

The new FILE_ALREADY_EXISTS error code follows the established taxonomy (5xxx for file errors) and provides a clear, consistent error message for duplicate file scenarios.

packages/workers/src/routes/projects.js (2)

244-255: LGTM! Fetching project metadata and members before deletion.

The approach of retrieving project name and member list before deletion is correct for supporting notifications. The queries are efficient and use proper Drizzle ORM patterns.


257-272: LGTM! Graceful DO disconnect with proper error handling.

The try-catch ensures that DO disconnect failures don't block the deletion flow. This is the correct approach since the project will be deleted anyway.

packages/web/src/primitives/useProject/index.js (5)

26-30: LGTM! IndexedDB prefix is consistent.

The INDEXEDDB_PREFIX constant (corates-project-) correctly matches the prefix used when creating IndexeddbPersistence on line 240 (corates-project-${projectId}). This ensures the cleanup function deletes the correct database.

Also applies to: 239-242


118-132: LGTM! Robust IndexedDB deletion with proper blockers handling.

The onblocked handler correctly logs a warning and continues with resolve() rather than blocking indefinitely. This is important because blocked deletions can occur when other tabs have open connections, and the cleanup should proceed gracefully.


139-141: LGTM! Registration at module load time.

Registering the cleanup callback at module load avoids circular dependency issues while ensuring the projectStore can trigger cleanup when detecting stale projects. The comment clearly explains the rationale.


274-278: LGTM! onAccessDenied triggers complete cleanup.

The async callback properly awaits cleanupProjectLocalData to ensure all local data is removed when access is denied. This handles project deletion, membership revocation, and unauthorized access scenarios comprehensively.


134-137: Both clearProject and removeProjectFromList methods are defined in projectStore and properly exported. No issues found.

packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx (1)

51-51: The error handling in this code is appropriate and follows standard patterns. The uploadPdf API function in pdf-api.js already sanitizes errors by attempting to parse the response JSON and falling back to a generic message if parsing fails. The backend is responsible for providing user-friendly error messages in the error.error field. The change on line 51 to surface err.message with a fallback is good UX practice—it displays specific backend-provided errors while falling back to a generic message if none is available. No sensitive information is exposed in the current implementation.

packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (1)

191-191: Improved error messaging.

Using err.message provides users with specific error details instead of a generic message, making it easier to diagnose upload issues. The fallback ensures a message is always shown.

Comment thread packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
@InfinityBowman InfinityBowman merged commit be8bf45 into main Dec 19, 2025
0 of 3 checks passed
@InfinityBowman InfinityBowman deleted the 98-fix-deleting-projects-for-members branch December 19, 2025 14:49
This was referenced Dec 19, 2025
This was linked to issues Dec 20, 2025
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.

Fix deleting projects for members Navigating to non-existent project User can sign out then go back

1 participant