119 project add member without account#174
Conversation
📝 WalkthroughWalkthroughAdds 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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
corates | 113a20c | Commit Preview URL | Dec 27 2025, 05:49 AM |
There was a problem hiding this comment.
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'sawait 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 thesendProjectInvitationfunction from the email service.The email service (
packages/workers/src/auth/email.js) already exposes asendProjectInvitationfunction 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'ssendEmail).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
projectInvitationstable 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 inmembers.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:syncMemberToDOis duplicated frommembers.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
📒 Files selected for processing (16)
packages/web/src/components/auth-ui/CompleteProfile.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/workers/migrations/0000_acoustic_power_pack.sqlpackages/workers/migrations/0001_init.sqlpackages/workers/migrations/meta/0000_snapshot.jsonpackages/workers/migrations/meta/_journal.jsonpackages/workers/src/auth/email.jspackages/workers/src/auth/emailTemplates.jspackages/workers/src/config/validation.jspackages/workers/src/db/schema.jspackages/workers/src/index.jspackages/workers/src/routes/invitations.jspackages/workers/src/routes/members.jspackages/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.jspackages/workers/src/config/validation.jspackages/workers/src/db/schema.jspackages/workers/src/index.jspackages/workers/src/auth/emailTemplates.jspackages/workers/src/auth/email.jspackages/workers/src/routes/members.js
packages/workers/src/routes/**/*.js
📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)
packages/workers/src/routes/**/*.js: Always usevalidateRequestmiddleware for request body validation with validation schemas
Always usevalidateQueryParamsmiddleware for query parameter validation with Zod schemas
Always create database client from environment usingcreateDbfunction, never inline database connections
Usedb.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 usecreateDomainErrorfrom@corates/sharedwith 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 usingSYSTEM_ERRORS.DB_ERROR
Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler
UserequireAuthmiddleware to apply authentication to routes and access authenticated user viagetAuth(c).user
Extract validated request body data from context usingc.get('validatedBody')after using validateRequest middleware
Extract validated query parameters from context usingc.get('validatedQuery')after using validateQueryParams middleware
Files:
packages/workers/src/routes/invitations.jspackages/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-iconslibrary or SVGs only
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/config/validation.jspackages/workers/src/db/schema.jspackages/workers/src/index.jspackages/workers/wrangler.jsoncpackages/workers/migrations/meta/_journal.jsonpackages/workers/src/auth/emailTemplates.jspackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/web/src/components/auth-ui/CompleteProfile.jsxpackages/workers/src/auth/email.jspackages/workers/migrations/0000_acoustic_power_pack.sqlpackages/workers/migrations/meta/0000_snapshot.jsonpackages/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 theuseProjecthook 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.jspackages/workers/src/config/validation.jspackages/workers/src/db/schema.jspackages/workers/src/index.jspackages/workers/src/auth/emailTemplates.jspackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/web/src/components/auth-ui/CompleteProfile.jsxpackages/workers/src/auth/email.jspackages/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 managementAlways use
db.batch()for multiple related database operations to ensure atomicity in Drizzle ORM
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/config/validation.jspackages/workers/src/db/schema.jspackages/workers/src/index.jspackages/workers/src/auth/emailTemplates.jspackages/workers/src/auth/email.jspackages/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 usecreateDomainErrorfrom@corates/sharedfor 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 usingcreateDomainError(SYSTEM_ERRORS.DB_ERROR, ...)with operation metadata
Use validation middleware withvalidateRequest(schema)for request validation; do not manually validate in routes
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/config/validation.jspackages/workers/src/db/schema.jspackages/workers/src/index.jspackages/workers/src/auth/emailTemplates.jspackages/workers/src/auth/email.jspackages/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
UseisErrorCodeutility from@corates/sharedor@/lib/error-utils.jsto check for specific error codes instead of manual error comparisons
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/config/validation.jspackages/workers/src/db/schema.jspackages/workers/src/index.jspackages/workers/src/auth/emailTemplates.jspackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/web/src/components/auth-ui/CompleteProfile.jsxpackages/workers/src/auth/email.jspackages/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 usingvalidateRequestmiddleware from the validation config
UsevalidateQueryParamsmiddleware for query parameter validation in addition to request body validation
Files:
packages/workers/src/routes/invitations.jspackages/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.jsand reusecommonFieldswhen 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.jsand reusecommonFieldswhen 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 thesolid-iconslibrary or SVGs only. Do not use emojis.
UsecreateMemoto compute values based on props or state to ensure reactive updates in SolidJS components
Use Solid'screateStorefor complex state or state objects instead of createSignal for better performance and reactivity
Files:
packages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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/uipackage 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.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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 useuploadPdffrom@api/pdf-api.jsfor 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)
UsecachePdfandgetCachedPdffrom@primitives/pdfCache.jsfor 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
UseimportFromGoogleDrivefrom@api/google-drive.jsfor importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state usingsaveFormStatefrom@/lib/formStatePersistence.jsbefore triggering OAuth redirects for Google Drive
UseaddPdfToStudyoperation fromuseProjecthook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
UseremovePdfFromStudyoperation fromuseProjecthook to remove PDFs from a study
UsepdfPreviewStorefrom@/stores/pdfPreviewStore.jsfor managing PDF preview state (openPreview, closePreview, getPreview methods)
UsedownloadPdffrom@api/pdf-api.jsto download PDFs from server, then cache the result usingcachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations fromuseProjecthook instead of bypassing through direct API calls
Files:
packages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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/uipackage, not local component imports
Files:
packages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/web/src/components/auth-ui/CompleteProfile.jsx
packages/{web,landing,ui}/src/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)
Use
solid-iconslibrary for icons (e.g.,solid-icons/bi,solid-icons/fi)
Files:
packages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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 frompackages/web/src/stores/
Do NOT destructure props in SolidJS components. Accessprops.fielddirectly 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 underpackages/web/src/stores/Use
PdfViewercomponent from@/components/checklist-ui/pdf/PdfViewer.jsxfor displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange
Files:
packages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/web/src/components/auth-ui/CompleteProfile.jsx
packages/web/src/**/*.{jsx,tsx,js,ts}
📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)
Use
createMemofor derived values in SolidJS
Files:
packages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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 usehandleFetchErrorfrom@/lib/error-utils.jsfor frontend fetch calls with optionalshowToastparameter
UsecreateFormErrorSignalsfrom@/lib/form-errors.jsfor handling form validation errors, field-level errors, and global errors in frontend forms
Files:
packages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AddMemberModal.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/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.jspackages/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.jspackages/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.jspackages/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.jsonpackages/workers/migrations/0000_acoustic_power_pack.sqlpackages/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.jsxpackages/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.sqlpackages/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
canAddByEmailpredicate 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
Showcomponent 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_URLenvironment 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.orgAlso applies to: 102-102
packages/workers/src/config/validation.js (1)
78-85: LGTM!The
invitationSchemasaddition 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 forproject_invitationslooks correct.The table definition properly includes:
- Unique index on
tokenfor secure token lookup- Foreign key constraints with cascading deletes to
projectsanduser- Required fields (
expiresAt) and optional fields (acceptedAt) aligned with the Drizzle schemapackages/workers/src/auth/email.js (1)
106-119: LGTM!The
sendProjectInvitationfunction 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.
| 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()) | ||
| ); |
There was a problem hiding this comment.
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.
| { | ||
| "version": "7", | ||
| "dialect": "sqlite", | ||
| "entries": [ | ||
| { | ||
| "idx": 0, | ||
| "version": "6", | ||
| "when": 1766805635744, | ||
| "tag": "0000_acoustic_power_pack", | ||
| "breakpoints": true | ||
| } | ||
| ] | ||
| } |
There was a problem hiding this comment.
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.
…thub.com/InfinityBowman/corates into 119-project---add-member-without-account
There was a problem hiding this comment.
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_FORMATis 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_TOKENor 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/sharederror 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_FORMATwithvalue: '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_EXISTSindicates 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.SECRETappears 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:
- Return a partial success indicator to the client (e.g.,
emailSent: false) so the UI can offer to resend- Use a more robust email queue with retry logic
- 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
📒 Files selected for processing (12)
.clauderc.cursor/rules/corates.mdc.github/copilot-instructions.mdpackages/shared/src/errors/domains/domain.tspackages/web/src/components/auth-ui/CompleteProfile.jsxpackages/web/src/components/auth-ui/SignUp.jsxpackages/workers/src/auth/email.jspackages/workers/src/auth/emailTemplates.jspackages/workers/src/lib/escapeHtml.jspackages/workers/src/routes/contact.jspackages/workers/src/routes/invitations.jspackages/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.jspackages/workers/src/lib/escapeHtml.jspackages/workers/src/auth/email.jspackages/workers/src/routes/members.jspackages/workers/src/routes/contact.jspackages/workers/src/auth/emailTemplates.js
packages/workers/src/routes/**/*.js
📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)
packages/workers/src/routes/**/*.js: Always usevalidateRequestmiddleware for request body validation with validation schemas
Always usevalidateQueryParamsmiddleware for query parameter validation with Zod schemas
Always create database client from environment usingcreateDbfunction, never inline database connections
Usedb.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 usecreateDomainErrorfrom@corates/sharedwith 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 usingSYSTEM_ERRORS.DB_ERROR
Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler
UserequireAuthmiddleware to apply authentication to routes and access authenticated user viagetAuth(c).user
Extract validated request body data from context usingc.get('validatedBody')after using validateRequest middleware
Extract validated query parameters from context usingc.get('validatedQuery')after using validateQueryParams middleware
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/routes/members.jspackages/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-iconslibrary or SVGs only
Files:
packages/workers/src/routes/invitations.jspackages/shared/src/errors/domains/domain.tspackages/workers/src/lib/escapeHtml.jspackages/workers/src/auth/email.jspackages/workers/src/routes/members.jspackages/web/src/components/auth-ui/CompleteProfile.jsxpackages/workers/src/routes/contact.jspackages/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 theuseProjecthook 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.jspackages/shared/src/errors/domains/domain.tspackages/workers/src/lib/escapeHtml.jspackages/workers/src/auth/email.jspackages/workers/src/routes/members.jspackages/web/src/components/auth-ui/CompleteProfile.jsxpackages/workers/src/routes/contact.jspackages/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 managementAlways use
db.batch()for multiple related database operations to ensure atomicity in Drizzle ORM
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/lib/escapeHtml.jspackages/workers/src/auth/email.jspackages/workers/src/routes/members.jspackages/workers/src/routes/contact.jspackages/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 usecreateDomainErrorfrom@corates/sharedfor 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 usingcreateDomainError(SYSTEM_ERRORS.DB_ERROR, ...)with operation metadata
Use validation middleware withvalidateRequest(schema)for request validation; do not manually validate in routes
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/lib/escapeHtml.jspackages/workers/src/auth/email.jspackages/workers/src/routes/members.jspackages/workers/src/routes/contact.jspackages/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
UseisErrorCodeutility from@corates/sharedor@/lib/error-utils.jsto check for specific error codes instead of manual error comparisons
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/lib/escapeHtml.jspackages/workers/src/auth/email.jspackages/workers/src/routes/members.jspackages/web/src/components/auth-ui/CompleteProfile.jsxpackages/workers/src/routes/contact.jspackages/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 usingvalidateRequestmiddleware from the validation config
UsevalidateQueryParamsmiddleware for query parameter validation in addition to request body validation
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/routes/members.jspackages/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 thesolid-iconslibrary or SVGs only. Do not use emojis.
UsecreateMemoto compute values based on props or state to ensure reactive updates in SolidJS components
Use Solid'screateStorefor 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/uipackage 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 useuploadPdffrom@api/pdf-api.jsfor 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)
UsecachePdfandgetCachedPdffrom@primitives/pdfCache.jsfor 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
UseimportFromGoogleDrivefrom@api/google-drive.jsfor importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state usingsaveFormStatefrom@/lib/formStatePersistence.jsbefore triggering OAuth redirects for Google Drive
UseaddPdfToStudyoperation fromuseProjecthook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
UseremovePdfFromStudyoperation fromuseProjecthook to remove PDFs from a study
UsepdfPreviewStorefrom@/stores/pdfPreviewStore.jsfor managing PDF preview state (openPreview, closePreview, getPreview methods)
UsedownloadPdffrom@api/pdf-api.jsto download PDFs from server, then cache the result usingcachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations fromuseProjecthook 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/uipackage, 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-iconslibrary 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 frompackages/web/src/stores/
Do NOT destructure props in SolidJS components. Accessprops.fielddirectly 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 underpackages/web/src/stores/Use
PdfViewercomponent from@/components/checklist-ui/pdf/PdfViewer.jsxfor 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
createMemofor 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 usehandleFetchErrorfrom@/lib/error-utils.jsfor frontend fetch calls with optionalshowToastparameter
UsecreateFormErrorSignalsfrom@/lib/form-errors.jsfor 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.jspackages/workers/src/auth/email.jspackages/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.jspackages/workers/src/auth/email.jspackages/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.jspackages/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.jspackages/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.tspackages/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.claudercpackages/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:
.claudercpackages/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_ACCEPTEDerror is correctly added with appropriate code naming convention and HTTP status (400 for client error). The type will be automatically inferred via the existingProjectErrorCodetype 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
handleFetchErrorfor the API call, properly cleans uplocalStoragein both success and error paths, and gracefully falls back to dashboard navigation if acceptance fails. The toast notification follows the@corates/uipattern.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, androleTextto prevent XSS. TheinvitationUrlis 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 ofdb.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 constantPROJECT_ERRORS.INVITATION_ALREADY_ACCEPTEDis properly defined inpackages/shared/src/errors/domains/domain.tsand exported correctly. The code at lines 211-216 correctly uses this constant withcreateDomainError.
304-312: No action needed — Better Auth'ssignInMagicLinkAPI correctly handles non-existent users. By default, it automatically creates a new user for emails without existing accounts, andnewUserCallbackURLis 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. |
There was a problem hiding this comment.
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.
| 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."
| // 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`; |
There was a problem hiding this comment.
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 & 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.
| // 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 "&" 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.
| 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'); | ||
| } |
There was a problem hiding this comment.
🛠️ 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.
There was a problem hiding this comment.
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.jsfor:
- 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 usingsyncMemberToDOhelper for consistency.The
sync-profileendpoint implements inline DO synchronization (lines 262-279), while the delete flow at line 194 uses the centralizedsyncMemberToDOhelper. 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,
syncProjectToDOshould also verify the response status to surface DO-side failures to callers.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
packages/web/src/components/auth-ui/CompleteProfile.jsxpackages/workers/src/lib/project-sync.jspackages/workers/src/routes/admin/users.jspackages/workers/src/routes/invitations.jspackages/workers/src/routes/members.jspackages/workers/src/routes/projects.jspackages/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.jspackages/workers/src/lib/project-sync.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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 usevalidateRequestmiddleware for request body validation with validation schemas
Always usevalidateQueryParamsmiddleware for query parameter validation with Zod schemas
Always create database client from environment usingcreateDbfunction, never inline database connections
Usedb.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 usecreateDomainErrorfrom@corates/sharedwith 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 usingSYSTEM_ERRORS.DB_ERROR
Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler
UserequireAuthmiddleware to apply authentication to routes and access authenticated user viagetAuth(c).user
Extract validated request body data from context usingc.get('validatedBody')after using validateRequest middleware
Extract validated query parameters from context usingc.get('validatedQuery')after using validateQueryParams middleware
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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-iconslibrary or SVGs only
Files:
packages/workers/src/routes/invitations.jspackages/web/src/components/auth-ui/CompleteProfile.jsxpackages/workers/src/lib/project-sync.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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 theuseProjecthook 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.jspackages/web/src/components/auth-ui/CompleteProfile.jsxpackages/workers/src/lib/project-sync.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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 managementAlways use
db.batch()for multiple related database operations to ensure atomicity in Drizzle ORM
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/lib/project-sync.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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 usecreateDomainErrorfrom@corates/sharedfor 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 usingcreateDomainError(SYSTEM_ERRORS.DB_ERROR, ...)with operation metadata
Use validation middleware withvalidateRequest(schema)for request validation; do not manually validate in routes
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/lib/project-sync.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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
UseisErrorCodeutility from@corates/sharedor@/lib/error-utils.jsto check for specific error codes instead of manual error comparisons
Files:
packages/workers/src/routes/invitations.jspackages/web/src/components/auth-ui/CompleteProfile.jsxpackages/workers/src/lib/project-sync.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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 usingvalidateRequestmiddleware from the validation config
UsevalidateQueryParamsmiddleware for query parameter validation in addition to request body validation
Files:
packages/workers/src/routes/invitations.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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 thesolid-iconslibrary or SVGs only. Do not use emojis.
UsecreateMemoto compute values based on props or state to ensure reactive updates in SolidJS components
Use Solid'screateStorefor 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/uipackage 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 useuploadPdffrom@api/pdf-api.jsfor 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)
UsecachePdfandgetCachedPdffrom@primitives/pdfCache.jsfor 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
UseimportFromGoogleDrivefrom@api/google-drive.jsfor importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state usingsaveFormStatefrom@/lib/formStatePersistence.jsbefore triggering OAuth redirects for Google Drive
UseaddPdfToStudyoperation fromuseProjecthook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
UseremovePdfFromStudyoperation fromuseProjecthook to remove PDFs from a study
UsepdfPreviewStorefrom@/stores/pdfPreviewStore.jsfor managing PDF preview state (openPreview, closePreview, getPreview methods)
UsedownloadPdffrom@api/pdf-api.jsto download PDFs from server, then cache the result usingcachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations fromuseProjecthook 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/uipackage, 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-iconslibrary 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 frompackages/web/src/stores/
Do NOT destructure props in SolidJS components. Accessprops.fielddirectly 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 underpackages/web/src/stores/Use
PdfViewercomponent from@/components/checklist-ui/pdf/PdfViewer.jsxfor 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
createMemofor 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 usehandleFetchErrorfrom@/lib/error-utils.jsfor frontend fetch calls with optionalshowToastparameter
UsecreateFormErrorSignalsfrom@/lib/form-errors.jsfor 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.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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.jspackages/workers/src/lib/project-sync.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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.jspackages/workers/src/lib/project-sync.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/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.jspackages/workers/src/lib/project-sync.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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.jspackages/workers/src/lib/project-sync.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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.jspackages/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.jspackages/workers/src/routes/users.jspackages/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.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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.jspackages/workers/src/routes/projects.jspackages/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.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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.jspackages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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.jspackages/workers/src/routes/projects.jspackages/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.jspackages/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.jspackages/workers/src/routes/members.jspackages/workers/src/routes/users.jspackages/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.jspackages/workers/src/routes/users.jspackages/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.jspackages/workers/src/routes/users.jspackages/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.jspackages/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.jspackages/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.jspackages/workers/src/routes/users.jspackages/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
handleFetchErrorper coding guidelines- Cleans up
pendingInvitationTokenfrom localStorage on both success and error paths- Gracefully degrades to dashboard navigation if acceptance fails
One minor observation: the
urlParamsvariable (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:
- Fetches all project memberships before deletions
- Syncs removals to all DOs atomically via
Promise.all- 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
syncProjectToDOensures 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
syncMemberToDOhelper 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_ACCEPTEDis properly defined in@corates/shared/src/errors/domains/domain.tswith codePROJECT_INVITATION_ALREADY_ACCEPTED. No action needed.Likely an incorrect or invalid review comment.
| 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); |
There was a problem hiding this comment.
**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.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (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 applyinstead 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, andtwoFactortables 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:
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.
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).
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: UsecreateMemofor derived connection state.Based on learnings, access Y.Doc connection state via projectStore.getConnectionState(projectId), but wrap it in
createMemofor 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.jsand aligns with the coding guideline: "UsecreateMemofor 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 fromhelpers.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
beforeEachblock (lines 79-83) and similar logic exists inpackages/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
beforeEachblock:} 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
📒 Files selected for processing (15)
packages/docs/guides/api-development.mdpackages/docs/guides/authentication.mdpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/web/src/components/project-ui/overview-tab/ChartSection.jsxpackages/web/src/components/project-ui/overview-tab/OverviewTab.jsxpackages/workers/api-docs.yamlpackages/workers/package.jsonpackages/workers/scripts/generate-openapi.mjspackages/workers/scripts/generate-test-sql.mjspackages/workers/scripts/reset-db-prod.mjspackages/workers/src/__tests__/admin.test.jspackages/workers/src/__tests__/helpers.jspackages/workers/src/durable-objects/__tests__/EmailQueue.test.jspackages/workers/src/routes/__tests__/members.test.jspackages/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 thesolid-iconslibrary or SVGs only. Do not use emojis.
UsecreateMemoto compute values based on props or state to ensure reactive updates in SolidJS components
Use Solid'screateStorefor complex state or state objects instead of createSignal for better performance and reactivity
Files:
packages/web/src/components/project-ui/overview-tab/OverviewTab.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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/uipackage 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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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-iconslibrary or SVGs only
Files:
packages/web/src/components/project-ui/overview-tab/OverviewTab.jsxpackages/workers/src/routes/__tests__/members.test.jspackages/workers/api-docs.yamlpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/workers/package.jsonpackages/workers/src/durable-objects/__tests__/EmailQueue.test.jspackages/workers/scripts/generate-openapi.mjspackages/docs/guides/api-development.mdpackages/workers/scripts/generate-test-sql.mjspackages/web/src/components/project-ui/overview-tab/ChartSection.jsxpackages/workers/src/__tests__/admin.test.jspackages/workers/src/__tests__/helpers.jspackages/workers/scripts/reset-db-prod.mjspackages/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 theuseProjecthook 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.jsxpackages/workers/src/routes/__tests__/members.test.jspackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/workers/src/durable-objects/__tests__/EmailQueue.test.jspackages/web/src/components/project-ui/overview-tab/ChartSection.jsxpackages/workers/src/__tests__/admin.test.jspackages/workers/src/__tests__/helpers.jspackages/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 useuploadPdffrom@api/pdf-api.jsfor 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)
UsecachePdfandgetCachedPdffrom@primitives/pdfCache.jsfor 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
UseimportFromGoogleDrivefrom@api/google-drive.jsfor importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state usingsaveFormStatefrom@/lib/formStatePersistence.jsbefore triggering OAuth redirects for Google Drive
UseaddPdfToStudyoperation fromuseProjecthook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
UseremovePdfFromStudyoperation fromuseProjecthook to remove PDFs from a study
UsepdfPreviewStorefrom@/stores/pdfPreviewStore.jsfor managing PDF preview state (openPreview, closePreview, getPreview methods)
UsedownloadPdffrom@api/pdf-api.jsto download PDFs from server, then cache the result usingcachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations fromuseProjecthook instead of bypassing through direct API calls
Files:
packages/web/src/components/project-ui/overview-tab/OverviewTab.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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/uipackage, not local component imports
Files:
packages/web/src/components/project-ui/overview-tab/OverviewTab.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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-iconslibrary for icons (e.g.,solid-icons/bi,solid-icons/fi)
Files:
packages/web/src/components/project-ui/overview-tab/OverviewTab.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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 frompackages/web/src/stores/
Do NOT destructure props in SolidJS components. Accessprops.fielddirectly 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 underpackages/web/src/stores/Use
PdfViewercomponent from@/components/checklist-ui/pdf/PdfViewer.jsxfor displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange
Files:
packages/web/src/components/project-ui/overview-tab/OverviewTab.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/web/src/components/project-ui/overview-tab/ChartSection.jsx
packages/web/src/**/*.{jsx,tsx,js,ts}
📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)
Use
createMemofor derived values in SolidJS
Files:
packages/web/src/components/project-ui/overview-tab/OverviewTab.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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 usehandleFetchErrorfrom@/lib/error-utils.jsfor frontend fetch calls with optionalshowToastparameter
UsecreateFormErrorSignalsfrom@/lib/form-errors.jsfor handling form validation errors, field-level errors, and global errors in frontend forms
Files:
packages/web/src/components/project-ui/overview-tab/OverviewTab.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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
UseisErrorCodeutility from@corates/sharedor@/lib/error-utils.jsto check for specific error codes instead of manual error comparisons
Files:
packages/web/src/components/project-ui/overview-tab/OverviewTab.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/web/src/components/project-ui/overview-tab/ChartSection.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jspackages/workers/src/durable-objects/__tests__/EmailQueue.test.jspackages/workers/src/__tests__/admin.test.jspackages/workers/src/__tests__/helpers.jspackages/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 usevalidateRequestmiddleware for request body validation with validation schemas
Always usevalidateQueryParamsmiddleware for query parameter validation with Zod schemas
Always create database client from environment usingcreateDbfunction, never inline database connections
Usedb.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 usecreateDomainErrorfrom@corates/sharedwith 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 usingSYSTEM_ERRORS.DB_ERROR
Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler
UserequireAuthmiddleware to apply authentication to routes and access authenticated user viagetAuth(c).user
Extract validated request body data from context usingc.get('validatedBody')after using validateRequest middleware
Extract validated query parameters from context usingc.get('validatedQuery')after using validateQueryParams middleware
Files:
packages/workers/src/routes/__tests__/members.test.jspackages/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 managementAlways use
db.batch()for multiple related database operations to ensure atomicity in Drizzle ORM
Files:
packages/workers/src/routes/__tests__/members.test.jspackages/workers/src/durable-objects/__tests__/EmailQueue.test.jspackages/workers/src/__tests__/admin.test.jspackages/workers/src/__tests__/helpers.jspackages/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 usingvalidateRequestmiddleware from the validation config
UsevalidateQueryParamsmiddleware for query parameter validation in addition to request body validation
Files:
packages/workers/src/routes/__tests__/members.test.jspackages/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 usecreateDomainErrorfrom@corates/sharedfor 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 usingcreateDomainError(SYSTEM_ERRORS.DB_ERROR, ...)with operation metadata
Use validation middleware withvalidateRequest(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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/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.jsxpackages/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.jsxpackages/web/src/components/project-ui/overview-tab/AMSTAR2ResultsTable.jsxpackages/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.jsxpackages/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.jsonpackages/workers/scripts/generate-test-sql.mjspackages/workers/src/__tests__/admin.test.jspackages/workers/src/__tests__/helpers.jspackages/workers/scripts/reset-db-prod.mjspackages/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.jsonpackages/workers/scripts/generate-test-sql.mjspackages/workers/src/__tests__/admin.test.jspackages/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.jsonpackages/workers/scripts/generate-test-sql.mjspackages/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.jsonpackages/workers/scripts/generate-test-sql.mjspackages/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.jsonpackages/workers/scripts/generate-test-sql.mjspackages/workers/src/__tests__/admin.test.jspackages/workers/src/__tests__/helpers.jspackages/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.jsonpackages/workers/scripts/generate-test-sql.mjspackages/workers/src/__tests__/admin.test.jspackages/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.jsonpackages/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.jsonpackages/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.jspackages/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.jspackages/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.jspackages/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.jspackages/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.jspackages/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.jspackages/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.jspackages/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.jspackages/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.jspackages/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
| // 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'); |
There was a problem hiding this comment.
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.
…thub.com/InfinityBowman/corates into 119-project---add-member-without-account
There was a problem hiding this comment.
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.jsand re-runpnpm db:generate."
609-619: Document the drizzle.config.ts location.Line 619 references
drizzle.config.tsat the end of the Related Files section. For completeness, specify its location in the repository (e.g.,packages/workers/drizzle.config.tsor at the root). This helps developers locate the configuration file quickly.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/docs/guides/database.mdpackages/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-iconslibrary or SVGs only
Files:
packages/docs/guides/development-workflow.mdpackages/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.mdpackages/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.mdpackages/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.mdpackages/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.mdpackages/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.mdpackages/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.mdpackages/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.mdpackages/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.mdpackages/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 viapnpm 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.mdcand.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.
…thub.com/InfinityBowman/corates into 119-project---add-member-without-account
There was a problem hiding this comment.
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
createTablehelper 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
📒 Files selected for processing (3)
packages/workers/src/__tests__/admin.test.jspackages/workers/src/routes/__tests__/members.test.jspackages/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.jspackages/workers/src/routes/__tests__/projects.test.jspackages/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 usevalidateRequestmiddleware for request body validation with validation schemas
Always usevalidateQueryParamsmiddleware for query parameter validation with Zod schemas
Always create database client from environment usingcreateDbfunction, never inline database connections
Usedb.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 usecreateDomainErrorfrom@corates/sharedwith 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 usingSYSTEM_ERRORS.DB_ERROR
Apply middleware in the correct order: Authentication (requireAuth) → Authorization (requireEntitlement, requireQuota) → Validation (validateRequest, validateQueryParams) → Route handler
UserequireAuthmiddleware to apply authentication to routes and access authenticated user viagetAuth(c).user
Extract validated request body data from context usingc.get('validatedBody')after using validateRequest middleware
Extract validated query parameters from context usingc.get('validatedQuery')after using validateQueryParams middleware
Files:
packages/workers/src/routes/__tests__/members.test.jspackages/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-iconslibrary or SVGs only
Files:
packages/workers/src/routes/__tests__/members.test.jspackages/workers/src/routes/__tests__/projects.test.jspackages/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 theuseProjecthook 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.jspackages/workers/src/routes/__tests__/projects.test.jspackages/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 managementAlways use
db.batch()for multiple related database operations to ensure atomicity in Drizzle ORM
Files:
packages/workers/src/routes/__tests__/members.test.jspackages/workers/src/routes/__tests__/projects.test.jspackages/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 usingvalidateRequestmiddleware from the validation config
UsevalidateQueryParamsmiddleware for query parameter validation in addition to request body validation
Files:
packages/workers/src/routes/__tests__/members.test.jspackages/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.jspackages/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.jspackages/workers/src/routes/__tests__/projects.test.jspackages/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.jspackages/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.jspackages/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.jspackages/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
clearProjectDOsensures 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
clearProjectDOshelper 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
clearProjectDOsimport properly supports the test isolation improvements added inbeforeEach.
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.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.