Skip to content

119 project add member without account#174

Merged
InfinityBowman merged 16 commits into
mainfrom
119-project---add-member-without-account
Dec 27, 2025
Merged

119 project add member without account#174
InfinityBowman merged 16 commits into
mainfrom
119-project---add-member-without-account

Conversation

@InfinityBowman
Copy link
Copy Markdown
Owner

@InfinityBowman InfinityBowman commented Dec 27, 2025

Summary by CodeRabbit

  • New Features
    • Email-based invitations with magic-link emails, server-side accept endpoint, and automatic acceptance after profile completion (redirects to invited project).
  • UX
    • In-app "Add Member" supports inviting by email with role selection and contextual messaging.
    • Simplified "No projects yet" screen with centered CTA.
  • Documentation
    • New invitation guides and OpenAPI updates.
  • Tests
    • Improved test resiliency for Durable Objects and DO-related cleanup.
  • Chores
    • Migration/tooling updates and input sanitization for emails.

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

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

coderabbitai Bot commented Dec 27, 2025

📝 Walkthrough

Walkthrough

Adds a project invitation system: frontend captures invitation tokens and auto‑accepts after profile completion; members can invite by email; workers store invitations, generate magic links, send invitation emails, validate/accept tokens, create memberships, and sync/notify via Durable Objects. Migrations, docs, tests, and utilities updated accordingly.

Changes

Cohort / File(s) Summary
Auth UI — token capture & auto-accept
packages/web/src/components/auth-ui/SignUp.jsx, packages/web/src/components/auth-ui/CompleteProfile.jsx
Persist ?invitation=TOKEN to localStorage on signup; CompleteProfile reads token on submit, POSTs /api/invitations/accept, clears token, and redirects to invited project on success or dashboard otherwise.
Add Member UI — email invites
packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
Support inviting by email when no user match: client email validation, submit { email, role }, handle API-created/updated invitation, queue email send, show toast; updated UI states and labels.
Invitations API & members flow
packages/workers/src/routes/invitations.js, packages/workers/src/routes/members.js, packages/workers/src/index.js
New /api/invitations/accept route (POST) validating token/email, marking accepted or creating membership; members route can create/update email invitations, generate tokens, queue emails, and use sync helpers.
Email service & templates
packages/workers/src/auth/email.js, packages/workers/src/auth/emailTemplates.js
Added sendProjectInvitation and templates getProjectInvitationEmailHtml / getProjectInvitationEmailText; templates use escapeHtml. Email service exports new API.
DB schema, validation, migrations & snapshot
packages/workers/src/db/schema.js, packages/workers/src/config/validation.js, packages/workers/migrations/*, packages/workers/migrations/meta/*
New project_invitations table and validation schema for accept token; added migration SQL, snapshot, and journal entries to include invitations.
DO sync helpers & usage
packages/workers/src/lib/project-sync.js, packages/workers/src/routes/projects.js, packages/workers/src/routes/members.js, packages/workers/src/routes/admin/users.js, packages/workers/src/routes/users.js
New syncMemberToDO / syncProjectToDO helpers; routes call these to notify ProjectDoc DOs. Some flows wrap DO sync in try/catch; user deletion requires DO removals to succeed before DB deletes.
Utilities & small refactors
packages/workers/src/lib/escapeHtml.js, packages/workers/src/routes/contact.js, packages/shared/src/errors/domains/domain.ts
Added escapeHtml util and replaced inline usage; added INVITATION_ALREADY_ACCEPTED project error code.
Frontend overview & synced prop propagation
packages/web/src/components/project-ui/overview-tab/ChartSection.jsx, packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx, packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
Added synced signal prop and wired it through OverviewTab to ChartSection and AMSTAR2ResultsTable to trigger memos when Y.Doc sync state changes.
Project dashboard UI
packages/web/src/components/project-ui/ProjectDashboard.jsx
Simplified "No projects yet" fallback: centered text and optionally centered create button; removed ContactPrompt and nested Show logic.
Wrangler / config / scripts / docs
packages/workers/wrangler.jsonc, packages/workers/package.json, packages/workers/scripts/*, .clauderc, .github/copilot-instructions.md, .cursor/rules/corates.mdc, packages/docs/guides/*
Added APP_URL env var, switched prod DB setup to D1 migrations apply, adapted scripts to read/apply multiple migrations, updated agent/guidance docs, and added invitation docs to API/auth guides.
Tests & test helpers
packages/workers/src/__tests__/*, packages/workers/src/__tests__/helpers.js, packages/workers/src/durable-objects/__tests__/EmailQueue.test.js, packages/workers/src/routes/__tests__/*
Improved test DB reset (foreign key handling), added clearProjectDOs, added DO retry helper for tests, adjusted tests to expect invitation creation and to clear DOs between runs.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Browser as Browser (localStorage)
    participant SignUp as SignUp Page
    participant Complete as CompleteProfile Page
    participant API as Workers API (/api/invitations)
    participant DB as Database
    participant DO as UserSession DO
    participant Toast as UI Toast

    User->>SignUp: Visit signup with ?invitation=TOKEN
    SignUp->>Browser: Store token (pendingInvitationToken)
    User->>Complete: Finish profile
    Complete->>Browser: Read pendingInvitationToken

    alt token present
        Complete->>API: POST /invitations/accept { token }
        API->>DB: Validate token & invitation record
        alt valid & not accepted
            API->>DB: Create project_member, mark invitation accepted
            API->>DO: Notify user session
            API-->>Complete: { success, projectId, projectName }
            Complete->>Toast: Show success
            Complete->>Browser: Clear pendingInvitationToken
            Complete->>User: Redirect to project
        else invalid/expired/already accepted
            API-->>Complete: { error / alreadyMember }
            Complete->>Browser: Clear pendingInvitationToken
            Complete->>User: Redirect to dashboard
        end
    else no token
        Complete->>User: Redirect to dashboard
    end
Loading
sequenceDiagram
    participant TeamMember as Team Member
    participant Modal as AddMemberModal
    participant API as Workers Members API
    participant DB as Database
    participant Email as Email Service / Queue
    participant Invitee as Invitee (email)

    TeamMember->>Modal: Enter email + role
    Modal->>Modal: Validate email
    Modal->>API: POST /projects/{id}/members { email, role }
    API->>DB: Check existing invitation for project+email
    alt existing pending invitation
        API->>DB: Update role & extend expiry
        API-->>Modal: Return updated invitation
    else create new invitation
        API->>DB: Insert invitation (id, token, expiresAt)
        API->>DB: Fetch project & inviter names
        API->>Email: Queue sendProjectInvitation (magic link)
        API-->>Modal: Return invitation metadata (201)
    end
    Modal->>Modal: Close & show success toast
    Email->>Invitee: Deliver invitation email with magic link
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I found a token by the door,

I tucked it in the local store,
I stitched a link with carrot cheer,
Sent a magic hop for friends to near,
Now projects bloom — hip hop hooray!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.58% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title "119 project add member without account" is partially related to the changeset, referring to adding members without existing accounts via email invitations, but uses unclear spacing and formatting that reduces clarity. Clarify the title by removing extra spaces and using more descriptive phrasing, such as 'Add project members via email invitations' or 'Enable email-based project member invitations'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 119-project---add-member-without-account

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 27, 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 113a20c Commit Preview URL Dec 27 2025, 05:49 AM

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: 5

🧹 Nitpick comments (4)
packages/web/src/components/auth-ui/CompleteProfile.jsx (1)

136-172: Add error handling for JSON parsing.

The invitation acceptance logic is well-structured and correctly uses handleFetchError. However, Line 154's await response.json() could throw if the response isn't valid JSON, which would bypass the catch block at Line 166.

Suggested improvement
         const response = await handleFetchError(
           fetch(`${API_BASE}/api/invitations/accept`, {
             method: 'POST',
             credentials: 'include',
             headers: {
               'Content-Type': 'application/json',
             },
             body: JSON.stringify({ token: invitationToken }),
           }),
           {
             showToast: false,
           },
         );

-        const result = await response.json();
+        let result;
+        try {
+          result = await response.json();
+        } catch (parseErr) {
+          console.error('Failed to parse invitation response:', parseErr);
+          localStorage.removeItem('pendingInvitationToken');
+          // Continue to dashboard
+          await new Promise(resolve => setTimeout(resolve, 200));
+          navigate('/dashboard', { replace: true });
+          return;
+        }
+
         localStorage.removeItem('pendingInvitationToken');

         // Redirect to the project
         if (result.projectId) {
           showToast.success(
             'Invitation Accepted',
             `You've been added to "${result.projectName}"`,
           );
           navigate(`/projects/${result.projectId}`, { replace: true });
           return;
         }
packages/workers/src/routes/members.js (1)

266-299: Consider using the sendProjectInvitation function from the email service.

The email service (packages/workers/src/auth/email.js) already exposes a sendProjectInvitation function that handles template generation and the dev/production environment guards. This code duplicates that logic and uses a different email sending mechanism (direct queue vs. the service's sendEmail).

Using the shared service would reduce duplication and ensure consistent behavior across the codebase.

packages/workers/src/db/schema.js (1)

138-153: Schema definition is correct.

The projectInvitations table design properly supports the invitation workflow with appropriate constraints (unique token, cascading foreign keys).

Consider adding a composite index on (projectId, email) to optimize the lookup in members.js (lines 187-192) that queries by both fields. This is a nice-to-have for query performance as the table grows.

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

25-43: syncMemberToDO is duplicated from members.js.

This helper function is identical to the one in packages/workers/src/routes/members.js (lines 29-47). Consider extracting it to a shared utility module to avoid duplication and ensure consistent behavior.

Example shared module location
// packages/workers/src/utils/durable-objects.js
export async function syncMemberToDO(env, projectId, action, memberData) {
  try {
    const doId = env.PROJECT_DOC.idFromName(projectId);
    const projectDoc = env.PROJECT_DOC.get(doId);

    await projectDoc.fetch(
      new Request('https://internal/sync-member', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Internal-Request': 'true',
        },
        body: JSON.stringify({ action, member: memberData }),
      }),
    );
  } catch (err) {
    console.error('Failed to sync member to DO:', err);
  }
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f8dad5 and c61fac7.

📒 Files selected for processing (16)
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/workers/migrations/0000_acoustic_power_pack.sql
  • packages/workers/migrations/0001_init.sql
  • packages/workers/migrations/meta/0000_snapshot.json
  • packages/workers/migrations/meta/_journal.json
  • packages/workers/src/auth/email.js
  • packages/workers/src/auth/emailTemplates.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/db/schema.js
  • packages/workers/src/index.js
  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/members.js
  • packages/workers/wrangler.jsonc
💤 Files with no reviewable changes (1)
  • packages/workers/migrations/0001_init.sql
🧰 Additional context used
📓 Path-based instructions (26)
**/packages/workers/**/*.{js,ts}

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

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

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/db/schema.js
  • packages/workers/src/index.js
  • packages/workers/src/auth/emailTemplates.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
packages/workers/src/routes/**/*.js

📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)

packages/workers/src/routes/**/*.js: Always use validateRequest middleware for request body validation with validation schemas
Always use validateQueryParams middleware for query parameter validation with Zod schemas
Always create database client from environment using createDb function, never inline database connections
Use db.batch() for related database operations that must be atomic - operations must succeed or fail together
Always use Drizzle ORM with query builders (eq, and, count, etc.) - never use raw SQL
Always use createDomainError from @corates/shared with error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS) - never create error objects manually or throw strings
Wrap database operations in try-catch blocks and create domain errors for database failures using SYSTEM_ERRORS.DB_ERROR
Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler
Use requireAuth middleware to apply authentication to routes and access authenticated user via getAuth(c).user
Extract validated request body data from context using c.get('validatedBody') after using validateRequest middleware
Extract validated query parameters from context using c.get('validatedQuery') after using validateQueryParams middleware

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/members.js
**/*

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

NEVER use emojis anywhere in code, comments, documentation, plan files, commit messages, or examples. Do not use unicode symbols or emojis. For UI icons, use solid-icons library or SVGs only

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/db/schema.js
  • packages/workers/src/index.js
  • packages/workers/wrangler.jsonc
  • packages/workers/migrations/meta/_journal.json
  • packages/workers/src/auth/emailTemplates.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/workers/src/auth/email.js
  • packages/workers/migrations/0000_acoustic_power_pack.sql
  • packages/workers/migrations/meta/0000_snapshot.json
  • packages/workers/src/routes/members.js
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Comments should explain WHY something is being done or provide context, not narrate what the code does. Reserve comments for non-obvious intent, workarounds, external system context, and edge cases

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/db/schema.js
  • packages/workers/src/index.js
  • packages/workers/src/auth/emailTemplates.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
packages/workers/src/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

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

Always use db.batch() for multiple related database operations to ensure atomicity in Drizzle ORM

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/db/schema.js
  • packages/workers/src/index.js
  • packages/workers/src/auth/emailTemplates.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Always use createDomainError from @corates/shared for backend error handling in workers, with predefined error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)
Wrap database operations in try-catch blocks and return domain errors using createDomainError(SYSTEM_ERRORS.DB_ERROR, ...) with operation metadata
Use validation middleware with validateRequest(schema) for request validation; do not manually validate in routes

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/db/schema.js
  • packages/workers/src/index.js
  • packages/workers/src/auth/emailTemplates.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Never throw string literals; always throw Error objects or return domain errors from API routes
Use isErrorCode utility from @corates/shared or @/lib/error-utils.js to check for specific error codes instead of manual error comparisons

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/db/schema.js
  • packages/workers/src/index.js
  • packages/workers/src/auth/emailTemplates.js
  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
packages/workers/src/routes/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)

packages/workers/src/routes/**/*.{js,ts}: Always validate request bodies using validateRequest middleware from the validation config
Use validateQueryParams middleware for query parameter validation in addition to request body validation

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/members.js
packages/workers/src/config/validation.js

📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)

Add new validation schemas to src/config/validation.js and reuse commonFields when possible

Files:

  • packages/workers/src/config/validation.js
packages/workers/src/config/validation.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)

Add new Zod schemas to config/validation.js and reuse commonFields when possible

Files:

  • packages/workers/src/config/validation.js
**/packages/web/src/**/*.{jsx,tsx,js,ts}

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

**/packages/web/src/**/*.{jsx,tsx,js,ts}: For UI icons, use the solid-icons library or SVGs only. Do not use emojis.
Use createMemo to compute values based on props or state to ensure reactive updates in SolidJS components
Use Solid's createStore for complex state or state objects instead of createSignal for better performance and reactivity

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
**/packages/web/src/**/*.{jsx,tsx}

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

**/packages/web/src/**/*.{jsx,tsx}: Use Ark UI components from @corates/ui package for UI components - import from '@corates/ui', not from local components
Do NOT use Zag.js components directly - use Ark UI components from @corates/ui instead
Do NOT destructure props in SolidJS components - access props directly or wrap in a function to maintain reactivity

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{js,jsx,ts,tsx}: Use import aliases from jsconfig.json as specified in ui-components.mdc
Ensure browser compatibility, particularly with Safari

packages/web/src/**/*.{js,jsx,ts,tsx}: Always use uploadPdf from @api/pdf-api.js for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
Use cachePdf and getCachedPdf from @primitives/pdfCache.js for caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
Use importFromGoogleDrive from @api/google-drive.js for importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state using saveFormState from @/lib/formStatePersistence.js before triggering OAuth redirects for Google Drive
Use addPdfToStudy operation from useProject hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
Use removePdfFromStudy operation from useProject hook to remove PDFs from a study
Use pdfPreviewStore from @/stores/pdfPreviewStore.js for managing PDF preview state (openPreview, closePreview, getPreview methods)
Use downloadPdf from @api/pdf-api.js to download PDFs from server, then cache the result using cachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations from useProject hook instead of bypassing through direct API calls

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,ui}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Group related components in subdirectories with barrel exports

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,landing}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use Ark UI components from @corates/ui package, not local component imports

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,landing,ui}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use solid-icons library for icons (e.g., solid-icons/bi, solid-icons/fi)

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{jsx,tsx}: Do NOT prop-drill application state in SolidJS. Import stores directly where needed from packages/web/src/stores/
Do NOT destructure props in SolidJS components. Access props.field directly or wrap in function: () => props.field
Move business logic to stores, utilities, or primitives rather than keeping it in components in SolidJS
Components should receive at most 1-5 props (local config only, not shared state). Shared state lives in external stores under packages/web/src/stores/

Use PdfViewer component from @/components/checklist-ui/pdf/PdfViewer.jsx for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use createMemo for derived values in SolidJS

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Always use handleFetchError from @/lib/error-utils.js for frontend fetch calls with optional showToast parameter
Use createFormErrorSignals from @/lib/form-errors.js for handling form validation errors, field-level errors, and global errors in frontend forms

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

packages/web/src/**/*.{js,jsx}: Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Only save serializable data to IndexedDB—exclude File objects, ArrayBuffers, functions, and other non-serializable objects from form state persistence
Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
For temporary File object storage during OAuth flows (e.g., pending PDFs), use projectStore.setPendingProjectData() instead of IndexedDB, as File objects cannot be serialized
Add restore parameters to the URL after OAuth redirects in the format '?restore=&projectId=' to signal form state restoration on mount
Clear URL restore parameters after form state restoration by calling clearRestoreParamsFromUrl() to prevent stale restoration attempts on subsequent navigation
Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from @/lib/formStatePersistence.js

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/components/project-ui/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

Form state should include serializable metadata when handling files (name, size, type) and use the store's pendingPdfs pattern for actual File objects that persist across redirects

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS components

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,ui}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

packages/{web,ui}/**/*.{js,jsx,ts,tsx}: Import UI components from '@corates/ui' package instead of local components directories
Use solid-icons library for icons instead of emojis or other icon sources
Use Tailwind CSS classes for styling UI components
Apply responsive design using mobile-first approach with Tailwind CSS

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

Use import aliases from jsconfig.json (e.g., @/, @components/, @auth-ui/, @checklist-ui/, @project-ui/, @routes/, @primitives/, @api/, @config/, @lib/) instead of relative paths

Files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/workers/migrations/*.sql

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

All database migrations go in a single file: packages/workers/migrations/0001_init.sql. Do NOT create separate migration files. Edit the existing 0001_init.sql file directly

Files:

  • packages/workers/migrations/0000_acoustic_power_pack.sql
🧠 Learnings (31)
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Better-Auth for authentication and user management

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/index.js
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Better-Auth for authentication and user management

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/index.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/config/validation.js : Add new validation schemas to `src/config/validation.js` and reuse `commonFields` when possible

Applied to files:

  • packages/workers/src/config/validation.js
📚 Learning: 2025-12-27T03:02:38.199Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2025-12-27T03:02:38.199Z
Learning: Applies to packages/workers/src/config/validation.{js,ts} : Add new Zod schemas to `config/validation.js` and reuse `commonFields` when possible

Applied to files:

  • packages/workers/src/config/validation.js
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Use validation middleware with `validateRequest(schema)` for request validation; do not manually validate in routes

Applied to files:

  • packages/workers/src/config/validation.js
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Zod for schema and input validation on the backend

Applied to files:

  • packages/workers/src/config/validation.js
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Zod for schema and input validation on the backend

Applied to files:

  • packages/workers/src/config/validation.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always use `validateRequest` middleware for request body validation with validation schemas

Applied to files:

  • packages/workers/src/config/validation.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Check user membership in project before allowing WebSocket connection in ProjectDoc

Applied to files:

  • packages/workers/src/index.js
  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Verify authentication before WebSocket upgrade in ProjectDoc

Applied to files:

  • packages/workers/src/index.js
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/migrations/*.sql : All database migrations go in a single file: `packages/workers/migrations/0001_init.sql`. Do NOT create separate migration files. Edit the existing 0001_init.sql file directly

Applied to files:

  • packages/workers/migrations/meta/_journal.json
  • packages/workers/migrations/0000_acoustic_power_pack.sql
  • packages/workers/migrations/meta/0000_snapshot.json
📚 Learning: 2025-12-27T03:01:06.918Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.918Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js,packages/web/src/primitives/useProject/checklists.js : Use checklist operations from useProject hook (createChecklist, updateChecklistAnswer, getChecklistData) instead of manually updating checklist structure

Applied to files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Solid's Show component for conditional rendering instead of ternary operators

Applied to files:

  • packages/web/src/components/project-ui/ProjectDashboard.jsx
  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createSignal for simple reactive values in SolidJS components

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/web/src/**/*.{jsx,tsx,js,ts} : Use Solid's `createStore` for complex state or state objects instead of createSignal for better performance and reactivity

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Prefer derived state with createMemo or signals over effects whenever possible

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createStore for complex objects and arrays that need granular reactivity in SolidJS

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Always clean up SolidJS effects that create subscriptions or timers using onCleanup

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Use `createFormErrorSignals` from `@/lib/form-errors.js` for handling form validation errors, field-level errors, and global errors in frontend forms

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Solid's For component for rendering lists instead of Array.map()

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx
📚 Learning: 2025-12-27T03:01:54.715Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.715Z
Learning: Applies to packages/web/src/**/*.{js,jsx} : Add restore parameters to the URL after OAuth redirects in the format '?restore=<formType>&projectId=<projectId>' to signal form state restoration on mount

Applied to files:

  • packages/web/src/components/auth-ui/SignUp.jsx
📚 Learning: 2025-12-27T03:01:54.715Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.715Z
Learning: Applies to packages/web/src/**/*.{js,jsx} : Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()

Applied to files:

  • packages/web/src/components/auth-ui/SignUp.jsx
📚 Learning: 2025-12-27T03:02:05.939Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.939Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Save form state using `saveFormState` from `@/lib/formStatePersistence.js` before triggering OAuth redirects for Google Drive

Applied to files:

  • packages/web/src/components/auth-ui/SignUp.jsx
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Always use `handleFetchError` from `@/lib/error-utils.js` for frontend fetch calls with optional `showToast` parameter

Applied to files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/migrations/0001_init.sql : All database migrations should go in a single file: `packages/workers/migrations/0001_init.sql` - do NOT create separate migration files

Applied to files:

  • packages/workers/migrations/0000_acoustic_power_pack.sql
  • packages/workers/migrations/meta/0000_snapshot.json
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Drizzle ORM for database interactions and migrations

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Drizzle ORM for ALL database interactions and migrations

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always create database client from environment using `createDb` function, never inline database connections

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always use Drizzle ORM with query builders (eq, and, count, etc.) - never use raw SQL

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Use `requireAuth` middleware to apply authentication to routes and access authenticated user via `getAuth(c).user`

Applied to files:

  • packages/workers/src/routes/members.js
🧬 Code graph analysis (4)
packages/workers/src/index.js (1)
packages/workers/src/routes/invitations.js (1)
  • invitationRoutes (20-20)
packages/web/src/components/auth-ui/CompleteProfile.jsx (1)
packages/web/src/lib/error-utils.js (2)
  • response (52-52)
  • handleFetchError (50-84)
packages/workers/src/auth/email.js (1)
packages/workers/src/auth/emailTemplates.js (2)
  • getProjectInvitationEmailHtml (196-242)
  • getProjectInvitationEmailText (244-271)
packages/workers/src/routes/members.js (4)
packages/workers/src/middleware/auth.js (1)
  • getAuth (63-68)
packages/workers/src/db/schema.js (6)
  • projectInvitations (139-153)
  • projectInvitations (139-153)
  • projects (73-82)
  • projects (73-82)
  • user (5-24)
  • user (5-24)
packages/workers/src/auth/emailTemplates.js (2)
  • getProjectInvitationEmailHtml (196-242)
  • getProjectInvitationEmailText (244-271)
packages/workers/src/routes/email.js (1)
  • queue (39-39)
⏰ 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 (13)
packages/web/src/components/auth-ui/SignUp.jsx (1)

40-45: Invitation token capture looks good.

The implementation correctly captures the invitation token from the URL and stores it in localStorage for later use during profile completion. The token is properly cleaned up in CompleteProfile.jsx after use.

Note: While localStorage is vulnerable to XSS attacks, this is acceptable here since:

  • The token is single-use and short-lived (7 days per the PR description)
  • The token gets validated server-side during acceptance
  • The token is removed immediately after processing
packages/web/src/components/project-ui/ProjectDashboard.jsx (1)

204-214: Improved empty state UI.

The simplified empty state provides a clearer call-to-action for users with project creation permissions. The conditional rendering with canCreateProject() ensures users without quota see the ContactPrompt in the header instead.

packages/web/src/components/project-ui/overview-tab/AddMemberModal.jsx (3)

85-143: Well-implemented email invitation validation.

The email validation logic and canAddByEmail predicate are well-structured:

  • Basic email regex is appropriate for client-side validation
  • Server-side validation provides final authority
  • Clear separation between user selection and email invitation paths

98-125: Excellent conditional payload handling.

The request payload correctly branches between user-based and email-based invitations, with appropriate response handling for each case. The toast notification distinguishes between direct member addition and invitation sending.


241-256: User-friendly invitation UI.

The UI changes provide clear feedback to users:

  • Informative prompt when inviting by email
  • Appropriate button states with loading indicators
  • Conditional role selector visibility

The implementation follows SolidJS best practices by using the Show component for conditional rendering.

Also applies to: 288-288, 322-331

packages/workers/src/index.js (1)

30-30: Invitation routes properly integrated.

The invitation routes are correctly imported and mounted at /api/invitations, following the same pattern as other API routes in the application.

Also applies to: 195-196

packages/workers/wrangler.jsonc (1)

58-58: APP_URL configuration added correctly.

The APP_URL environment variable is properly configured for both development and production environments, which is necessary for generating invitation links in email templates.

Development: http://localhost:5173 (Vite dev server)
Production: https://corates.org

Also applies to: 102-102

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

78-85: LGTM!

The invitationSchemas addition follows the established patterns in this file, uses Zod for validation per coding guidelines, and provides clear error messaging.

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

177-314: Invitation flow implementation looks good overall.

The logic correctly handles the three cases: resend existing pending invitation, reject already-accepted invitation, and create new invitation. The email queue failure is appropriately non-fatal. The token generation using crypto.randomUUID() provides sufficient entropy.

packages/workers/migrations/meta/0000_snapshot.json (1)

197-296: Migration snapshot for project_invitations looks correct.

The table definition properly includes:

  • Unique index on token for secure token lookup
  • Foreign key constraints with cascading deletes to projects and user
  • Required fields (expiresAt) and optional fields (acceptedAt) aligned with the Drizzle schema
packages/workers/src/auth/email.js (1)

106-119: LGTM!

The sendProjectInvitation function follows the established patterns in this file, including the development environment guard with URL logging and proper template usage.

packages/workers/src/auth/emailTemplates.js (1)

244-271: LGTM!

The plain text email template is well-structured and handles all role types appropriately.

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

49-223: Invitation acceptance flow is well-implemented.

The route correctly handles:

  • Token validation and lookup
  • Expiration checking
  • Prevention of double-acceptance
  • Email matching for security (case-insensitive)
  • Edge case where user is already a member
  • Proper error responses using domain errors

The middleware ordering (requireAuth -> validateRequest) follows the coding guidelines.

Comment on lines +1 to +137
CREATE TABLE `account` (
`id` text PRIMARY KEY NOT NULL,
`accountId` text NOT NULL,
`providerId` text NOT NULL,
`userId` text NOT NULL,
`accessToken` text,
`refreshToken` text,
`idToken` text,
`accessTokenExpiresAt` integer,
`refreshTokenExpiresAt` integer,
`scope` text,
`password` text,
`createdAt` integer DEFAULT (unixepoch()),
`updatedAt` integer DEFAULT (unixepoch()),
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `mediaFiles` (
`id` text PRIMARY KEY NOT NULL,
`filename` text NOT NULL,
`originalName` text,
`fileType` text,
`fileSize` integer,
`uploadedBy` text,
`bucketKey` text NOT NULL,
`createdAt` integer DEFAULT (unixepoch()),
FOREIGN KEY (`uploadedBy`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `project_invitations` (
`id` text PRIMARY KEY NOT NULL,
`projectId` text NOT NULL,
`email` text NOT NULL,
`role` text DEFAULT 'member',
`token` text NOT NULL,
`invitedBy` text NOT NULL,
`expiresAt` integer NOT NULL,
`acceptedAt` integer,
`createdAt` integer DEFAULT (unixepoch()),
FOREIGN KEY (`projectId`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`invitedBy`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `project_invitations_token_unique` ON `project_invitations` (`token`);--> statement-breakpoint
CREATE TABLE `project_members` (
`id` text PRIMARY KEY NOT NULL,
`projectId` text NOT NULL,
`userId` text NOT NULL,
`role` text DEFAULT 'member',
`joinedAt` integer DEFAULT (unixepoch()),
FOREIGN KEY (`projectId`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `projects` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`description` text,
`createdBy` text NOT NULL,
`createdAt` integer DEFAULT (unixepoch()),
`updatedAt` integer DEFAULT (unixepoch()),
FOREIGN KEY (`createdBy`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `session` (
`id` text PRIMARY KEY NOT NULL,
`expiresAt` integer NOT NULL,
`token` text NOT NULL,
`createdAt` integer DEFAULT (unixepoch()),
`updatedAt` integer DEFAULT (unixepoch()),
`ipAddress` text,
`userAgent` text,
`userId` text NOT NULL,
`impersonatedBy` text,
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`impersonatedBy`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE UNIQUE INDEX `session_token_unique` ON `session` (`token`);--> statement-breakpoint
CREATE TABLE `subscriptions` (
`id` text PRIMARY KEY NOT NULL,
`userId` text NOT NULL,
`stripeCustomerId` text,
`stripeSubscriptionId` text,
`tier` text DEFAULT 'free' NOT NULL,
`status` text DEFAULT 'active' NOT NULL,
`currentPeriodStart` integer,
`currentPeriodEnd` integer,
`cancelAtPeriodEnd` integer DEFAULT false,
`createdAt` integer DEFAULT (unixepoch()),
`updatedAt` integer DEFAULT (unixepoch()),
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `subscriptions_userId_unique` ON `subscriptions` (`userId`);--> statement-breakpoint
CREATE UNIQUE INDEX `subscriptions_stripeCustomerId_unique` ON `subscriptions` (`stripeCustomerId`);--> statement-breakpoint
CREATE UNIQUE INDEX `subscriptions_stripeSubscriptionId_unique` ON `subscriptions` (`stripeSubscriptionId`);--> statement-breakpoint
CREATE TABLE `twoFactor` (
`id` text PRIMARY KEY NOT NULL,
`userId` text NOT NULL,
`secret` text NOT NULL,
`backupCodes` text NOT NULL,
`createdAt` integer DEFAULT (unixepoch()),
`updatedAt` integer DEFAULT (unixepoch()),
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `user` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`email` text NOT NULL,
`emailVerified` integer DEFAULT false,
`image` text,
`createdAt` integer DEFAULT (unixepoch()),
`updatedAt` integer DEFAULT (unixepoch()),
`username` text,
`displayName` text,
`avatarUrl` text,
`role` text,
`persona` text,
`profileCompletedAt` integer,
`twoFactorEnabled` integer DEFAULT false,
`banned` integer DEFAULT false,
`banReason` text,
`banExpires` integer
);
--> statement-breakpoint
CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);--> statement-breakpoint
CREATE UNIQUE INDEX `user_username_unique` ON `user` (`username`);--> statement-breakpoint
CREATE TABLE `verification` (
`id` text PRIMARY KEY NOT NULL,
`identifier` text NOT NULL,
`value` text NOT NULL,
`expiresAt` integer NOT NULL,
`createdAt` integer DEFAULT (unixepoch()),
`updatedAt` integer DEFAULT (unixepoch())
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Migration file violates project guidelines - consolidate into 0001_init.sql.

This PR creates a new migration file 0000_acoustic_power_pack.sql, which violates the project's migration policy.

As per coding guidelines: "All database migrations go in a single file: packages/workers/migrations/0001_init.sql. Do NOT create separate migration files. Edit the existing 0001_init.sql file directly."

Please consolidate this migration into the existing packages/workers/migrations/0001_init.sql file and remove this separate migration file.

Based on learnings, all migrations should be in 0001_init.sql.

🤖 Prompt for AI Agents
In packages/workers/migrations/0000_acoustic_power_pack.sql lines 1-137 you
introduced a separate migration file which violates the single-migration-file
policy; move every CREATE TABLE / CREATE INDEX statement from this file into
packages/workers/migrations/0001_init.sql (append them in appropriate schema
order, preserving statement boundaries and constraints, ensuring no duplicate
definitions or index name collisions), then remove 0000_acoustic_power_pack.sql
from the repo; after merging, run the project's migration lint/tests to confirm
the consolidated 0001_init.sql is valid.

Comment on lines +1 to +13
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1766805635744,
"tag": "0000_acoustic_power_pack",
"breakpoints": true
}
]
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Migration journal violates project guidelines.

This migration journal entry references the separate migration file 0000_acoustic_power_pack, which violates the project's migration policy.

As noted in the review of 0000_acoustic_power_pack.sql, all migrations should be consolidated into packages/workers/migrations/0001_init.sql. This journal file should also be updated accordingly or removed if the migrations are consolidated into the existing 0001_init.sql structure.

Based on learnings, all migrations should be in 0001_init.sql.

🤖 Prompt for AI Agents
In packages/workers/migrations/meta/_journal.json around lines 1-13, the journal
contains an entry referencing a separate migration tag
"0000_acoustic_power_pack" which violates the project policy requiring all
migrations to be consolidated into packages/workers/migrations/0001_init.sql;
remove the offending entry (or remove the entire _journal.json if journals are
not used) and ensure the journal (if kept) only reflects migrations present in
0001_init.sql (update version/entries/timestamps accordingly) so no external
migration files are referenced.

Comment thread packages/workers/src/auth/emailTemplates.js
Comment thread packages/workers/src/routes/invitations.js Outdated
Comment thread packages/workers/src/routes/members.js Outdated
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: 4

🧹 Nitpick comments (6)
packages/workers/src/routes/contact.js (1)

181-181: Move import statement to the top of the file with other imports.

While ES modules hoist imports, placing this import after the code that uses it (lines 140-154) is unconventional and reduces readability. Standard practice is to group all imports at the top.

Proposed fix
 import { Hono } from 'hono';
 import { Client as PostmarkClient } from 'postmark';
 import { z } from 'zod';
+import { escapeHtml } from '../lib/escapeHtml.js';
 import { contactRateLimit } from '../middleware/rateLimit.js';
 import {
   createDomainError,
   createValidationError,
   VALIDATION_ERRORS,
   SYSTEM_ERRORS,
 } from '@corates/shared';

Then remove the import from line 181.

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

73-79: Use a more semantically appropriate error code for invalid invitation tokens.

VALIDATION_ERRORS.FIELD_INVALID_FORMAT is intended for general field validation issues (e.g., malformed email addresses). An invalid or non-existent invitation token represents a domain-specific error related to the invitation flow.

Consider using a dedicated invitation error constant

If an INVITATION_ERRORS.INVALID_TOKEN or similar constant exists in the shared error definitions, use it here for clearer error semantics:

     if (!invitation) {
-      const error = createDomainError(VALIDATION_ERRORS.FIELD_INVALID_FORMAT, {
-        field: 'token',
-        value: token,
+      const error = createDomainError(INVITATION_ERRORS.INVALID_TOKEN, {
+        token,
       });
       return c.json(error, error.statusCode);
     }

If the constant doesn't exist, consider adding it to @corates/shared error definitions.


81-90: Use a domain-specific error code for expired invitations.

Similar to invalid tokens, expired invitations are a domain concept, not a field validation issue. Using VALIDATION_ERRORS.FIELD_INVALID_FORMAT with value: 'expired' conflates validation errors with business logic errors.

Consider using a dedicated expiration error constant
     if (now > expiresAt) {
-      const error = createDomainError(VALIDATION_ERRORS.FIELD_INVALID_FORMAT, {
-        field: 'token',
-        value: 'expired',
+      const error = createDomainError(INVITATION_ERRORS.EXPIRED, {
+        expiresAt: invitation.expiresAt,
       });
       return c.json(error, error.statusCode);
     }

92-98: Semantic mismatch: "already accepted" is not "already a member".

PROJECT_ERRORS.MEMBER_ALREADY_EXISTS indicates the user is already a project member, but here the invitation was simply accepted (possibly by a different mechanism or as part of cleanup). The distinction matters for client-side handling and error messaging.

Use a more precise error code
     if (invitation.acceptedAt) {
-      const error = createDomainError(PROJECT_ERRORS.MEMBER_ALREADY_EXISTS, {
+      const error = createDomainError(INVITATION_ERRORS.ALREADY_ACCEPTED, {
         projectId: invitation.projectId,
+        acceptedAt: invitation.acceptedAt,
       });
       return c.json(error, error.statusCode);
     }
packages/workers/src/routes/members.js (2)

272-275: Centralize AUTH_SECRET retrieval logic.

The fallback pattern c.env.AUTH_SECRET || c.env.SECRET appears here and likely elsewhere. Consider extracting to a shared utility to ensure consistency across the codebase.

Create a shared helper

In a shared auth utilities file (e.g., packages/workers/src/auth/utils.js):

export function getAuthSecret(env) {
  const authSecret = env.AUTH_SECRET || env.SECRET;
  if (!authSecret) {
    throw new Error('AUTH_SECRET must be configured');
  }
  return authSecret;
}

Then use it here:

-        const authSecret = c.env.AUTH_SECRET || c.env.SECRET;
-        if (!authSecret) {
-          throw new Error('AUTH_SECRET must be configured');
-        }
+        const { getAuthSecret } = await import('../auth/utils.js');
+        const authSecret = getAuthSecret(c.env);

177-377: Consider atomic handling of invitation creation and email queueing.

Currently, if the invitation is created successfully but email queueing fails (lines 363-366), the invitation exists in the database but the user never receives the email. While the code logs the error and continues, there's no retry mechanism or notification to the inviter.

Consider one of these approaches:

  1. Return a partial success indicator to the client (e.g., emailSent: false) so the UI can offer to resend
  2. Use a more robust email queue with retry logic
  3. Store email send status in the invitation record for tracking and manual intervention

This is currently acceptable for a non-production system but should be addressed before launch.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c61fac7 and 99c382e.

📒 Files selected for processing (12)
  • .clauderc
  • .cursor/rules/corates.mdc
  • .github/copilot-instructions.md
  • packages/shared/src/errors/domains/domain.ts
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/web/src/components/auth-ui/SignUp.jsx
  • packages/workers/src/auth/email.js
  • packages/workers/src/auth/emailTemplates.js
  • packages/workers/src/lib/escapeHtml.js
  • packages/workers/src/routes/contact.js
  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/members.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/web/src/components/auth-ui/SignUp.jsx
🧰 Additional context used
📓 Path-based instructions (22)
**/packages/workers/**/*.{js,ts}

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

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

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/escapeHtml.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/contact.js
  • packages/workers/src/auth/emailTemplates.js
packages/workers/src/routes/**/*.js

📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)

packages/workers/src/routes/**/*.js: Always use validateRequest middleware for request body validation with validation schemas
Always use validateQueryParams middleware for query parameter validation with Zod schemas
Always create database client from environment using createDb function, never inline database connections
Use db.batch() for related database operations that must be atomic - operations must succeed or fail together
Always use Drizzle ORM with query builders (eq, and, count, etc.) - never use raw SQL
Always use createDomainError from @corates/shared with error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS) - never create error objects manually or throw strings
Wrap database operations in try-catch blocks and create domain errors for database failures using SYSTEM_ERRORS.DB_ERROR
Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler
Use requireAuth middleware to apply authentication to routes and access authenticated user via getAuth(c).user
Extract validated request body data from context using c.get('validatedBody') after using validateRequest middleware
Extract validated query parameters from context using c.get('validatedQuery') after using validateQueryParams middleware

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/contact.js
**/*

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

NEVER use emojis anywhere in code, comments, documentation, plan files, commit messages, or examples. Do not use unicode symbols or emojis. For UI icons, use solid-icons library or SVGs only

Files:

  • packages/workers/src/routes/invitations.js
  • packages/shared/src/errors/domains/domain.ts
  • packages/workers/src/lib/escapeHtml.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/workers/src/routes/contact.js
  • packages/workers/src/auth/emailTemplates.js
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Comments should explain WHY something is being done or provide context, not narrate what the code does. Reserve comments for non-obvious intent, workarounds, external system context, and edge cases

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/workers/src/routes/invitations.js
  • packages/shared/src/errors/domains/domain.ts
  • packages/workers/src/lib/escapeHtml.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/workers/src/routes/contact.js
  • packages/workers/src/auth/emailTemplates.js
packages/workers/src/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

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

Always use db.batch() for multiple related database operations to ensure atomicity in Drizzle ORM

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/escapeHtml.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/contact.js
  • packages/workers/src/auth/emailTemplates.js
packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Always use createDomainError from @corates/shared for backend error handling in workers, with predefined error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)
Wrap database operations in try-catch blocks and return domain errors using createDomainError(SYSTEM_ERRORS.DB_ERROR, ...) with operation metadata
Use validation middleware with validateRequest(schema) for request validation; do not manually validate in routes

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/escapeHtml.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/contact.js
  • packages/workers/src/auth/emailTemplates.js
packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Never throw string literals; always throw Error objects or return domain errors from API routes
Use isErrorCode utility from @corates/shared or @/lib/error-utils.js to check for specific error codes instead of manual error comparisons

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/escapeHtml.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/workers/src/routes/contact.js
  • packages/workers/src/auth/emailTemplates.js
packages/workers/src/routes/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)

packages/workers/src/routes/**/*.{js,ts}: Always validate request bodies using validateRequest middleware from the validation config
Use validateQueryParams middleware for query parameter validation in addition to request body validation

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/contact.js
**/packages/web/src/**/*.{jsx,tsx,js,ts}

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

**/packages/web/src/**/*.{jsx,tsx,js,ts}: For UI icons, use the solid-icons library or SVGs only. Do not use emojis.
Use createMemo to compute values based on props or state to ensure reactive updates in SolidJS components
Use Solid's createStore for complex state or state objects instead of createSignal for better performance and reactivity

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
**/packages/web/src/**/*.{jsx,tsx}

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

**/packages/web/src/**/*.{jsx,tsx}: Use Ark UI components from @corates/ui package for UI components - import from '@corates/ui', not from local components
Do NOT use Zag.js components directly - use Ark UI components from @corates/ui instead
Do NOT destructure props in SolidJS components - access props directly or wrap in a function to maintain reactivity

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{js,jsx,ts,tsx}: Use import aliases from jsconfig.json as specified in ui-components.mdc
Ensure browser compatibility, particularly with Safari

packages/web/src/**/*.{js,jsx,ts,tsx}: Always use uploadPdf from @api/pdf-api.js for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
Use cachePdf and getCachedPdf from @primitives/pdfCache.js for caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
Use importFromGoogleDrive from @api/google-drive.js for importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state using saveFormState from @/lib/formStatePersistence.js before triggering OAuth redirects for Google Drive
Use addPdfToStudy operation from useProject hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
Use removePdfFromStudy operation from useProject hook to remove PDFs from a study
Use pdfPreviewStore from @/stores/pdfPreviewStore.js for managing PDF preview state (openPreview, closePreview, getPreview methods)
Use downloadPdf from @api/pdf-api.js to download PDFs from server, then cache the result using cachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations from useProject hook instead of bypassing through direct API calls

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,ui}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Group related components in subdirectories with barrel exports

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,landing}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use Ark UI components from @corates/ui package, not local component imports

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,landing,ui}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use solid-icons library for icons (e.g., solid-icons/bi, solid-icons/fi)

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{jsx,tsx}: Do NOT prop-drill application state in SolidJS. Import stores directly where needed from packages/web/src/stores/
Do NOT destructure props in SolidJS components. Access props.field directly or wrap in function: () => props.field
Move business logic to stores, utilities, or primitives rather than keeping it in components in SolidJS
Components should receive at most 1-5 props (local config only, not shared state). Shared state lives in external stores under packages/web/src/stores/

Use PdfViewer component from @/components/checklist-ui/pdf/PdfViewer.jsx for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use createMemo for derived values in SolidJS

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Always use handleFetchError from @/lib/error-utils.js for frontend fetch calls with optional showToast parameter
Use createFormErrorSignals from @/lib/form-errors.js for handling form validation errors, field-level errors, and global errors in frontend forms

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

packages/web/src/**/*.{js,jsx}: Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Only save serializable data to IndexedDB—exclude File objects, ArrayBuffers, functions, and other non-serializable objects from form state persistence
Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
For temporary File object storage during OAuth flows (e.g., pending PDFs), use projectStore.setPendingProjectData() instead of IndexedDB, as File objects cannot be serialized
Add restore parameters to the URL after OAuth redirects in the format '?restore=&projectId=' to signal form state restoration on mount
Clear URL restore parameters after form state restoration by calling clearRestoreParamsFromUrl() to prevent stale restoration attempts on subsequent navigation
Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from @/lib/formStatePersistence.js

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS components

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,ui}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

packages/{web,ui}/**/*.{js,jsx,ts,tsx}: Import UI components from '@corates/ui' package instead of local components directories
Use solid-icons library for icons instead of emojis or other icon sources
Use Tailwind CSS classes for styling UI components
Apply responsive design using mobile-first approach with Tailwind CSS

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

Use import aliases from jsconfig.json (e.g., @/, @components/, @auth-ui/, @checklist-ui/, @project-ui/, @routes/, @primitives/, @api/, @config/, @lib/) instead of relative paths

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
🧠 Learnings (36)
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Better-Auth for authentication and user management

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Better-Auth for authentication and user management

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/auth/email.js
  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Use y-websocket protocol with message type 1 for awareness (presence) messages in ProjectDoc

Applied to files:

  • packages/workers/src/routes/invitations.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Verify authentication before WebSocket upgrade in ProjectDoc

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/auth/email.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Check user membership in project before allowing WebSocket connection in ProjectDoc

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Handle WebSocket upgrade requests by checking for 'Upgrade: websocket' header in ProjectDoc fetch method

Applied to files:

  • packages/workers/src/routes/invitations.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Implement sync endpoints (/sync, /sync-member, /sync-pdf, /disconnect-all) for internal Durable Object requests

Applied to files:

  • packages/workers/src/routes/invitations.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Use `db.batch()` for related database operations that must be atomic - operations must succeed or fail together

Applied to files:

  • packages/workers/src/routes/invitations.js
📚 Learning: 2025-12-27T03:02:38.199Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2025-12-27T03:02:38.199Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Always use `db.batch()` for multiple related database operations to ensure atomicity in Drizzle ORM

Applied to files:

  • packages/workers/src/routes/invitations.js
  • .cursor/rules/corates.mdc
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always use `createDomainError` from `corates/shared` with error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS) - never create error objects manually or throw strings

Applied to files:

  • packages/shared/src/errors/domains/domain.ts
  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Always use `createDomainError` from `corates/shared` for backend error handling in workers, with predefined error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)

Applied to files:

  • packages/shared/src/errors/domains/domain.ts
  • .clauderc
  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Follow standard JavaScript/SolidJS/Cloudflare best practices

Applied to files:

  • .clauderc
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities

Applied to files:

  • .clauderc
  • .cursor/rules/corates.mdc
  • .github/copilot-instructions.md
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : Move business logic to stores, utilities, or primitives rather than keeping it in components in SolidJS

Applied to files:

  • .clauderc
  • .cursor/rules/corates.mdc
  • .github/copilot-instructions.md
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Use `isErrorCode` utility from `corates/shared` or `@/lib/error-utils.js` to check for specific error codes instead of manual error comparisons

Applied to files:

  • .clauderc
  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Zod for schema and input validation on the backend

Applied to files:

  • .clauderc
  • .cursor/rules/corates.mdc
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components

Applied to files:

  • .clauderc
  • .github/copilot-instructions.md
📚 Learning: 2025-12-27T03:01:06.918Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.918Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js : Maintain separate answer format implementations for each checklist type (AMSTAR2, ROBINS-I, Generic) to prevent data corruption

Applied to files:

  • .clauderc
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : Do NOT prop-drill application state in SolidJS. Import stores directly where needed from `packages/web/src/stores/`

Applied to files:

  • .clauderc
  • .cursor/rules/corates.mdc
  • .github/copilot-instructions.md
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity

Applied to files:

  • .clauderc
  • .github/copilot-instructions.md
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Never throw string literals; always throw Error objects or return domain errors from API routes

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Wrap database operations in try-catch blocks and create domain errors for database failures using `SYSTEM_ERRORS.DB_ERROR`

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Wrap database operations in try-catch blocks and return domain errors using `createDomainError(SYSTEM_ERRORS.DB_ERROR, ...)` with operation metadata

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Drizzle ORM for database interactions and migrations

Applied to files:

  • packages/workers/src/routes/members.js
  • .cursor/rules/corates.mdc
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Drizzle ORM for ALL database interactions and migrations

Applied to files:

  • packages/workers/src/routes/members.js
  • .cursor/rules/corates.mdc
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always create database client from environment using `createDb` function, never inline database connections

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always use Drizzle ORM with query builders (eq, and, count, etc.) - never use raw SQL

Applied to files:

  • packages/workers/src/routes/members.js
  • .cursor/rules/corates.mdc
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Use `requireAuth` middleware to apply authentication to routes and access authenticated user via `getAuth(c).user`

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Always use `handleFetchError` from `@/lib/error-utils.js` for frontend fetch calls with optional `showToast` parameter

Applied to files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/migrations/*.sql : All database migrations go in a single file: `packages/workers/migrations/0001_init.sql`. Do NOT create separate migration files. Edit the existing 0001_init.sql file directly

Applied to files:

  • .cursor/rules/corates.mdc
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/migrations/0001_init.sql : All database migrations should go in a single file: `packages/workers/migrations/0001_init.sql` - do NOT create separate migration files

Applied to files:

  • .cursor/rules/corates.mdc
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Keep files small, focused, and modular - if a file exceeds a high number of lines, consider refactoring into a folder with index.jsx and helper components, or extract complex logic into separate utility files

Applied to files:

  • .github/copilot-instructions.md
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling

Applied to files:

  • .github/copilot-instructions.md
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : Components should receive at most 1-5 props (local config only, not shared state). Shared state lives in external stores under `packages/web/src/stores/`

Applied to files:

  • .github/copilot-instructions.md
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

Applied to files:

  • .github/copilot-instructions.md
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/web/src/**/*.{jsx,tsx} : Do NOT destructure props in SolidJS components - access props directly or wrap in a function to maintain reactivity

Applied to files:

  • .github/copilot-instructions.md
🧬 Code graph analysis (3)
packages/workers/src/auth/email.js (4)
packages/workers/src/routes/members.js (3)
  • projectName (329-329)
  • inviterName (330-330)
  • safeProjectName (346-346)
packages/workers/src/routes/contact.js (1)
  • env (56-56)
packages/workers/src/lib/escapeHtml.js (1)
  • escapeHtml (5-15)
packages/workers/src/auth/emailTemplates.js (2)
  • getProjectInvitationEmailHtml (198-244)
  • getProjectInvitationEmailText (246-273)
packages/web/src/components/auth-ui/CompleteProfile.jsx (1)
packages/web/src/lib/error-utils.js (2)
  • response (52-52)
  • handleFetchError (50-84)
packages/workers/src/auth/emailTemplates.js (2)
packages/workers/src/lib/escapeHtml.js (1)
  • escapeHtml (5-15)
packages/workers/src/routes/members.js (2)
  • inviterName (330-330)
  • projectName (329-329)
🪛 LanguageTool
.github/copilot-instructions.md

[grammar] ~25-~25: Use a hyphen to join words.
Context: .... Do not worry about migrations (client side or backend) unless specifically ins...

(QB_NEW_EN_HYPHEN)

⏰ 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 (14)
.clauderc (1)

1-155: LGTM - Documentation updates align with project standards.

The agent instructions file is well-structured, covering package layout, coding standards, and critical patterns. The migration guidance update (use DrizzleKit) is consistent with the rest of the PR changes.

packages/workers/src/lib/escapeHtml.js (1)

1-15: LGTM - Solid HTML escaping implementation.

The utility correctly handles null/undefined, coerces to string, and escapes all standard HTML special characters in a single pass. This is a well-established pattern for XSS prevention.

packages/shared/src/errors/domains/domain.ts (1)

101-105: LGTM - New error constant follows established patterns.

The INVITATION_ALREADY_ACCEPTED error is correctly added with appropriate code naming convention and HTTP status (400 for client error). The type will be automatically inferred via the existing ProjectErrorCode type alias.

.cursor/rules/corates.mdc (1)

98-100: LGTM - Migration guidance update is clear and consistent.

The updated guidance to use DrizzleKit for migrations aligns with the project's Drizzle ORM usage and is consistent with the .clauderc changes.

packages/web/src/components/auth-ui/CompleteProfile.jsx (1)

143-176: LGTM - Invitation acceptance flow is well-implemented.

The invitation handling correctly uses handleFetchError for the API call, properly cleans up localStorage in both success and error paths, and gracefully falls back to dashboard navigation if acceptance fails. The toast notification follows the @corates/ui pattern.

packages/workers/src/auth/email.js (1)

106-123: LGTM - sendProjectInvitation function structure is correct.

The function follows the established pattern (dev environment guard, template generation, sendEmail call) and is properly exposed in the return object. The templates handle HTML escaping internally for body content.

packages/workers/src/auth/emailTemplates.js (3)

1-4: LGTM - Centralized escapeHtml import for consistent sanitization.

Good refactoring to import the shared utility for HTML escaping across all templates.


198-244: LGTM - HTML template properly escapes user-controlled values.

The project invitation HTML template correctly escapes inviterName, projectName, and roleText to prevent XSS. The invitationUrl is server-generated and assumed safe. This addresses the previous review concern about XSS in email templates.


246-273: LGTM - Plain text template correctly omits HTML escaping.

The text version of the invitation email appropriately does not HTML-escape values since plain text emails don't interpret HTML entities.

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

171-183: Good use of db.batch() for atomicity.

The batch operation ensures that both the member insertion and invitation update succeed or fail together, preventing inconsistent state. This aligns with coding guidelines and addresses the previous review feedback.


119-133: Email normalization and mismatch logging are well-implemented.

The normalization (trim + lowercase) and detailed error logging provide good security hygiene and debuggability for email-based invitation flows.

.github/copilot-instructions.md (1)

1-158: Documentation structure improvements are well-organized.

The reorganization into structured sections (Package Structure, Critical Rules, Coding Standards, etc.) with YAML front matter and clear references to specialized rule files improves discoverability and maintainability of the guidance.

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

211-216: No issues found. The error constant PROJECT_ERRORS.INVITATION_ALREADY_ACCEPTED is properly defined in packages/shared/src/errors/domains/domain.ts and exported correctly. The code at lines 211-216 correctly uses this constant with createDomainError.


304-312: No action needed — Better Auth's signInMagicLink API correctly handles non-existent users. By default, it automatically creates a new user for emails without existing accounts, and newUserCallbackURL is designed specifically to route newly created users after magic link verification. The code is using the API correctly for the invitation flow.

- Split large forms into section components (see `add-studies/` folder pattern)
- Each file should handle one coherent responsibility
- Group related components in subdirectories with an `index.js` barrel export
Do not worry about migrations (client side or backend) unless specifically instructed. This project is not in production and has no users.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix compound adjective hyphenation.

The phrase "client side or backend" should use "client-side" (hyphenated) when used as a compound adjective modifying "migrations."

Proposed fix
-Do not worry about migrations (client side or backend) unless specifically instructed. This project is not in production and has no users.
+Do not worry about migrations (client-side or backend) unless specifically instructed. This project is not in production and has no users.
📝 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
Do not worry about migrations (client side or backend) unless specifically instructed. This project is not in production and has no users.
Do not worry about migrations (client-side or backend) unless specifically instructed. This project is not in production and has no users.
🧰 Tools
🪛 LanguageTool

[grammar] ~25-~25: Use a hyphen to join words.
Context: .... Do not worry about migrations (client side or backend) unless specifically ins...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
.github/copilot-instructions.md around line 25: the phrase "client side or
backend" is missing hyphenation for the compound adjective; change "client side"
to "client-side" (i.e., "Do not worry about migrations (client-side or backend)
unless specifically instructed.") to correctly hyphenate the compound adjective
modifying "migrations."

Comment thread packages/web/src/components/auth-ui/CompleteProfile.jsx Outdated
Comment on lines +115 to +119
// Note: Email subjects are plain text, not HTML, so we don't need HTML escaping
// However, we should still sanitize to prevent issues with email clients
const { escapeHtml } = await import('../lib/escapeHtml.js');
const safeProjectName = escapeHtml(projectName);
const subject = `You're Invited to "${safeProjectName}" - CoRATES`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Email subject should not use HTML escaping.

The comment correctly notes that email subjects are plain text, not HTML, but then proceeds to HTML-escape the project name anyway. This will result in subjects like You're Invited to "Test &amp; Project" displaying HTML entities to users.

Email clients display subject lines as plain text, so HTML entities won't be rendered - they'll appear literally.

Proposed fix - remove HTML escaping from subject
-    // Note: Email subjects are plain text, not HTML, so we don't need HTML escaping
-    // However, we should still sanitize to prevent issues with email clients
-    const { escapeHtml } = await import('../lib/escapeHtml.js');
-    const safeProjectName = escapeHtml(projectName);
-    const subject = `You're Invited to "${safeProjectName}" - CoRATES`;
+    const subject = `You're Invited to "${projectName}" - CoRATES`;
📝 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
// Note: Email subjects are plain text, not HTML, so we don't need HTML escaping
// However, we should still sanitize to prevent issues with email clients
const { escapeHtml } = await import('../lib/escapeHtml.js');
const safeProjectName = escapeHtml(projectName);
const subject = `You're Invited to "${safeProjectName}" - CoRATES`;
const subject = `You're Invited to "${projectName}" - CoRATES`;
🤖 Prompt for AI Agents
In packages/workers/src/auth/email.js around lines 115-119, the code
HTML-escapes the projectName for an email subject which causes literal entities
like "&amp;" to appear; remove the escapeHtml usage and import, use the raw
projectName for the subject but first sanitize it for header-injection by
stripping CR/LF characters (e.g. replace \r and \n with a space) and trim it,
then build the subject with the sanitized plain-text name.

Comment on lines +249 to +316
try {
const appUrl = c.env.APP_URL || 'https://corates.org';
const basepath = c.env.BASEPATH || '';
const basepathNormalized = basepath ? basepath.replace(/\/$/, '') : '';

// Create callback URL with invitation token
const callbackPath = `${basepathNormalized}/complete-profile?invitation=${token}`;
const callbackURL = `${appUrl}${callbackPath}`;

// Generate magic link using Better Auth's API
// We'll use Better Auth's signInMagicLink but intercept sendMagicLink to capture the URL
const authBaseUrl = c.env.AUTH_BASE_URL || c.env.APP_URL || 'https://api.corates.org';
let capturedMagicLinkUrl = null;

// Import required modules
const { betterAuth } = await import('better-auth');
const { magicLink } = await import('better-auth/plugins');
const { drizzleAdapter } = await import('better-auth/adapters/drizzle');
const { drizzle } = await import('drizzle-orm/d1');
const schema = await import('../db/schema.js');
const { MAGIC_LINK_EXPIRY_MINUTES } = await import('../auth/emailTemplates.js');

// Get auth secret from environment (same logic as getAuthSecret)
const authSecret = c.env.AUTH_SECRET || c.env.SECRET;
if (!authSecret) {
throw new Error('AUTH_SECRET must be configured');
}

// Create a temporary auth instance with a custom sendMagicLink that captures the URL
// Dynamic import returns module namespace, exports are directly on the object
const tempDb = drizzle(c.env.DB, { schema });
const tempAuth = betterAuth({
database: drizzleAdapter(tempDb, {
provider: 'sqlite',
schema: {
user: schema.user,
session: schema.session,
account: schema.account,
verification: schema.verification,
twoFactor: schema.twoFactor,
},
}),
baseURL: authBaseUrl,
secret: authSecret,
plugins: [
magicLink({
sendMagicLink: async ({ url }) => {
// Capture the URL instead of sending email
capturedMagicLinkUrl = url;
},
expiresIn: 60 * MAGIC_LINK_EXPIRY_MINUTES,
}),
],
});

// Call Better Auth's signInMagicLink API
await tempAuth.api.signInMagicLink({
body: {
email: email.toLowerCase(),
callbackURL: callbackURL,
newUserCallbackURL: callbackURL,
},
headers: new Headers(),
});

if (!capturedMagicLinkUrl) {
throw new Error('Failed to generate magic link URL');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Extract magic link generation into a reusable utility function.

This 67-line block dynamically imports Better Auth, creates a temporary auth instance, and captures the magic link URL. The complexity and heavyweight nature of this operation (creating a full auth instance per invitation) suggests it should be extracted to a dedicated utility module for:

  • Reusability across invitation and authentication flows
  • Testability in isolation
  • Performance optimization (potential caching of auth instance)
  • Maintainability and separation of concerns
Proposed refactor approach

Create a new utility function in packages/workers/src/auth/magicLink.js:

/**
 * Generate a magic link URL for email-based authentication
 * @param {Object} env - Environment variables (DB, AUTH_SECRET, APP_URL, etc.)
 * @param {string} email - User email
 * @param {string} callbackURL - Callback URL after authentication
 * @returns {Promise<string>} Magic link URL
 */
export async function generateMagicLinkUrl(env, email, callbackURL) {
  // Move lines 250-316 logic here
}

Then replace the inline logic with:

-      try {
-        const appUrl = c.env.APP_URL || 'https://corates.org';
-        // ... (65 lines)
-        const magicLinkUrl = capturedMagicLinkUrl;
+      try {
+        const { generateMagicLinkUrl } = await import('../auth/magicLink.js');
+        const appUrl = c.env.APP_URL || 'https://corates.org';
+        const basepath = c.env.BASEPATH || '';
+        const basepathNormalized = basepath ? basepath.replace(/\/$/, '') : '';
+        const callbackPath = `${basepathNormalized}/complete-profile?invitation=${token}`;
+        const callbackURL = `${appUrl}${callbackPath}`;
+        
+        const magicLinkUrl = await generateMagicLinkUrl(c.env, email.toLowerCase(), callbackURL);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/workers/src/routes/members.js around lines 249-316, the inline
67-line magic-link generation block should be extracted to a reusable utility to
improve reuse, testability and performance: create
packages/workers/src/auth/magicLink.js exporting an async
generateMagicLinkUrl(env, email, callbackURL) that contains the dynamic imports,
env/secret validation, temp DB/auth setup, plugin with sendMagicLink capturing
the URL, calls tempAuth.api.signInMagicLink and returns the captured URL (throw
if missing); implement optional internal caching of the auth instance to avoid
recreating it on every call; then replace the original block with a call to
generateMagicLinkUrl(c.env, email.toLowerCase(), callbackURL) and handle its
returned URL and errors.

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)

241-294: Consider extracting magic link generation to a utility.

This 50+ line block creates a temporary Better Auth instance to generate magic links. As noted in the previous review, this complexity suggests extraction to a reusable utility in packages/workers/src/auth/magicLink.js for:

  • Reusability (could be used in other invitation flows)
  • Testability in isolation
  • Potential performance optimization (auth instance caching)

The current implementation is functional but could benefit from this refactor in a follow-up PR.

🧹 Nitpick comments (3)
packages/workers/src/routes/users.js (1)

257-285: Consider using syncMemberToDO helper for consistency.

The sync-profile endpoint implements inline DO synchronization (lines 262-279), while the delete flow at line 194 uses the centralized syncMemberToDO helper. For consistency and maintainability, consider refactoring this to use the helper with action 'update'.

However, I notice the current inline implementation has different error-handling semantics (continues on failure per-project vs. fail-fast), which may be intentional for profile sync where partial success is acceptable.

Proposed refactor for consistency
     const syncPromises = userProjects.map(async ({ projectId }) => {
       try {
-        const doId = c.env.PROJECT_DOC.idFromName(projectId);
-        const projectDoc = c.env.PROJECT_DOC.get(doId);
-
-        await projectDoc.fetch(
-          new Request('https://internal/sync-member', {
-            method: 'POST',
-            headers: {
-              'Content-Type': 'application/json',
-              'X-Internal-Request': 'true',
-            },
-            body: JSON.stringify({
-              action: 'update',
-              member: {
-                userId: currentUser.id,
-                name: userData.name,
-                displayName: userData.displayName,
-                image: userData.image,
-              },
-            }),
-          }),
-        );
+        await syncMemberToDO(c.env, projectId, 'update', {
+          userId: currentUser.id,
+          name: userData.name,
+          displayName: userData.displayName,
+          image: userData.image,
+        });
         return { projectId, success: true };
       } catch (err) {
packages/workers/src/lib/project-sync.js (2)

13-27: Consider checking response status for better error detection.

The function awaits the fetch but doesn't check if the response indicates success. If the DO endpoint returns a 4xx/5xx status, the caller won't know the sync failed.

Proposed enhancement
 export async function syncMemberToDO(env, projectId, action, memberData) {
   const doId = env.PROJECT_DOC.idFromName(projectId);
   const projectDoc = env.PROJECT_DOC.get(doId);

-  await projectDoc.fetch(
+  const response = await projectDoc.fetch(
     new Request('https://internal/sync-member', {
       method: 'POST',
       headers: {
         'Content-Type': 'application/json',
         'X-Internal-Request': 'true',
       },
       body: JSON.stringify({ action, member: memberData }),
     }),
   );
+
+  if (!response.ok) {
+    throw new Error(`DO sync-member failed with status ${response.status}`);
+  }
 }

37-51: Same response status check recommendation applies here.

For consistency, syncProjectToDO should also verify the response status to surface DO-side failures to callers.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99c382e and 10aa39d.

📒 Files selected for processing (7)
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/admin/users.js
  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/users.js
🧰 Additional context used
📓 Path-based instructions (22)
**/packages/workers/**/*.{js,ts}

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

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

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
packages/workers/src/routes/**/*.js

📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)

packages/workers/src/routes/**/*.js: Always use validateRequest middleware for request body validation with validation schemas
Always use validateQueryParams middleware for query parameter validation with Zod schemas
Always create database client from environment using createDb function, never inline database connections
Use db.batch() for related database operations that must be atomic - operations must succeed or fail together
Always use Drizzle ORM with query builders (eq, and, count, etc.) - never use raw SQL
Always use createDomainError from @corates/shared with error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS) - never create error objects manually or throw strings
Wrap database operations in try-catch blocks and create domain errors for database failures using SYSTEM_ERRORS.DB_ERROR
Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler
Use requireAuth middleware to apply authentication to routes and access authenticated user via getAuth(c).user
Extract validated request body data from context using c.get('validatedBody') after using validateRequest middleware
Extract validated query parameters from context using c.get('validatedQuery') after using validateQueryParams middleware

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
**/*

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

NEVER use emojis anywhere in code, comments, documentation, plan files, commit messages, or examples. Do not use unicode symbols or emojis. For UI icons, use solid-icons library or SVGs only

Files:

  • packages/workers/src/routes/invitations.js
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Comments should explain WHY something is being done or provide context, not narrate what the code does. Reserve comments for non-obvious intent, workarounds, external system context, and edge cases

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/workers/src/routes/invitations.js
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
packages/workers/src/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

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

Always use db.batch() for multiple related database operations to ensure atomicity in Drizzle ORM

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Always use createDomainError from @corates/shared for backend error handling in workers, with predefined error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)
Wrap database operations in try-catch blocks and return domain errors using createDomainError(SYSTEM_ERRORS.DB_ERROR, ...) with operation metadata
Use validation middleware with validateRequest(schema) for request validation; do not manually validate in routes

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Never throw string literals; always throw Error objects or return domain errors from API routes
Use isErrorCode utility from @corates/shared or @/lib/error-utils.js to check for specific error codes instead of manual error comparisons

Files:

  • packages/workers/src/routes/invitations.js
  • packages/web/src/components/auth-ui/CompleteProfile.jsx
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
packages/workers/src/routes/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)

packages/workers/src/routes/**/*.{js,ts}: Always validate request bodies using validateRequest middleware from the validation config
Use validateQueryParams middleware for query parameter validation in addition to request body validation

Files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
**/packages/web/src/**/*.{jsx,tsx,js,ts}

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

**/packages/web/src/**/*.{jsx,tsx,js,ts}: For UI icons, use the solid-icons library or SVGs only. Do not use emojis.
Use createMemo to compute values based on props or state to ensure reactive updates in SolidJS components
Use Solid's createStore for complex state or state objects instead of createSignal for better performance and reactivity

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
**/packages/web/src/**/*.{jsx,tsx}

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

**/packages/web/src/**/*.{jsx,tsx}: Use Ark UI components from @corates/ui package for UI components - import from '@corates/ui', not from local components
Do NOT use Zag.js components directly - use Ark UI components from @corates/ui instead
Do NOT destructure props in SolidJS components - access props directly or wrap in a function to maintain reactivity

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{js,jsx,ts,tsx}: Use import aliases from jsconfig.json as specified in ui-components.mdc
Ensure browser compatibility, particularly with Safari

packages/web/src/**/*.{js,jsx,ts,tsx}: Always use uploadPdf from @api/pdf-api.js for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
Use cachePdf and getCachedPdf from @primitives/pdfCache.js for caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
Use importFromGoogleDrive from @api/google-drive.js for importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state using saveFormState from @/lib/formStatePersistence.js before triggering OAuth redirects for Google Drive
Use addPdfToStudy operation from useProject hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
Use removePdfFromStudy operation from useProject hook to remove PDFs from a study
Use pdfPreviewStore from @/stores/pdfPreviewStore.js for managing PDF preview state (openPreview, closePreview, getPreview methods)
Use downloadPdf from @api/pdf-api.js to download PDFs from server, then cache the result using cachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations from useProject hook instead of bypassing through direct API calls

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,ui}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Group related components in subdirectories with barrel exports

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,landing}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use Ark UI components from @corates/ui package, not local component imports

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,landing,ui}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use solid-icons library for icons (e.g., solid-icons/bi, solid-icons/fi)

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{jsx,tsx}: Do NOT prop-drill application state in SolidJS. Import stores directly where needed from packages/web/src/stores/
Do NOT destructure props in SolidJS components. Access props.field directly or wrap in function: () => props.field
Move business logic to stores, utilities, or primitives rather than keeping it in components in SolidJS
Components should receive at most 1-5 props (local config only, not shared state). Shared state lives in external stores under packages/web/src/stores/

Use PdfViewer component from @/components/checklist-ui/pdf/PdfViewer.jsx for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use createMemo for derived values in SolidJS

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Always use handleFetchError from @/lib/error-utils.js for frontend fetch calls with optional showToast parameter
Use createFormErrorSignals from @/lib/form-errors.js for handling form validation errors, field-level errors, and global errors in frontend forms

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

packages/web/src/**/*.{js,jsx}: Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Only save serializable data to IndexedDB—exclude File objects, ArrayBuffers, functions, and other non-serializable objects from form state persistence
Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
For temporary File object storage during OAuth flows (e.g., pending PDFs), use projectStore.setPendingProjectData() instead of IndexedDB, as File objects cannot be serialized
Add restore parameters to the URL after OAuth redirects in the format '?restore=&projectId=' to signal form state restoration on mount
Clear URL restore parameters after form state restoration by calling clearRestoreParamsFromUrl() to prevent stale restoration attempts on subsequent navigation
Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from @/lib/formStatePersistence.js

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS components

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,ui}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

packages/{web,ui}/**/*.{js,jsx,ts,tsx}: Import UI components from '@corates/ui' package instead of local components directories
Use solid-icons library for icons instead of emojis or other icon sources
Use Tailwind CSS classes for styling UI components
Apply responsive design using mobile-first approach with Tailwind CSS

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

Use import aliases from jsconfig.json (e.g., @/, @components/, @auth-ui/, @checklist-ui/, @project-ui/, @routes/, @primitives/, @api/, @config/, @lib/) instead of relative paths

Files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
🧠 Learnings (32)
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Better-Auth for authentication and user management

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Better-Auth for authentication and user management

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Verify authentication before WebSocket upgrade in ProjectDoc

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Use y-websocket protocol with message type 1 for awareness (presence) messages in ProjectDoc

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Check user membership in project before allowing WebSocket connection in ProjectDoc

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Implement sync endpoints (/sync, /sync-member, /sync-pdf, /disconnect-all) for internal Durable Object requests

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Handle WebSocket upgrade requests by checking for 'Upgrade: websocket' header in ProjectDoc fetch method

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/projects.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Use `db.batch()` for related database operations that must be atomic - operations must succeed or fail together

Applied to files:

  • packages/workers/src/routes/invitations.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:02:38.199Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2025-12-27T03:02:38.199Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Always use `db.batch()` for multiple related database operations to ensure atomicity in Drizzle ORM

Applied to files:

  • packages/workers/src/routes/invitations.js
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Always use `handleFetchError` from `@/lib/error-utils.js` for frontend fetch calls with optional `showToast` parameter

Applied to files:

  • packages/web/src/components/auth-ui/CompleteProfile.jsx
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Do not expose internal sync endpoints (/sync, /sync-member, /sync-pdf, /disconnect-all) publicly without X-Internal-Request verification

Applied to files:

  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Structure ProjectDoc Y.Doc with meta (Map), members (Map), and reviews (Map) containing checklists (Map), pdfs (Array), and reconciliation (Map)

Applied to files:

  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Use y-websocket protocol with message type 0 for Yjs sync messages in ProjectDoc

Applied to files:

  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : Never manually call syncFromYDoc() - sync from Y.Doc to store is handled automatically via ydoc.on('update')

Applied to files:

  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Use 'X-Internal-Request' header to identify internal sync requests to Durable Objects

Applied to files:

  • packages/workers/src/lib/project-sync.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Persist Y.Doc state to Durable Object storage using Y.encodeStateAsUpdate() on document updates

Applied to files:

  • packages/workers/src/lib/project-sync.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/workers/src/durable-objects/ProjectDoc.js, packages/web/src/primitives/useProject/** : Y.Doc structure in ProjectDoc follows a specific hierarchy: meta (Map), members (Map), reviews (Map containing studies with checklists, pdfs, and reconciliation)

Applied to files:

  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc

Applied to files:

  • packages/workers/src/lib/project-sync.js
  • packages/workers/src/routes/projects.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : Local projects (with IDs starting with 'local-') use IndexedDB persistence only and skip WebSocket connection, but still use Y.Doc structure and sync to store normally

Applied to files:

  • packages/workers/src/routes/projects.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always use `createDomainError` from `corates/shared` with error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS) - never create error objects manually or throw strings

Applied to files:

  • packages/workers/src/routes/projects.js
  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Always use `createDomainError` from `corates/shared` for backend error handling in workers, with predefined error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)

Applied to files:

  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Never throw string literals; always throw Error objects or return domain errors from API routes

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Use `isErrorCode` utility from `corates/shared` or `@/lib/error-utils.js` to check for specific error codes instead of manual error comparisons

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Wrap database operations in try-catch blocks and create domain errors for database failures using `SYSTEM_ERRORS.DB_ERROR`

Applied to files:

  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Wrap database operations in try-catch blocks and return domain errors using `createDomainError(SYSTEM_ERRORS.DB_ERROR, ...)` with operation metadata

Applied to files:

  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/admin/users.js
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Drizzle ORM for database interactions and migrations

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Drizzle ORM for ALL database interactions and migrations

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always create database client from environment using `createDb` function, never inline database connections

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always use Drizzle ORM with query builders (eq, and, count, etc.) - never use raw SQL

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Do not bypass membership checks when granting access to Durable Object WebSocket connections

Applied to files:

  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Use `requireAuth` middleware to apply authentication to routes and access authenticated user via `getAuth(c).user`

Applied to files:

  • packages/workers/src/routes/members.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : When access is denied (user removed or project deleted), the connection automatically triggers cleanup: IndexedDB data cleared, connection closed, store cleared, and user redirected

Applied to files:

  • packages/workers/src/routes/members.js
  • packages/workers/src/routes/users.js
  • packages/workers/src/routes/admin/users.js
🧬 Code graph analysis (5)
packages/workers/src/routes/invitations.js (5)
packages/workers/src/middleware/auth.js (1)
  • requireAuth (35-56)
packages/workers/src/config/validation.js (3)
  • validateRequest (299-325)
  • invitationSchemas (81-85)
  • invitationSchemas (81-85)
packages/workers/src/db/client.js (1)
  • createDb (9-11)
packages/workers/src/db/schema.js (8)
  • projectInvitations (139-153)
  • projectInvitations (139-153)
  • user (5-24)
  • user (5-24)
  • projectMembers (85-95)
  • projectMembers (85-95)
  • projects (73-82)
  • projects (73-82)
packages/workers/src/lib/project-sync.js (1)
  • syncMemberToDO (13-27)
packages/web/src/components/auth-ui/CompleteProfile.jsx (1)
packages/web/src/lib/error-utils.js (2)
  • response (52-52)
  • handleFetchError (50-84)
packages/workers/src/routes/projects.js (1)
packages/workers/src/lib/project-sync.js (1)
  • syncProjectToDO (37-51)
packages/workers/src/routes/users.js (3)
packages/workers/src/routes/admin/users.js (21)
  • userProjects (226-235)
  • userProjects (482-485)
  • db (39-39)
  • db (83-83)
  • db (215-215)
  • db (293-293)
  • db (339-339)
  • db (434-434)
  • db (457-457)
  • db (544-544)
  • db (617-617)
  • userId (214-214)
  • userId (291-291)
  • userId (338-338)
  • userId (369-369)
  • userId (433-433)
  • userId (455-455)
  • userId (543-543)
  • userId (616-616)
  • c (292-292)
  • c (547-547)
packages/workers/src/db/schema.js (2)
  • projectMembers (85-95)
  • projectMembers (85-95)
packages/workers/src/lib/project-sync.js (1)
  • syncMemberToDO (13-27)
packages/workers/src/routes/admin/users.js (2)
packages/workers/src/routes/users.js (9)
  • userProjects (187-190)
  • userProjects (251-254)
  • db (71-71)
  • db (148-148)
  • db (182-182)
  • db (231-231)
  • userId (138-138)
  • userId (183-183)
  • projectId (58-58)
packages/workers/src/lib/project-sync.js (1)
  • syncMemberToDO (13-27)
⏰ 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-workers-prod
  • GitHub Check: Workers Builds: corates
🔇 Additional comments (8)
packages/web/src/components/auth-ui/CompleteProfile.jsx (1)

139-176: LGTM - Invitation acceptance flow is well-implemented.

The invitation acceptance logic correctly:

  • Checks both URL params and localStorage for the token
  • Uses handleFetchError per coding guidelines
  • Cleans up pendingInvitationToken from localStorage on both success and error paths
  • Gracefully degrades to dashboard navigation if acceptance fails

One minor observation: the urlParams variable (line 121) is created outside the try block but only used inside it. This is fine but could be moved inside for tighter scoping.

packages/workers/src/routes/admin/users.js (1)

481-492: LGTM - Consistent pre-deletion DO synchronization.

The admin user deletion flow correctly mirrors the self-deletion pattern from users.js:

  1. Fetches all project memberships before deletions
  2. Syncs removals to all DOs atomically via Promise.all
  3. Only proceeds with database batch deletion if all DO syncs succeed

This fail-fast approach ensures data consistency between the database and Durable Objects.

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

131-155: LGTM - Appropriate non-blocking DO synchronization for project creation.

The try/catch wrapper around syncProjectToDO ensures project creation succeeds even if DO sync fails. This is the right approach because:

  • The project data is already persisted in the database
  • DO state will be reconciled when users connect via WebSocket
  • Blocking project creation on DO availability would reduce reliability

The error logging provides visibility for debugging.


219-223: LGTM - Consistent non-blocking pattern for project updates.

Same appropriate pattern as project creation - DO sync failures don't block the update response.

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

147-161: LGTM - Atomic batch operation for member insertion.

Good use of db.batch() to ensure the member insert and invitation update succeed or fail together, as required by coding guidelines. This addresses the consistency concern from the previous review.


191-204: LGTM - Non-blocking DO synchronization.

The DO sync is correctly wrapped in a try/catch to avoid failing the invitation acceptance if DO sync fails. The database operations have already succeeded atomically, so DO state can reconcile later.

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

415-427: LGTM - Consistent DO synchronization pattern.

The syncMemberToDO helper is now used consistently with proper try/catch wrapping. This matches the pattern used in other routes and ensures member additions don't fail if DO sync encounters issues.


176-211: PROJECT_ERRORS.INVITATION_ALREADY_ACCEPTED is properly defined in @corates/shared/src/errors/domains/domain.ts with code PROJECT_INVITATION_ALREADY_ACCEPTED. No action needed.

Likely an incorrect or invalid review comment.

Comment on lines +102 to +110
console.error(
`[Invitation] Email mismatch: user email="${currentUser.email}" (normalized="${normalizedUserEmail}"), invitation email="${invitation.email}" (normalized="${normalizedInvitationEmail}")`,
);
const error = createDomainError(AUTH_ERRORS.FORBIDDEN, {
reason: 'email_mismatch',
userEmail: currentUser.email,
invitationEmail: invitation.email,
});
return c.json(error, error.statusCode);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

**Consider reducing PII in error logs.**The error log at lines 102-104 includes full email addresses which are considered PII under GDPR - any information that helps to identify a person, including email addresses. GDPR logging best practices advise to only collect what you need and consider filtering sensitive fields before storage.

Consider masking the emails in the log output to reduce PII exposure while still providing useful debugging information.

Proposed fix
       if (normalizedUserEmail !== normalizedInvitationEmail) {
         console.error(
-          `[Invitation] Email mismatch: user email="${currentUser.email}" (normalized="${normalizedUserEmail}"), invitation email="${invitation.email}" (normalized="${normalizedInvitationEmail}")`,
+          `[Invitation] Email mismatch for user ${authUser.id}: normalized emails do not match`,
         );

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/workers/src/routes/invitations.js around lines 102-110, the
console.error currently logs full user and invitation email addresses (PII);
replace those raw emails with masked versions before logging (e.g., implement a
small maskEmail(email) helper that keeps the first char of the local part and
the full domain or replaces the middle of the local part with asterisks), use
the masked values in the console.error message, and also supply
maskedUserEmail/maskedInvitationEmail in the createDomainError payload instead
of the full emails so PII is not emitted in logs or error objects.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/workers/scripts/reset-db-prod.mjs (2)

58-71: Function change aligns with multi-file migrations but conflicts with guidelines.

The function has been updated to use wrangler d1 migrations apply instead of executing a specific migration file. This change is part of the broader shift to a multi-file migration workflow, which contradicts the established guideline that "All database migrations should go in a single file: packages/workers/migrations/0001_init.sql."

Ensure the architectural shift is documented and guidelines are updated accordingly.

Based on learnings, the single-file migration approach was an established pattern.


88-101: Add missing tables to DROP TABLE list.

The project_invitations, subscriptions, and twoFactor tables are created in the migration but not included in the DROP list. These tables will remain in the production database during reset, potentially leaving orphaned data. Add them to the tables array:

    const tables = [
      'mediaFiles',
      'project_members',
      'projects',
      'project_invitations',
      'subscriptions',
      'twoFactor',
      'verification',
      'account',
      'session',
      'user',
    ];
🧹 Nitpick comments (7)
packages/workers/src/routes/__tests__/members.test.js (1)

383-423: Consider enhancing test coverage for invitation creation.

The test validates the basic response structure when creating an invitation, but doesn't verify:

  • The invitation was actually stored in the database (token, expiration, role, etc.)
  • The invitation email was sent via the mocked Postmark client
  • The invitation can be retrieved and used for acceptance
Enhanced test example
  it('should create invitation when user not found', async () => {
    const nowSec = Math.floor(Date.now() / 1000);

    await seedUser({
      id: 'user-1',
      name: 'Owner',
      email: 'owner@example.com',
      createdAt: nowSec,
      updatedAt: nowSec,
    });

    await seedProject({
      id: 'project-1',
      name: 'Test Project',
      createdBy: 'user-1',
      createdAt: nowSec,
      updatedAt: nowSec,
    });

    await seedProjectMember({
      id: 'pm-1',
      projectId: 'project-1',
      userId: 'user-1',
      role: 'owner',
      joinedAt: nowSec,
    });

    const res = await fetchMembers('project-1', '', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({
        email: 'nonexistent@example.com',
        role: 'member',
      }),
    });

    expect(res.status).toBe(201);
    const body = await json(res);
    expect(body.success).toBe(true);
    expect(body.invitation).toBe(true);
+
+   // Verify invitation was stored in database
+   const invitation = await env.DB.prepare(
+     'SELECT * FROM project_invitations WHERE projectId = ?1 AND email = ?2'
+   )
+     .bind('project-1', 'nonexistent@example.com')
+     .first();
+   expect(invitation).toBeDefined();
+   expect(invitation.role).toBe('member');
+   expect(invitation.token).toBeDefined();
+   expect(invitation.acceptedAt).toBeNull();
  });
packages/docs/guides/api-development.md (1)

568-737: Comprehensive invitation documentation with good security practices.

The new documentation section thoroughly covers the invitation system with practical code examples. The emphasis on email normalization (lines 694-704) and HTML escaping (line 659, 662-663) demonstrates good security awareness.

A few optional suggestions for clarity:

  1. Lines 582-614: Consider adding a note about error handling - the example doesn't show the try-catch wrapper mentioned earlier in the guide for database operations.

  2. Lines 620-651: The temporary auth instance pattern is interesting but might benefit from a brief explanation of why this approach is used (to capture the URL without actually sending the email via Better Auth).

  3. Line 728-735: Consider adding a cross-reference to the shared error constants location (packages/shared/src/errors/domains/) for readers who want to see all available error codes.

packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx (1)

42-43: Use createMemo for derived connection state.

Based on learnings, access Y.Doc connection state via projectStore.getConnectionState(projectId), but wrap it in createMemo for better performance and reactivity. The current plain function recomputes on every access instead of caching the result.

Recommended refactor matching the useProject pattern
-  const connectionState = () => projectStore.getConnectionState(projectId);
+  const connectionState = createMemo(() => projectStore.getConnectionState(projectId));
   const synced = () => connectionState().synced;

This pattern is shown in packages/web/src/primitives/useProject/index.js and aligns with the coding guideline: "Use createMemo for derived values in SolidJS."

packages/workers/src/__tests__/admin.test.js (1)

48-83: Consider extracting shared cleanup logic.

The resetSchema() function duplicates the drop-table pattern from helpers.js. While test isolation is valuable, you could extract just the drop logic into a shared helper while keeping test-specific schema creation local.

This is optional since the current approach works and test files often benefit from explicit, self-contained setup.

packages/workers/src/__tests__/helpers.js (1)

74-104: Well-structured DO cleanup with good error handling.

The function properly handles the various DO invalidation error patterns that can occur between test runs. The graceful error handling prevents test failures from cleanup issues.

Use state.storage.deleteAll() instead of iterating over keys—it's more efficient and is available in Cloudflare Workers:

       await runInDurableObject(stub, async (instance, state) => {
-        // Clear all storage
-        const keys = await state.storage.list();
-        for (const [key] of keys) {
-          await state.storage.delete(key);
-        }
+        // Clear all storage
+        await state.storage.deleteAll();
       });
packages/workers/src/durable-objects/__tests__/EmailQueue.test.js (1)

36-58: Consider extracting error detection logic to a shared utility.

The error detection logic (lines 43-48) is duplicated in the beforeEach block (lines 79-83) and similar logic exists in packages/workers/src/__tests__/helpers.js (lines 92-96). Additionally, this implementation checks for a generic 'invalidating' string (line 46) that's not in the external helper, creating inconsistency.

Consider extracting the error detection into a shared helper function to ensure consistency across all test files.

Suggested refactor

In packages/workers/src/__tests__/helpers.js, add:

export function isInvalidationError(error) {
  const errorMessage = error?.message || String(error) || '';
  return (
    errorMessage.includes('invalidating this Durable Object') ||
    errorMessage.includes('inputGateBroken') ||
    errorMessage.includes('invalidating') ||
    error?.remote === true ||
    error?.durableObjectReset === true
  );
}

Then use it throughout this file:

 async function runInDurableObjectWithRetry(stub, fn, maxRetries = 5) {
   for (let attempt = 0; attempt < maxRetries; attempt++) {
     try {
       return await runInDurableObject(stub, fn);
     } catch (error) {
-      const errorMessage = error?.message || String(error) || '';
-      const isInvalidationError =
-        errorMessage.includes('invalidating this Durable Object') ||
-        errorMessage.includes('inputGateBroken') ||
-        errorMessage.includes('invalidating') ||
-        error?.remote === true ||
-        error?.durableObjectReset === true;
-      if (isInvalidationError && attempt < maxRetries - 1) {
+      if (isInvalidationError(error) && attempt < maxRetries - 1) {
         const delay = 100 * Math.pow(2, attempt);
         await new Promise(resolve => setTimeout(resolve, delay));
         continue;
       }
       throw error;
     }
   }
 }

And in the beforeEach block:

   } catch (error) {
-    const isInvalidationError =
-      error?.message?.includes('invalidating this Durable Object') ||
-      error?.message?.includes('inputGateBroken') ||
-      error?.remote === true ||
-      error?.durableObjectReset === true;
-    if (!isInvalidationError) {
+    if (!isInvalidationError(error)) {
       throw error;
     }
   }
packages/workers/scripts/generate-test-sql.mjs (1)

32-35: Consider adding per-file error handling.

Individual file reads could fail for various reasons (permissions, corruption, etc.), but the error would only be caught by the outer try-catch with a generic message. Consider wrapping each file read in a try-catch to provide more specific error context about which migration file failed.

Proposed enhancement for error handling
  // Read and combine all migration files
- const migrationSqls = migrationFiles.map(file => {
+ const migrationSqls = migrationFiles.map((file, index) => {
    const filePath = join(MIGRATIONS_DIR, file);
-   return readFileSync(filePath, 'utf-8');
+   try {
+     return readFileSync(filePath, 'utf-8');
+   } catch (err) {
+     throw new Error(`Failed to read migration file ${file}: ${err.message}`);
+   }
  });
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10aa39d and 489a836.

📒 Files selected for processing (15)
  • packages/docs/guides/api-development.md
  • packages/docs/guides/authentication.md
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/workers/api-docs.yaml
  • packages/workers/package.json
  • packages/workers/scripts/generate-openapi.mjs
  • packages/workers/scripts/generate-test-sql.mjs
  • packages/workers/scripts/reset-db-prod.mjs
  • packages/workers/src/__tests__/admin.test.js
  • packages/workers/src/__tests__/helpers.js
  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/users.test.js
✅ Files skipped from review due to trivial changes (1)
  • packages/docs/guides/authentication.md
🧰 Additional context used
📓 Path-based instructions (24)
**/packages/web/src/**/*.{jsx,tsx,js,ts}

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

**/packages/web/src/**/*.{jsx,tsx,js,ts}: For UI icons, use the solid-icons library or SVGs only. Do not use emojis.
Use createMemo to compute values based on props or state to ensure reactive updates in SolidJS components
Use Solid's createStore for complex state or state objects instead of createSignal for better performance and reactivity

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
**/packages/web/src/**/*.{jsx,tsx}

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

**/packages/web/src/**/*.{jsx,tsx}: Use Ark UI components from @corates/ui package for UI components - import from '@corates/ui', not from local components
Do NOT use Zag.js components directly - use Ark UI components from @corates/ui instead
Do NOT destructure props in SolidJS components - access props directly or wrap in a function to maintain reactivity

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

NEVER use emojis anywhere in code, comments, documentation, plan files, commit messages, or examples. Do not use unicode symbols or emojis. For UI icons, use solid-icons library or SVGs only

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/api-docs.yaml
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/workers/package.json
  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/scripts/generate-openapi.mjs
  • packages/docs/guides/api-development.md
  • packages/workers/scripts/generate-test-sql.mjs
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
  • packages/workers/src/__tests__/admin.test.js
  • packages/workers/src/__tests__/helpers.js
  • packages/workers/scripts/reset-db-prod.mjs
  • packages/workers/src/routes/__tests__/users.test.js
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Comments should explain WHY something is being done or provide context, not narrate what the code does. Reserve comments for non-obvious intent, workarounds, external system context, and edge cases

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/workers/src/routes/__tests__/members.test.js
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
  • packages/workers/src/__tests__/admin.test.js
  • packages/workers/src/__tests__/helpers.js
  • packages/workers/src/routes/__tests__/users.test.js
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{js,jsx,ts,tsx}: Use import aliases from jsconfig.json as specified in ui-components.mdc
Ensure browser compatibility, particularly with Safari

packages/web/src/**/*.{js,jsx,ts,tsx}: Always use uploadPdf from @api/pdf-api.js for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
Use cachePdf and getCachedPdf from @primitives/pdfCache.js for caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
Use importFromGoogleDrive from @api/google-drive.js for importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state using saveFormState from @/lib/formStatePersistence.js before triggering OAuth redirects for Google Drive
Use addPdfToStudy operation from useProject hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
Use removePdfFromStudy operation from useProject hook to remove PDFs from a study
Use pdfPreviewStore from @/stores/pdfPreviewStore.js for managing PDF preview state (openPreview, closePreview, getPreview methods)
Use downloadPdf from @api/pdf-api.js to download PDFs from server, then cache the result using cachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations from useProject hook instead of bypassing through direct API calls

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/{web,ui}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Group related components in subdirectories with barrel exports

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/{web,landing}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use Ark UI components from @corates/ui package, not local component imports

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/{web,landing,ui}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use solid-icons library for icons (e.g., solid-icons/bi, solid-icons/fi)

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{jsx,tsx}: Do NOT prop-drill application state in SolidJS. Import stores directly where needed from packages/web/src/stores/
Do NOT destructure props in SolidJS components. Access props.field directly or wrap in function: () => props.field
Move business logic to stores, utilities, or primitives rather than keeping it in components in SolidJS
Components should receive at most 1-5 props (local config only, not shared state). Shared state lives in external stores under packages/web/src/stores/

Use PdfViewer component from @/components/checklist-ui/pdf/PdfViewer.jsx for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/web/src/**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Use createMemo for derived values in SolidJS

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Always use handleFetchError from @/lib/error-utils.js for frontend fetch calls with optional showToast parameter
Use createFormErrorSignals from @/lib/form-errors.js for handling form validation errors, field-level errors, and global errors in frontend forms

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Never throw string literals; always throw Error objects or return domain errors from API routes
Use isErrorCode utility from @corates/shared or @/lib/error-utils.js to check for specific error codes instead of manual error comparisons

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
  • packages/workers/src/__tests__/helpers.js
packages/web/src/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

packages/web/src/**/*.{js,jsx}: Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Only save serializable data to IndexedDB—exclude File objects, ArrayBuffers, functions, and other non-serializable objects from form state persistence
Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
For temporary File object storage during OAuth flows (e.g., pending PDFs), use projectStore.setPendingProjectData() instead of IndexedDB, as File objects cannot be serialized
Add restore parameters to the URL after OAuth redirects in the format '?restore=&projectId=' to signal form state restoration on mount
Clear URL restore parameters after form state restoration by calling clearRestoreParamsFromUrl() to prevent stale restoration attempts on subsequent navigation
Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from @/lib/formStatePersistence.js

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/web/src/components/project-ui/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

Form state should include serializable metadata when handling files (name, size, type) and use the store's pendingPdfs pattern for actual File objects that persist across redirects

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS components

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/{web,ui}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

packages/{web,ui}/**/*.{js,jsx,ts,tsx}: Import UI components from '@corates/ui' package instead of local components directories
Use solid-icons library for icons instead of emojis or other icon sources
Use Tailwind CSS classes for styling UI components
Apply responsive design using mobile-first approach with Tailwind CSS

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/web/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

Use import aliases from jsconfig.json (e.g., @/, @components/, @auth-ui/, @checklist-ui/, @project-ui/, @routes/, @primitives/, @api/, @config/, @lib/) instead of relative paths

Files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
**/packages/workers/**/*.{js,ts}

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

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

Files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/src/__tests__/admin.test.js
  • packages/workers/src/__tests__/helpers.js
  • packages/workers/src/routes/__tests__/users.test.js
packages/workers/src/routes/**/*.js

📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)

packages/workers/src/routes/**/*.js: Always use validateRequest middleware for request body validation with validation schemas
Always use validateQueryParams middleware for query parameter validation with Zod schemas
Always create database client from environment using createDb function, never inline database connections
Use db.batch() for related database operations that must be atomic - operations must succeed or fail together
Always use Drizzle ORM with query builders (eq, and, count, etc.) - never use raw SQL
Always use createDomainError from @corates/shared with error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS) - never create error objects manually or throw strings
Wrap database operations in try-catch blocks and create domain errors for database failures using SYSTEM_ERRORS.DB_ERROR
Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler
Use requireAuth middleware to apply authentication to routes and access authenticated user via getAuth(c).user
Extract validated request body data from context using c.get('validatedBody') after using validateRequest middleware
Extract validated query parameters from context using c.get('validatedQuery') after using validateQueryParams middleware

Files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/users.test.js
packages/workers/src/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

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

Always use db.batch() for multiple related database operations to ensure atomicity in Drizzle ORM

Files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/src/__tests__/admin.test.js
  • packages/workers/src/__tests__/helpers.js
  • packages/workers/src/routes/__tests__/users.test.js
packages/workers/src/routes/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)

packages/workers/src/routes/**/*.{js,ts}: Always validate request bodies using validateRequest middleware from the validation config
Use validateQueryParams middleware for query parameter validation in addition to request body validation

Files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/users.test.js
packages/workers/src/durable-objects/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/durable-objects.mdc)

packages/workers/src/durable-objects/**/*.{js,ts}: Use 'X-Internal-Request' header to identify internal sync requests to Durable Objects
Implement sync endpoints (/sync, /sync-member, /sync-pdf, /disconnect-all) for internal Durable Object requests
Track active WebSocket sessions in a Map with session structure { user, awarenessClientId }
Remove WebSocket sessions from tracking on disconnect and perform cleanup
Persist Y.Doc state to Durable Object storage using Y.encodeStateAsUpdate() on document updates
Load Y.Doc from Durable Object storage on initialization using Y.decodeStateAsUpdate()
Do not allow unauthenticated WebSocket connections to Durable Objects
Do not bypass membership checks when granting access to Durable Object WebSocket connections
Do not expose internal sync endpoints (/sync, /sync-member, /sync-pdf, /disconnect-all) publicly without X-Internal-Request verification
Do not store large binary data directly in Y.Doc structures
Handle WebSocket connection errors gracefully by closing connections and cleaning up resources

Files:

  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Always use createDomainError from @corates/shared for backend error handling in workers, with predefined error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)
Wrap database operations in try-catch blocks and return domain errors using createDomainError(SYSTEM_ERRORS.DB_ERROR, ...) with operation metadata
Use validation middleware with validateRequest(schema) for request validation; do not manually validate in routes

Files:

  • packages/workers/src/__tests__/helpers.js
🧠 Learnings (49)
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't store Y.Doc references in component state; always retrieve the connection through useProject

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : Local projects (with IDs starting with 'local-') use IndexedDB persistence only and skip WebSocket connection, but still use Y.Doc structure and sync to store normally

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : When access is denied (user removed or project deleted), the connection automatically triggers cleanup: IndexedDB data cleared, connection closed, store cleared, and user redirected

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : IndexedDB persistence is set up automatically by useProject with database name 'corates-project-${projectId}'; don't manually create IndexedDB providers

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/workers/src/durable-objects/ProjectDoc.js, packages/web/src/primitives/useProject/** : Y.Doc structure in ProjectDoc follows a specific hierarchy: meta (Map), members (Map), reviews (Map containing studies with checklists, pdfs, and reconciliation)

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : Never manually call syncFromYDoc() - sync from Y.Doc to store is handled automatically via ydoc.on('update')

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
📚 Learning: 2025-12-27T03:02:14.843Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.843Z
Learning: Applies to {packages/web/src/components/checklist-ui/compare/**,packages/web/src/lib/checklist-domain.js} : Store reconciliation progress metadata (IDs and optional currentPage) in the study's Y.Map reconciliation property

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
📚 Learning: 2025-12-27T03:01:06.918Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.918Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js : Use getChecklistStatus utility from @/lib/checklist-domain.js to determine current checklist status before performing operations

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
📚 Learning: 2025-12-27T03:02:14.843Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.843Z
Learning: Applies to {packages/web/src/components/checklist-ui/compare/**,packages/web/src/AMSTAR2/checklist-compare.js} : Use compareChecklists utility from checklist-compare.js for comparing reviewer checklists

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
📚 Learning: 2025-12-27T03:01:06.918Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.918Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/** : Use scoreChecklist utility from @/AMSTAR2/checklist.js to determine quality assessment rating: 'High' | 'Moderate' | 'Low' | 'Critically Low'

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
📚 Learning: 2025-12-27T03:01:06.918Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.918Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/** : AMSTAR2 answers must use matrix format: array of column arrays with boolean values representing checkbox selections per column

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
📚 Learning: 2025-12-27T03:02:14.843Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.843Z
Learning: Applies to {packages/web/src/components/checklist-ui/compare/**,packages/web/src/lib/checklist-domain.js,packages/web/src/AMSTAR2/checklist-compare.js} : Store final answers in the reconciled checklist itself, not in reconciliation progress metadata

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx
  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/web/src/**/*.{jsx,tsx,js,ts} : Use `createMemo` to compute values based on props or state to ensure reactive updates in SolidJS components

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx,js,ts} : Use `createMemo` for derived values in SolidJS

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createMemo for computed/derived values that depend on reactive state in SolidJS

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
📚 Learning: 2025-12-27T03:01:06.918Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.918Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js,packages/web/src/primitives/useProject/checklists.js : Use checklist operations from useProject hook (createChecklist, updateChecklistAnswer, getChecklistData) instead of manually updating checklist structure

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
📚 Learning: 2025-12-27T03:01:54.715Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.715Z
Learning: Applies to packages/web/src/components/project-ui/**/*.{js,jsx} : Form state should include serializable metadata when handling files (name, size, type) and use the store's pendingPdfs pattern for actual File objects that persist across redirects

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
📚 Learning: 2025-12-27T03:02:26.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.927Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Prefer derived state with createMemo or signals over effects whenever possible

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsx
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Drizzle ORM for database interactions and migrations

Applied to files:

  • packages/workers/package.json
  • packages/workers/scripts/generate-test-sql.mjs
  • packages/workers/src/__tests__/admin.test.js
  • packages/workers/src/__tests__/helpers.js
  • packages/workers/scripts/reset-db-prod.mjs
  • packages/workers/src/routes/__tests__/users.test.js
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Drizzle ORM for ALL database interactions and migrations

Applied to files:

  • packages/workers/package.json
  • packages/workers/scripts/generate-test-sql.mjs
  • packages/workers/src/__tests__/admin.test.js
  • packages/workers/scripts/reset-db-prod.mjs
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/migrations/*.sql : All database migrations go in a single file: `packages/workers/migrations/0001_init.sql`. Do NOT create separate migration files. Edit the existing 0001_init.sql file directly

Applied to files:

  • packages/workers/package.json
  • packages/workers/scripts/generate-test-sql.mjs
  • packages/workers/scripts/reset-db-prod.mjs
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/migrations/0001_init.sql : All database migrations should go in a single file: `packages/workers/migrations/0001_init.sql` - do NOT create separate migration files

Applied to files:

  • packages/workers/package.json
  • packages/workers/scripts/generate-test-sql.mjs
  • packages/workers/scripts/reset-db-prod.mjs
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Wrap database operations in try-catch blocks and return domain errors using `createDomainError(SYSTEM_ERRORS.DB_ERROR, ...)` with operation metadata

Applied to files:

  • packages/workers/package.json
  • packages/workers/scripts/generate-test-sql.mjs
  • packages/workers/src/__tests__/admin.test.js
  • packages/workers/src/__tests__/helpers.js
  • packages/workers/scripts/reset-db-prod.mjs
📚 Learning: 2025-12-27T03:02:38.199Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2025-12-27T03:02:38.199Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Always use `db.batch()` for multiple related database operations to ensure atomicity in Drizzle ORM

Applied to files:

  • packages/workers/package.json
  • packages/workers/scripts/generate-test-sql.mjs
  • packages/workers/src/__tests__/admin.test.js
  • packages/workers/scripts/reset-db-prod.mjs
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Use `db.batch()` for related database operations that must be atomic - operations must succeed or fail together

Applied to files:

  • packages/workers/package.json
  • packages/workers/src/__tests__/admin.test.js
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Always use `createDomainError` from `corates/shared` for backend error handling in workers, with predefined error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)

Applied to files:

  • packages/workers/package.json
  • packages/workers/src/__tests__/helpers.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always create database client from environment using `createDb` function, never inline database connections

Applied to files:

  • packages/workers/package.json
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Wrap database operations in try-catch blocks and create domain errors for database failures using `SYSTEM_ERRORS.DB_ERROR`

Applied to files:

  • packages/workers/package.json
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Persist Y.Doc state to Durable Object storage using Y.encodeStateAsUpdate() on document updates

Applied to files:

  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/src/__tests__/helpers.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Implement sync endpoints (/sync, /sync-member, /sync-pdf, /disconnect-all) for internal Durable Object requests

Applied to files:

  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/src/__tests__/helpers.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Handle WebSocket connection errors gracefully by closing connections and cleaning up resources

Applied to files:

  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/src/__tests__/helpers.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Use 'X-Internal-Request' header to identify internal sync requests to Durable Objects

Applied to files:

  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/src/__tests__/helpers.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Load Y.Doc from Durable Object storage on initialization using Y.decodeStateAsUpdate()

Applied to files:

  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/src/__tests__/helpers.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Do not bypass membership checks when granting access to Durable Object WebSocket connections

Applied to files:

  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Remove WebSocket sessions from tracking on disconnect and perform cleanup

Applied to files:

  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/src/__tests__/helpers.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Do not expose internal sync endpoints (/sync, /sync-member, /sync-pdf, /disconnect-all) publicly without X-Internal-Request verification

Applied to files:

  • packages/workers/src/durable-objects/__tests__/EmailQueue.test.js
  • packages/workers/src/__tests__/helpers.js
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler

Applied to files:

  • packages/workers/scripts/generate-openapi.mjs
📚 Learning: 2025-12-27T03:01:06.918Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.918Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/primitives/useProject/checklists.js : Store question notes as Y.Text objects obtained from useProject hook via getQuestionNote operation to enable collaborative editing

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
📚 Learning: 2025-12-27T03:02:14.843Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.843Z
Learning: Applies to packages/web/src/components/checklist-ui/compare/** : Use Y.Text objects for collaborative editing of question notes in reconciliation

Applied to files:

  • packages/web/src/components/project-ui/overview-tab/ChartSection.jsx
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Structure ProjectDoc Y.Doc with meta (Map), members (Map), and reviews (Map) containing checklists (Map), pdfs (Array), and reconciliation (Map)

Applied to files:

  • packages/workers/src/__tests__/helpers.js
  • packages/workers/src/routes/__tests__/users.test.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Verify authentication before WebSocket upgrade in ProjectDoc

Applied to files:

  • packages/workers/src/__tests__/helpers.js
  • packages/workers/src/routes/__tests__/users.test.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/*.{js,ts} : Track active WebSocket sessions in a Map with session structure { user, awarenessClientId }

Applied to files:

  • packages/workers/src/__tests__/helpers.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Check user membership in project before allowing WebSocket connection in ProjectDoc

Applied to files:

  • packages/workers/src/routes/__tests__/users.test.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Use y-websocket protocol with message type 1 for awareness (presence) messages in ProjectDoc

Applied to files:

  • packages/workers/src/routes/__tests__/users.test.js
📚 Learning: 2025-12-27T03:02:05.939Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.939Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `removePdfFromStudy` operation from `useProject` hook to remove PDFs from a study

Applied to files:

  • packages/workers/src/routes/__tests__/users.test.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Handle WebSocket upgrade requests by checking for 'Upgrade: websocket' header in ProjectDoc fetch method

Applied to files:

  • packages/workers/src/routes/__tests__/users.test.js
🧬 Code graph analysis (7)
packages/web/src/components/project-ui/overview-tab/OverviewTab.jsx (1)
packages/web/src/primitives/useProject/index.js (2)
  • connectionState (159-159)
  • synced (162-162)
packages/workers/src/routes/__tests__/members.test.js (5)
packages/workers/src/__tests__/helpers.js (2)
  • res (318-318)
  • json (302-309)
packages/workers/src/__tests__/admin.test.js (1)
  • res (282-282)
packages/workers/src/routes/__tests__/users.test.js (1)
  • res (76-76)
packages/workers/src/routes/__tests__/projects.test.js (1)
  • res (75-75)
packages/workers/src/routes/__tests__/database.test.js (1)
  • res (59-59)
packages/workers/src/durable-objects/__tests__/EmailQueue.test.js (1)
packages/workers/src/__tests__/helpers.js (2)
  • stub (82-82)
  • isInvalidationError (93-97)
packages/workers/scripts/generate-test-sql.mjs (2)
packages/workers/scripts/generate-openapi.mjs (2)
  • __dirname (16-16)
  • filePath (273-273)
packages/workers/scripts/reset-db-prod.mjs (1)
  • __dirname (13-13)
packages/workers/src/__tests__/admin.test.js (1)
packages/workers/src/__tests__/helpers.js (2)
  • run (111-111)
  • tablesToDrop (119-130)
packages/workers/src/__tests__/helpers.js (3)
packages/workers/src/routes/avatars.js (1)
  • doId (42-42)
packages/workers/src/routes/contact.js (1)
  • env (56-56)
packages/workers/src/__tests__/admin.test.js (1)
  • run (51-51)
packages/workers/src/routes/__tests__/users.test.js (1)
packages/workers/src/__tests__/helpers.js (1)
  • clearProjectDOs (74-104)
⏰ 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

Comment on lines +18 to +38
// Read all .sql files from migrations directory
const migrationFiles = readdirSync(MIGRATIONS_DIR)
.filter(file => {
// Filter for .sql files and exclude the meta subdirectory
const filePath = join(MIGRATIONS_DIR, file);
return file.endsWith('.sql') && statSync(filePath).isFile();
})
.sort(); // Sort alphabetically (numeric prefixes ensure correct order)

if (migrationFiles.length === 0) {
throw new Error('No migration files found in migrations directory');
}

// Read and combine all migration files
const migrationSqls = migrationFiles.map(file => {
const filePath = join(MIGRATIONS_DIR, file);
return readFileSync(filePath, 'utf-8');
});

// Join all migrations with newlines to preserve structure
const combinedSql = migrationSqls.join('\n\n');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Multi-file migration approach conflicts with established guidelines.

The script has been refactored to read, concatenate, and combine multiple migration files, directly contradicting the established guideline: "All database migrations go in a single file: packages/workers/migrations/0001_init.sql. Do NOT create separate migration files. Edit the existing 0001_init.sql file directly."

This represents a fundamental shift from single-file to multi-file migrations. If this is intentional, update the project documentation and learnings to reflect the new DrizzleKit-based, multi-file migration strategy.

Based on learnings, the single-file migration pattern was explicitly documented and enforced.

🤖 Prompt for AI Agents
In packages/workers/scripts/generate-test-sql.mjs around lines 18 to 38, the
script was changed to read and concatenate all .sql files in the migrations
directory which violates the established single-file migration policy; revert
this by only reading the canonical file
packages/workers/migrations/0001_init.sql (or explicitly fail if that file is
missing), remove the logic that enumerates and joins multiple files, and ensure
any validation checks enforce that no other .sql migration files are used so the
script continues to produce SQL solely from 0001_init.sql.

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: 0

🧹 Nitpick comments (2)
packages/docs/guides/database.md (2)

250-268: Emphasize: Do not edit migration files manually.

Lines 252-257 clearly state that generated migration files should not be edited manually. This is important guidance. However, also ensure that developers understand that if regeneration is needed (e.g., due to accidental manual edits), the process is to update the Drizzle schema and re-run pnpm db:generate, not to fix the SQL files directly.

Consider adding a troubleshooting section or brief note explicitly stating: "If a migration file needs to be regenerated, do not manually edit it. Instead, update src/db/schema.js and re-run pnpm db:generate."


609-619: Document the drizzle.config.ts location.

Line 619 references drizzle.config.ts at the end of the Related Files section. For completeness, specify its location in the repository (e.g., packages/workers/drizzle.config.ts or at the root). This helps developers locate the configuration file quickly.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 489a836 and b3c2adc.

📒 Files selected for processing (2)
  • packages/docs/guides/database.md
  • packages/docs/guides/development-workflow.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

NEVER use emojis anywhere in code, comments, documentation, plan files, commit messages, or examples. Do not use unicode symbols or emojis. For UI icons, use solid-icons library or SVGs only

Files:

  • packages/docs/guides/development-workflow.md
  • packages/docs/guides/database.md
🧠 Learnings (12)
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/migrations/*.sql : All database migrations go in a single file: `packages/workers/migrations/0001_init.sql`. Do NOT create separate migration files. Edit the existing 0001_init.sql file directly

Applied to files:

  • packages/docs/guides/development-workflow.md
  • packages/docs/guides/database.md
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/migrations/0001_init.sql : All database migrations should go in a single file: `packages/workers/migrations/0001_init.sql` - do NOT create separate migration files

Applied to files:

  • packages/docs/guides/development-workflow.md
  • packages/docs/guides/database.md
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Drizzle ORM for database interactions and migrations

Applied to files:

  • packages/docs/guides/development-workflow.md
  • packages/docs/guides/database.md
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Drizzle ORM for ALL database interactions and migrations

Applied to files:

  • packages/docs/guides/development-workflow.md
  • packages/docs/guides/database.md
📚 Learning: 2025-12-27T03:01:45.058Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-27T03:01:45.058Z
Learning: Applies to packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx} : Wrap database operations in try-catch blocks and return domain errors using `createDomainError(SYSTEM_ERRORS.DB_ERROR, ...)` with operation metadata

Applied to files:

  • packages/docs/guides/development-workflow.md
  • packages/docs/guides/database.md
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Use `db.batch()` for related database operations that must be atomic - operations must succeed or fail together

Applied to files:

  • packages/docs/guides/development-workflow.md
  • packages/docs/guides/database.md
📚 Learning: 2025-12-27T03:02:38.199Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2025-12-27T03:02:38.199Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Always use `db.batch()` for multiple related database operations to ensure atomicity in Drizzle ORM

Applied to files:

  • packages/docs/guides/development-workflow.md
  • packages/docs/guides/database.md
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Wrap database operations in try-catch blocks and create domain errors for database failures using `SYSTEM_ERRORS.DB_ERROR`

Applied to files:

  • packages/docs/guides/development-workflow.md
  • packages/docs/guides/database.md
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Zod for schema and input validation on the backend

Applied to files:

  • packages/docs/guides/development-workflow.md
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always create database client from environment using `createDb` function, never inline database connections

Applied to files:

  • packages/docs/guides/development-workflow.md
📚 Learning: 2025-12-27T03:00:54.611Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-27T03:00:54.611Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always use Drizzle ORM with query builders (eq, and, count, etc.) - never use raw SQL

Applied to files:

  • packages/docs/guides/database.md
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Zod for schema and input validation on the backend

Applied to files:

  • packages/docs/guides/database.md
⏰ 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: Workers Builds: corates
🔇 Additional comments (2)
packages/docs/guides/development-workflow.md (1)

178-185: Correct - No conflict exists.

The documentation accurately describes the project's migration workflow. Lines 178-185 correctly reflect the actual project rules in .cursor/rules/corates.mdc, which mandate using DrizzleKit to generate migrations via pnpm db:generate (not creating separate migration files manually). The documentation change requires no updates.

packages/docs/guides/database.md (1)

230-241: No critical issue: Documentation aligns with project rules.

The documentation accurately describes the DrizzleKit-driven migration workflow and is consistent with .cursor/rules/corates.mdc and .github/copilot-instructions.md. Both rule files explicitly mandate using DrizzleKit to generate migrations automatically—they do not require a single-file approach. The rule files were not updated in this PR because they already support the multi-file auto-generated migration strategy described in the documentation. The actual migration file (packages/workers/migrations/0000_acoustic_power_pack.sql) follows the DrizzleKit auto-generated naming pattern, confirming the documentation reflects the project's current direction.

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: 0

🧹 Nitpick comments (2)
packages/workers/src/routes/__tests__/members.test.js (1)

386-426: Verify invitation creation in database.

The test correctly validates the response format for invitation creation, but it doesn't verify that an invitation record was actually created in the database. Consider adding a DB query to confirm the invitation exists and has the expected properties (email, projectId, role, token, etc.).

Proposed enhancement to verify DB state
 expect(res.status).toBe(201);
 const body = await json(res);
 expect(body.success).toBe(true);
 expect(body.invitation).toBe(true);
+
+// Verify invitation was created in DB
+const invitation = await env.DB.prepare(
+  'SELECT * FROM project_invitations WHERE projectId = ?1 AND email = ?2',
+)
+  .bind('project-1', 'nonexistent@example.com')
+  .first();
+expect(invitation).toBeDefined();
+expect(invitation.role).toBe('member');
+expect(invitation.token).toBeDefined();
packages/workers/src/__tests__/admin.test.js (1)

54-103: Consider wrapping foreign key operations in try-finally.

The schema reset logic is sound and the createTable helper effectively handles "already exists" errors. However, wrapping the foreign key PRAGMA statements in a try-finally block would ensure foreign keys are always re-enabled, even if an unexpected error occurs.

Proposed refactor for more robust foreign key handling
   // Disable foreign keys before dropping to avoid constraint errors
   await run('PRAGMA foreign_keys = OFF');
 
+  try {
     // Drop tables in reverse dependency order (child tables first, then parent tables)
     // This order matches the reverse of table creation order
     // Wrap in try-catch to handle cases where tables don't exist
     const tablesToDrop = [
       'subscriptions',
       'twoFactor',
       'verification',
       'account',
       'project_members',
       'projects',
       'session',
       'user',
     ];
 
     // First, try to delete all data from tables (in case DROP fails)
     for (const table of tablesToDrop) {
       try {
         await run(`DELETE FROM \`${table}\``);
       } catch {
         // Ignore - table might not exist
       }
     }
 
     // Then drop tables
     for (const table of tablesToDrop) {
       try {
         await run(`DROP TABLE IF EXISTS \`${table}\``);
       } catch (error) {
         // Ignore all errors during drop - tables might not exist or have constraint issues
         // This is safe because we're recreating the schema anyway
       }
     }
+  } finally {
-  // Re-enable foreign keys after dropping tables
-  await run('PRAGMA foreign_keys = ON');
+    // Re-enable foreign keys after dropping tables
+    await run('PRAGMA foreign_keys = ON');
+  }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3c2adc and 113a20c.

📒 Files selected for processing (3)
  • packages/workers/src/__tests__/admin.test.js
  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
🧰 Additional context used
📓 Path-based instructions (6)
**/packages/workers/**/*.{js,ts}

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

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

Files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
  • packages/workers/src/__tests__/admin.test.js
packages/workers/src/routes/**/*.js

📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)

packages/workers/src/routes/**/*.js: Always use validateRequest middleware for request body validation with validation schemas
Always use validateQueryParams middleware for query parameter validation with Zod schemas
Always create database client from environment using createDb function, never inline database connections
Use db.batch() for related database operations that must be atomic - operations must succeed or fail together
Always use Drizzle ORM with query builders (eq, and, count, etc.) - never use raw SQL
Always use createDomainError from @corates/shared with error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS) - never create error objects manually or throw strings
Wrap database operations in try-catch blocks and create domain errors for database failures using SYSTEM_ERRORS.DB_ERROR
Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler
Use requireAuth middleware to apply authentication to routes and access authenticated user via getAuth(c).user
Extract validated request body data from context using c.get('validatedBody') after using validateRequest middleware
Extract validated query parameters from context using c.get('validatedQuery') after using validateQueryParams middleware

Files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
**/*

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

NEVER use emojis anywhere in code, comments, documentation, plan files, commit messages, or examples. Do not use unicode symbols or emojis. For UI icons, use solid-icons library or SVGs only

Files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
  • packages/workers/src/__tests__/admin.test.js
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Comments should explain WHY something is being done or provide context, not narrate what the code does. Reserve comments for non-obvious intent, workarounds, external system context, and edge cases

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
  • packages/workers/src/__tests__/admin.test.js
packages/workers/src/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

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

Always use db.batch() for multiple related database operations to ensure atomicity in Drizzle ORM

Files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
  • packages/workers/src/__tests__/admin.test.js
packages/workers/src/routes/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)

packages/workers/src/routes/**/*.{js,ts}: Always validate request bodies using validateRequest middleware from the validation config
Use validateQueryParams middleware for query parameter validation in addition to request body validation

Files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
🧠 Learnings (17)
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Check user membership in project before allowing WebSocket connection in ProjectDoc

Applied to files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Verify authentication before WebSocket upgrade in ProjectDoc

Applied to files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
  • packages/workers/src/__tests__/admin.test.js
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Better-Auth for authentication and user management

Applied to files:

  • packages/workers/src/routes/__tests__/members.test.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/workers/src/durable-objects/ProjectDoc.js, packages/web/src/primitives/useProject/** : Y.Doc structure in ProjectDoc follows a specific hierarchy: meta (Map), members (Map), reviews (Map containing studies with checklists, pdfs, and reconciliation)

Applied to files:

  • packages/workers/src/routes/__tests__/members.test.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Structure ProjectDoc Y.Doc with meta (Map), members (Map), and reviews (Map) containing checklists (Map), pdfs (Array), and reconciliation (Map)

Applied to files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : When access is denied (user removed or project deleted), the connection automatically triggers cleanup: IndexedDB data cleared, connection closed, store cleared, and user redirected

Applied to files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : Local projects (with IDs starting with 'local-') use IndexedDB persistence only and skip WebSocket connection, but still use Y.Doc structure and sync to store normally

Applied to files:

  • packages/workers/src/routes/__tests__/members.test.js
  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc

Applied to files:

  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Use y-websocket protocol with message type 1 for awareness (presence) messages in ProjectDoc

Applied to files:

  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:02:05.939Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.939Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `removePdfFromStudy` operation from `useProject` hook to remove PDFs from a study

Applied to files:

  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Use y-websocket protocol with message type 0 for Yjs sync messages in ProjectDoc

Applied to files:

  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:01:35.584Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.584Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Handle WebSocket upgrade requests by checking for 'Upgrade: websocket' header in ProjectDoc fetch method

Applied to files:

  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:02:05.939Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.939Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use PDF operations from `useProject` hook instead of bypassing through direct API calls

Applied to files:

  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : IndexedDB persistence is set up automatically by useProject with database name 'corates-project-${projectId}'; don't manually create IndexedDB providers

Applied to files:

  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:02:50.075Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.075Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly

Applied to files:

  • packages/workers/src/routes/__tests__/projects.test.js
📚 Learning: 2025-12-27T03:00:44.537Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T03:00:44.537Z
Learning: Applies to **/packages/workers/**/*.{js,ts} : Use Drizzle ORM for database interactions and migrations

Applied to files:

  • packages/workers/src/__tests__/admin.test.js
📚 Learning: 2025-12-27T03:01:22.089Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-27T03:01:22.089Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Drizzle ORM for ALL database interactions and migrations

Applied to files:

  • packages/workers/src/__tests__/admin.test.js
🧬 Code graph analysis (3)
packages/workers/src/routes/__tests__/members.test.js (1)
packages/workers/src/__tests__/helpers.js (3)
  • clearProjectDOs (74-104)
  • res (318-318)
  • json (302-309)
packages/workers/src/routes/__tests__/projects.test.js (1)
packages/workers/src/__tests__/helpers.js (1)
  • clearProjectDOs (74-104)
packages/workers/src/__tests__/admin.test.js (1)
packages/workers/src/__tests__/helpers.js (2)
  • run (111-111)
  • clearProjectDOs (74-104)
⏰ 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-workers-prod
  • GitHub Check: Workers Builds: corates
🔇 Additional comments (4)
packages/workers/src/routes/__tests__/projects.test.js (1)

11-11: LGTM! Improved test isolation.

The addition of clearProjectDOs ensures Durable Object state is properly reset between tests, preventing invalidation errors and improving test reliability.

Also applies to: 64-65

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

11-11: LGTM! Proper DO cleanup for test isolation.

The clearProjectDOs helper is correctly integrated to reset Durable Object state between tests.

Also applies to: 63-64

packages/workers/src/__tests__/admin.test.js (2)

25-25: LGTM!

The addition of clearProjectDOs import properly supports the test isolation improvements added in beforeEach.


316-317: Good addition for test isolation.

Clearing ProjectDoc DOs between tests prevents invalidation errors and ensures proper test isolation. The comment clearly documents the purpose.

@InfinityBowman InfinityBowman merged commit d736630 into main Dec 27, 2025
3 checks passed
@InfinityBowman InfinityBowman deleted the 119-project---add-member-without-account branch December 27, 2025 15:41
@coderabbitai coderabbitai Bot mentioned this pull request Feb 26, 2026
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.

Project - add member without account

2 participants