Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
💡 Enable Vercel Agent with $100 free credit for automated AI reviews |
WalkthroughAdds roadmap subscription and email-sending via Resend, a new rate-limiting subsystem (in-memory sliding window + middleware), marketing pages/components (roadmap, rate-limit test), Prisma model/migration for roadmap subscriptions, ORPC procedure/hook for subscribing, and supporting utilities and types. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant UI as Roadmap Page
participant Hook as useSubscribeToRoadmap
participant oRPC as orpc.users.subscribeToRoadmap
participant Middleware as RateLimit Middleware
participant DB as Prisma
participant Email as Resend
User->>UI: Submit email
UI->>Hook: mutate({ email })
Hook->>oRPC: RPC call
oRPC->>Middleware: rateLimit check (ip+identifier)
alt throttled
Middleware-->>oRPC: throw TOO_MANY_REQUESTS (retryAfter)
oRPC-->>Hook: error
Hook-->>UI: show error toast
else allowed
Middleware-->>oRPC: proceed (rateLimit meta)
oRPC->>DB: ensure not duplicate / create subscription
alt duplicate
DB-->>oRPC: constraint error -> return error response
oRPC-->>Hook: { success: false, error }
Hook-->>UI: show error
else created
oRPC->>Email: send roadmap email (non-fatal on failure)
Email-->>oRPC: success / error (logged)
oRPC-->>Hook: { success: true }
Hook-->>UI: show success toast
end
end
sequenceDiagram
participant Request as Incoming Request
participant Context as ORPC Context
participant RateLib as checkRateLimit()
participant Cache as In-Memory Cache
participant Next as Next Middleware / Handler
Request->>Context: createContext(headers)
Context->>RateLib: checkRateLimit(ip, config)
RateLib->>Cache: get/set entry + increment
alt over limit
RateLib-->>Context: { allowed: false, retryAfter, resetAt }
Context-->>Request: throw ORPCError(TOO_MANY_REQUESTS, payload)
else allowed
RateLib-->>Context: { allowed: true, remaining, limit, resetAt }
Context->>Next: call next() with rateLimit meta
Next-->>Request: response
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 14
🧹 Nitpick comments (19)
orpc/hooks/use-users.ts (1)
93-101: Consider addingonSuccesshandler for user feedback.The mutation only has an
onErrorhandler. Users should receive confirmation when they successfully subscribe to roadmap updates. Other mutations in this file (e.g.,useJoinWaitliston lines 28-45) includeonSuccesshandlers for cache invalidation or user feedback.Consider adding success handling:
export function useSubscribeToRoadmap() { return useMutation({ mutationFn: orpc.users.subscribeToRoadmap.call, + onSuccess: (data) => { + // Show success toast or trigger other success feedback + }, onError: (error) => { console.error("Failed to subscribe to roadmap:", error) }, }) }app/(marketing)/roadmap/page.tsx (1)
21-58: Consider making roadmap items configurable.The roadmap items are hard-coded in the component. While this works for now, consider:
- Moving the items to a config file for easier updates
- The
"in-progress"status fromRoadmapStatusis defined but never usedIf you plan to update the roadmap frequently, you might want to extract this to
config/roadmap.ts:// config/roadmap.ts import type { RoadmapItem } from "@/types" export const roadmapItems: RoadmapItem[] = [ // ... items here ]Then import and use in the page component.
components/app/marketing-roadmap-subscription.tsx (1)
23-25: Consider reusing the imported schema.The local
subscriptionSchemaduplicatessubscribeToRoadmapInputSchemafromschemas/user/roadmap-subscription.ts. While functional, this creates maintenance overhead if validation rules change.Consider importing and reusing the existing schema:
+import { subscribeToRoadmapInputSchema } from "@/schemas/user/roadmap-subscription" -import { z } from "zod" -const subscriptionSchema = z.object({ - email: z.string().min(1, "Email is required").email("Invalid email address"), -}) - -type SubscriptionFormData = z.infer<typeof subscriptionSchema> +type SubscriptionFormData = z.infer<typeof subscribeToRoadmapInputSchema> export function MarketingRoadmapSubscription() { const subscribeToRoadmapMutation = useSubscribeToRoadmap() // ... const form = useForm<SubscriptionFormData>({ - resolver: zodResolver(subscriptionSchema), + resolver: zodResolver(subscribeToRoadmapInputSchema), defaultValues: { email: "", }, })schemas/user/roadmap.ts (1)
4-6: Normalize email to avoid case/whitespace duplicates.Trim and lowercase the input so
Foo@bar.comandfoo@bar.comdon’t create separate records.export const subscribeToRoadmapInputSchema = z.object({ - email: z.string().min(1, "Email is required").email("Invalid email address"), + email: z + .string() + .min(1, "Email is required") + .email("Invalid email address") + .transform((v) => v.trim().toLowerCase()), })lib/email/send-roadmap-subscription-email.ts (1)
11-31: Replace console logging with structured logger and add explicit return type.Avoid console usage per repo rules; return type clarity helps callers. Also consider a friendly “From” display name.
-interface SendRoadmapSubscriptionEmailOptions { +interface SendRoadmapSubscriptionEmailOptions { to: string } -export async function sendRoadmapSubscriptionEmail({ - to, -}: SendRoadmapSubscriptionEmailOptions) { +export async function sendRoadmapSubscriptionEmail({ + to, +}: SendRoadmapSubscriptionEmailOptions): Promise<{ success: true; data: unknown }> { try { const { data, error } = await resendClient.emails.send({ - from: env.MARKETING_SUBSCRIPTION_EMAIL, + // Consider using a friendly from-name: + from: `Zero Locker <${env.MARKETING_SUBSCRIPTION_EMAIL}>`, to, subject: "You're Subscribed to Zero-Locker Updates! 🎉", - react: EmailRoadmapSubscription({ email: to }), + react: EmailRoadmapSubscription({ email: to }), }) if (error) { - console.error("Failed to send roadmap subscription email:", error) + // logger.error("Failed to send roadmap subscription email", { error }) throw new Error("Failed to send roadmap subscription email") } return { success: true, data } } catch (error) { - console.error("Error sending roadmap subscription email:", error) + // logger.error("Error sending roadmap subscription email", { error }) throw error } }app/(marketing)/rate-limit-test/page.tsx (2)
233-266: Use spinner + text for loading states instead of only changing label.Follow the UI guideline: show Icons.spinner with animate-spin and keep text; add disabled opacity.
- <Button - onClick={() => testEndpoint("userCount", "User Count", 100)} - disabled={userCountQuery.isFetching} - className="w-full" - > - {userCountQuery.isFetching ? "Testing..." : "Test User Count"} - </Button> + <Button + onClick={() => testEndpoint("userCount", "User Count", 100)} + disabled={userCountQuery.isFetching} + className="w-full disabled:opacity-50" + > + {userCountQuery.isFetching ? ( + <> + <Icons.spinner className="mr-2 size-4 animate-spin" /> + Testing + </> + ) : ( + "Test User Count" + )} + </Button>Apply the same pattern to “Waitlist Count”, “Encrypted Data Count”, and “Test Join Waitlist” buttons.
Also applies to: 278-287
53-57: Use cn() for conditional classNames.Keep class composition consistent with the project convention.
+import { cn, handleORPCError } from "@/lib/utils" -import { handleORPCError } from "@/lib/utils" ... - <div - className={`from-background via-background/50 pointer-events-none absolute bottom-0 left-0 right-0 h-32 rounded-b-lg bg-gradient-to-t to-transparent transition-opacity duration-500 ${ - showScrollIndicator ? "opacity-100" : "opacity-0" - }`} - /> + <div + className={cn( + "pointer-events-none absolute bottom-0 left-0 right-0 h-32 rounded-b-lg bg-gradient-to-t from-background via-background/50 to-transparent transition-opacity duration-500", + showScrollIndicator ? "opacity-100" : "opacity-0", + )} + /> ... - <div - className={`pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-500 ${ - showScrollIndicator ? "opacity-100" : "opacity-0" - }`} - > + <div + className={cn( + "pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-500", + showScrollIndicator ? "opacity-100" : "opacity-0", + )} + >Also applies to: 60-66
components/app/marketing-roadmap-list.tsx (2)
42-49: Avoid Array index in keys; prefer a stable unique key.Index-based keys hurt reconciliation. Use a unique id/slug; if titles are unique, use title alone.
- {items.map((item, index) => ( + {items.map((item) => ( <MarketingRoadmapItem - key={`${item.title}-${index}`} + key={item.title} // or item.id if available title={item.title} description={item.description} status={item.status} /> ))}If titles aren’t guaranteed unique, extend RoadmapItem with an
idand use that as key.
53-57: Use cn() for conditional classes.Aligns with the codebase style for class concatenation.
+import { cn } from "@/lib/utils" ... - <div - className={`from-background via-background/50 pointer-events-none absolute bottom-0 left-0 right-0 h-32 rounded-b-lg bg-gradient-to-t to-transparent transition-opacity duration-500 ${ - showScrollIndicator ? "opacity-100" : "opacity-0" - }`} - /> + <div + className={cn( + "pointer-events-none absolute bottom-0 left-0 right-0 h-32 rounded-b-lg bg-gradient-to-t from-background via-background/50 to-transparent transition-opacity duration-500", + showScrollIndicator ? "opacity-100" : "opacity-0", + )} + /> ... - <div - className={`pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-500 ${ - showScrollIndicator ? "opacity-100" : "opacity-0" - }`} - > + <div + className={cn( + "pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-500", + showScrollIndicator ? "opacity-100" : "opacity-0", + )} + >Also applies to: 60-66
middleware/rate-limit.ts (1)
50-60: Consider normalizing resetAt units in context.
checkRateLimitreturns seconds; if consumers expect ms, convert here or add a JSDoc note to prevent drift.orpc/routers/user.ts (3)
41-53: Simplify middleware usage — pass the middleware function directly.The extra wrapper is unnecessary;
.use(lenientRateLimit())etc. is cleaner.-const publicProcedure = baseProcedure.use(({ context, next }) => - lenientRateLimit()({ context, next }) -) +const publicProcedure = baseProcedure.use(lenientRateLimit()) ... -const strictPublicProcedure = baseProcedure.use(({ context, next }) => - strictRateLimit()({ context, next }) -) +const strictPublicProcedure = baseProcedure.use(strictRateLimit()) ... -const moderatePublicProcedure = baseProcedure.use(({ context, next }) => - moderateRateLimit()({ context, next }) -) +const moderatePublicProcedure = baseProcedure.use(moderateRateLimit())
216-253: Email case sensitivity — ensure DB uniqueness aligns with input normalization.If you adopt
.toLowerCase().trim()in the schema, you’re good; otherwise, consider lowercasing beforefindUnique/createto avoid duplicates by case.
327-333: Magic number for expected defaults.Extract
EXPECTED_DEFAULT_CONTAINER_COUNT = 3as a named constant to avoid magic numbers and ease future changes.- const expectedDefaultCount = 3 + const EXPECTED_DEFAULT_CONTAINER_COUNT = 3 - if (existingDefaultContainers.length >= expectedDefaultCount) { + if (existingDefaultContainers.length >= EXPECTED_DEFAULT_CONTAINER_COUNT) {lib/utils/rate-limit.ts (6)
7-12: Doc says “sliding window”, implementation is fixed-window. Pick one (or implement sliding).The code resets a fixed window at first hit; it is not a true sliding/rolling window. Update docs or switch algorithm to sliding window log/token bucket.
Apply doc tweak if you intend fixed window:
- * - Sliding window rate limiting + * - Fixed-window rate limiting (window anchored at first request)
135-137: Singleton leaks across Next.js dev HMR; reuse a global to prevent duplicate intervals.Dev reloads can instantiate multiple caches/intervals. Reuse one via globalThis.
-const rateLimitCache = new RateLimitCache() +const rateLimitCache: RateLimitCache = + (globalThis as any).__rateLimitCache ?? ((globalThis as any).__rateLimitCache = new RateLimitCache())
159-166: Async function without await.checkRateLimit is async but synchronous. Either remove async (and return a non‑Promise) or keep async only if you’ll await a store (e.g., Redis) soon. Pick one for consistency.
Option A (simpler now):
-export async function checkRateLimit( +export function checkRateLimit( ip: string, config: RateLimitConfig -): Promise<RateLimitResult> { +): RateLimitResult {Option B (keep async): add a small awaited no‑op and a TODO to switch to external store soon.
206-215: Clamp retryAfter to non‑negative.At exact boundary, subtraction can yield 0 or negative due to clock rounding. Clamp.
if (entry.count > maxRequests) { return { allowed: false, remaining: 0, limit: maxRequests, resetAt: entry.resetAt, - retryAfter: entry.resetAt - now, + retryAfter: Math.max(0, entry.resetAt - now), } }
63-67: Operational note: in‑memory cache isn’t production‑safe across instances.Serverless/multi‑instance deployments will bypass limits per instance/invocation. Ensure production uses a distributed store (Redis/Upstash) and wire this utility to it before enabling globally.
203-206: Minor: extra Map.set after mutating the entry is unnecessary.Map stores the object reference; after entry.count++, re‑setting is redundant. Safe to remove for micro‑perf/readability.
- entry.count++ - rateLimitCache.set(key, entry) + entry.count++
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
github.pngis excluded by!**/*.pngpnpm-lock.yamlis excluded by!**/pnpm-lock.yamlpublic/email/roadmap.pngis excluded by!**/*.pngpublic/og.pngis excluded by!**/*.png
📒 Files selected for processing (33)
.env.example(1 hunks)README.md(1 hunks)app/(marketing)/layout.tsx(1 hunks)app/(marketing)/page.tsx(1 hunks)app/(marketing)/rate-limit-test/page.tsx(1 hunks)app/(marketing)/roadmap/page.tsx(1 hunks)components/app/email-roadmap-subscription.tsx(1 hunks)components/app/email-waitlist.tsx(1 hunks)components/app/marketing-header-desktop.tsx(1 hunks)components/app/marketing-roadmap-item.tsx(1 hunks)components/app/marketing-roadmap-list.tsx(1 hunks)components/app/marketing-roadmap-subscription.tsx(1 hunks)components/app/marketing-waitlist-form.tsx(3 hunks)components/shared/email-footer.tsx(1 hunks)env.ts(2 hunks)lib/email/index.ts(1 hunks)lib/email/resend-client.ts(1 hunks)lib/email/send-roadmap-subscription-email.ts(1 hunks)lib/email/send-waitlist-email.ts(1 hunks)lib/utils/index.ts(1 hunks)lib/utils/rate-limit.ts(1 hunks)middleware/index.ts(1 hunks)middleware/rate-limit.ts(1 hunks)orpc/context.ts(1 hunks)orpc/hooks/use-users.ts(1 hunks)orpc/routers/user.ts(5 hunks)orpc/types.ts(1 hunks)package.json(2 hunks)prisma/schema/auth.prisma(1 hunks)prisma/schema/migrations/20251018210700_added_roadmap_subscription/migration.sql(1 hunks)schemas/user/roadmap-subscription.ts(1 hunks)schemas/user/roadmap.ts(1 hunks)types/index.d.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (49)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{js,jsx,ts,tsx}: Don't use consecutive spaces in regular expression literals
Don't use the arguments object
Don't use the comma operator
Don't write functions that exceed a given Cognitive Complexity score
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Use for...of statements instead of Array.forEach
Don't create classes that only have static members
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules that don't change anything
Don't use unnecessary escape sequences in regular expression literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports, exports, and destructured assignments to the same name
Don't use unnecessary string or template literal concatenation
Don't use String.raw in template literals when there are no escape sequences
Don't use useless case statements in switch statements
Don't use ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use void operators
Use arrow functions instead of function expressions
Use Date.now() to get milliseconds since the Unix Epoch
Use .flatMap() instead of map().flat() when possible
Use literal property access instead of computed property access
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work
Use concise optional chaining instead of chained logical expressions
Use regular expression literals instead of the RegExp constructor when possible
Don't use number literal object member names that aren't base 10 or use underscore separators
Remove redundant terms from logical expressions
Use while loops instead of for loops when you don't need initializer and update expressions
Don't reassign const variables
Don't use constant expressions in conditions
Don't use Math.min and Math.max to clamp value...
Files:
schemas/user/roadmap-subscription.tslib/utils/index.tslib/email/send-waitlist-email.tscomponents/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxapp/(marketing)/roadmap/page.tsxenv.tscomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxlib/email/send-roadmap-subscription-email.tslib/email/index.tscomponents/shared/email-footer.tsxcomponents/app/marketing-roadmap-list.tsxschemas/user/roadmap.tstypes/index.d.tslib/email/resend-client.tsapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/(marketing)/layout.tsxcomponents/app/marketing-waitlist-form.tsxmiddleware/index.tsorpc/hooks/use-users.tsorpc/types.tsmiddleware/rate-limit.tscomponents/app/email-roadmap-subscription.tsxorpc/context.tsorpc/routers/user.tslib/utils/rate-limit.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't return a value from a function that has a 'void' return type
Don't use the TypeScript directive @ts-ignore
Make sure switch-case statements are exhaustive
Don't use TypeScript enums
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Use as const instead of literal types and type annotations
Use either T[] or Array consistently
Use consistent accessibility modifiers on class properties and methods
Put default function parameters and optional function parameters last
Initialize each enum member value explicitly
Use export type for types
Use import type for types
Make sure all enum members are literal values
Use function types instead of object types with call signatures
Don't use void type outside of generic or return types
Don't use TypeScript const enum
Don't declare empty interfaces
Don't let variables evolve into any type through reassignments
Don't use the any type
Don't misuse the non-null assertion operator (!) in TypeScript files
Don't use implicit any type on variable declarations
Don't merge interfaces and classes unsafely
Don't use overload signatures that aren't next to each other
Use the namespace keyword instead of the module keyword to declare TypeScript namespacesUse type-only imports when possible
**/*.{ts,tsx}: Use camelCase for function and method names
Use UPPER_SNAKE_CASE for constants
Use camelCase for variables
Use PascalCase for classes and types
Use absolute imports for top-level modules (e.g., @/components, @/lib, @/entities) instead of long relative paths
Follow import order: React imports, third-party, internal abso...
Files:
schemas/user/roadmap-subscription.tslib/utils/index.tslib/email/send-waitlist-email.tscomponents/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxapp/(marketing)/roadmap/page.tsxenv.tscomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxlib/email/send-roadmap-subscription-email.tslib/email/index.tscomponents/shared/email-footer.tsxcomponents/app/marketing-roadmap-list.tsxschemas/user/roadmap.tstypes/index.d.tslib/email/resend-client.tsapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/(marketing)/layout.tsxcomponents/app/marketing-waitlist-form.tsxmiddleware/index.tsorpc/hooks/use-users.tsorpc/types.tsmiddleware/rate-limit.tscomponents/app/email-roadmap-subscription.tsxorpc/context.tsorpc/routers/user.tslib/utils/rate-limit.ts
**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{js,ts}: Don't use __dirname and __filename in the global scope
Use with { type: "json" } for JSON module imports
Don't access namespace imports dynamically
Use node:assert/strict over node:assert
Use the node: protocol for Node.js builtin modules
Files:
schemas/user/roadmap-subscription.tslib/utils/index.tslib/email/send-waitlist-email.tsenv.tslib/email/send-roadmap-subscription-email.tslib/email/index.tsschemas/user/roadmap.tstypes/index.d.tslib/email/resend-client.tsmiddleware/index.tsorpc/hooks/use-users.tsorpc/types.tsmiddleware/rate-limit.tsorpc/context.tsorpc/routers/user.tslib/utils/rate-limit.ts
schemas/**/[a-z0-9-]*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-overview.mdc)
Zod schemas should be placed in the schemas directory and named in kebab-case with .ts
Files:
schemas/user/roadmap-subscription.tsschemas/user/roadmap.ts
schemas/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/schemas-folder.mdc)
schemas/**/*.ts: Use Zod for all validation in the /schemas folder (define schemas using z.object, z.enum, z.array, etc.)
Define enum schemas with z.enum and expose the enum and its values (e.g., accountStatusSchema, accountStatusEnum, LIST_*)
Use .optional() for optional fields, .nullable() for DB-nullable fields, and .nullish() for null or undefined
Provide clear, user-friendly validation messages on constraints (e.g., .min, .email)
Validate arrays with z.array(itemSchema); mark arrays optional with .optional() when appropriate
Validate nested objects by composing schemas (e.g., embed encryptedDataDtoSchema; use z.object({...}))
Compose schemas via .extend() and create update shapes via .partial().extend({...})
Reuse base schemas to avoid duplication (e.g., baseKeyValuePairSchema extended for domain-specific variants)
Generate and export TypeScript types from schemas with z.infer and export the types
Name schemas consistently: {entity}DtoSchema, {entity}SimpleRoSchema, {entity}IncludeRoSchema, {entity}FormDtoSchema; types as {Entity}Dto, {Entity}SimpleRo, etc.
Use custom validation with .refine() for complex rules (e.g., password strength) and provide messages
Use conditional validation patterns (schema.refine with path) when one field depends on another (e.g., 2FA code when has2FA)
Export both schemas and their associated types to ensure type-safe usage across the appDefine Zod validation schemas for API and forms under schemas/
schemas/**/*.ts: Define all input/output schemas in the /schemas folder
Export both the Zod schema and its inferred TypeScript type
Provide clear, field-specific, contextual validation error messages in Zod schemas
List endpoint input schemas must include pagination (page, limit), search, and filtering fields with consistent defaults and boundsSchemas must be named in kebab-case.ts (e.g., credential-form-dto.ts)
Files:
schemas/user/roadmap-subscription.tsschemas/user/roadmap.ts
{components,entities,lib,orpc,schemas,hooks,config,types}/**/index.ts
📄 CodeRabbit inference engine (.cursor/rules/project-overview.mdc)
Use barrel exports: each folder should include an index.ts for clean imports
Files:
lib/utils/index.tslib/email/index.ts
lib/utils/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Place general-purpose utilities under lib/utils/
Files:
lib/utils/index.tslib/utils/rate-limit.ts
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{html,jsx,tsx}: Don't use the accessKey attribute on any HTML element
Don't set aria-hidden="true" on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like or
Only use the scope prop on elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assign tabIndex to non-interactive HTML elements
Don't use positive integers for the tabIndex property
Don't include "image", "picture", or "photo" in img alt prop
Don't use an explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
Assign tabIndex to non-interactive HTML elements with aria-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include a type attribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden)
Always include a title attribute for iframe elements
Include caption tracks for audio and video elements
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid ARIA state and property values
Use valid values for the autocomplete attribute on input elements
Use correct ISO language/country codes for the lang attribute
Don't use variables that haven't been declared in the document
Make sure void (self-closing) elements don't have children
Don't use event handlers on non-interactiv...Files:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxapp/(marketing)/roadmap/page.tsxcomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/shared/email-footer.tsxcomponents/app/marketing-roadmap-list.tsxapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/(marketing)/layout.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/app/email-roadmap-subscription.tsx**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{jsx,tsx}: Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress
Accompany onMouseOver/onMouseOut with onFocus/onBlur
Use semantic elements instead of role attributes in JSX
Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't assign to React component props
Don't use dangerous JSX props
Don't use both children and dangerouslySetInnerHTML props on the same element
Use <>...</> instead of ...
Don't add extra closing tags for components without children
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Watch out for possible "wrong" semicolons inside JSX elements
Don't put multiple components in one file; each file must have one componentFiles:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxapp/(marketing)/roadmap/page.tsxcomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/shared/email-footer.tsxcomponents/app/marketing-roadmap-list.tsxapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/(marketing)/layout.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/app/email-roadmap-subscription.tsx**/*.{html,jsx,tsx,css}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
Don't use TailwindCSS class names 'h-NUMBER w-NUMBER'; instead use 'size-NUMBR'
Files:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxapp/(marketing)/roadmap/page.tsxcomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/shared/email-footer.tsxcomponents/app/marketing-roadmap-list.tsxapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/(marketing)/layout.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/app/email-roadmap-subscription.tsxcomponents/app/**
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
Place application-specific React components under components/app
Files:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxcomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/app/email-roadmap-subscription.tsxcomponents/app/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
Use naming pattern {feature}-{purpose}-{type}.tsx for app components
Files:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxcomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/app/email-roadmap-subscription.tsxcomponents/app/marketing-*.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
Marketing components in components/app must be prefixed with marketing-
Place marketing components under components/app/ with filenames starting marketing-*.tsx
Files:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
components/**/*.{ts,tsx}: Always define a TypeScript interface for component props
Follow established naming patterns consistently across files
Use strict, explicit TypeScript types for safetyFiles:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxcomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/shared/email-footer.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/app/email-roadmap-subscription.tsxcomponents/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
components/**/*.tsx: Add 'use client' directive for components that use client-side hooks/effects
Use cn() for conditional Tailwind classes
Use semantic Tailwind colors (e.g., text-foreground, bg-background) and consistent spacing (space-y-4, gap-4)
Use responsive Tailwind prefixes (md:, lg:, xl:) for adaptive layouts
Define component variants with cva and defaultVariants when variants are needed
Prefer useState for local state; use useReducer for complex state
Use TanStack Query hooks from /orpc/hooks for server state; prefer server-side data fetching when possible
Implement proper ARIA attributes and keyboard navigation for accessibility
Use React.memo for expensive components
Use useMemo and useCallback for expensive computations and stable callbacks
Lazy-load heavy components via dynamic import
Import only needed icons from lucide-react (no wildcard imports)
Wrap components that might fail in Error Boundaries
Provide loading and error states for async UI
Add JSDoc comments for complex components
components/**/*.tsx: Components must be named in kebab-case.tsx (e.g., dashboard-credential-form.tsx)
Use React Hook Form for form management in UI forms
Use React Hook Form error handling, display validation errors, and handle submission errors gracefully in formsFiles:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxcomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/shared/email-footer.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/app/email-roadmap-subscription.tsxcomponents/**/[a-z0-9-]*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-overview.mdc)
Name React components in kebab-case with .tsx under the components directory (e.g., dashboard-credential-form.tsx)
Files:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxcomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/shared/email-footer.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/app/email-roadmap-subscription.tsxcomponents/{app,layout,shared,ui}/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Organize React components under components/app, components/layout, components/shared, and components/ui
Files:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxcomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/shared/email-footer.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/app/email-roadmap-subscription.tsx**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/zero-locker-patterns.mdc)
**/*.tsx: Use Component Composition Pattern: build complex UI by composing smaller, reusable components (e.g., Form, FormField, FormItem)
Handle form errors with React Hook Form and Zod resolver for client-side validation
Avoid direct database access in React components; use hooks/services instead
**/*.tsx: Use PascalCase for React component names
Use functional components only, typed with explicit props interfaces
Optimize components using React.memo for expensive components, useMemo for heavy computations, and useCallback for stable handlers
**/*.tsx: Never use useState for form state; use React Hook Form (useForm) instead
Always use Zod schemas with zodResolver for form validation
Always use shadcn Form components: Form, FormField, FormItem, FormLabel, FormControl, FormMessage
Always use Icons.spinner with animate-spin for loading states
Never use text like "Deleting..." or "Loading..." for loading states
Use size-4 instead of h-4 w-4 for spinner sizing (Tailwind)
Disable buttons during loading: disabled={isLoading}
Show spinner + text inside buttons when loading
Use Button variant="destructive" for delete actions
Add disabled opacity class to buttons: className="disabled:opacity-50"
Avoid manual form validation; rely on Zod + resolver integrationFiles:
components/app/marketing-roadmap-item.tsxcomponents/app/marketing-header-desktop.tsxapp/(marketing)/roadmap/page.tsxcomponents/app/email-waitlist.tsxcomponents/app/marketing-roadmap-subscription.tsxcomponents/shared/email-footer.tsxcomponents/app/marketing-roadmap-list.tsxapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/(marketing)/layout.tsxcomponents/app/marketing-waitlist-form.tsxcomponents/app/email-roadmap-subscription.tsxapp/**/page.tsx
📄 CodeRabbit inference engine (.cursor/rules/app-folder.mdc)
Page files must be named page.tsx and define the page component and metadata
Page components in the Next.js App Router must be named page.tsx
Pages must be named page.tsx
Files:
app/(marketing)/roadmap/page.tsxapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/**/[(]*[)]/**
📄 CodeRabbit inference engine (.cursor/rules/app-folder.mdc)
Use parentheses-named folders (group) to create route groups that don’t affect the URL
Files:
app/(marketing)/roadmap/page.tsxapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/(marketing)/layout.tsxapp/**/{page,layout}.tsx
📄 CodeRabbit inference engine (.cursor/rules/app-folder.mdc)
app/**/{page,layout}.tsx: Export a Metadata object asexport const metadatain pages and layouts
Perform server-side data fetching in pages and layouts where possibleFiles:
app/(marketing)/roadmap/page.tsxapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/(marketing)/layout.tsxapp/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/app-folder.mdc)
app/**/*.tsx: Prefer Server Components by default in the app directory
Add "use client" only when necessary for Client ComponentsFiles:
app/(marketing)/roadmap/page.tsxapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/(marketing)/layout.tsxapp/(marketing)/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Place marketing and landing content within app/(marketing)/
Files:
app/(marketing)/roadmap/page.tsxapp/(marketing)/page.tsxapp/(marketing)/rate-limit-test/page.tsxapp/(marketing)/layout.tsxcomponents/shared/**
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
Place reusable shared components under components/shared
Files:
components/shared/email-footer.tsxcomponents/{layout,shared,ui}/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/zero-locker-rules.mdc)
Organize layout, shared, and base UI components under components/layout, components/shared, and components/ui
Files:
components/shared/email-footer.tsxtypes/**/[a-z0-9-]*.d.ts
📄 CodeRabbit inference engine (.cursor/rules/project-overview.mdc)
Type declaration files should be named in kebab-case with a .d.ts extension under the types directory
Files:
types/index.d.tstypes/**/*.d.ts
📄 CodeRabbit inference engine (.cursor/rules/types-folder.mdc)
types/**/*.d.ts: Group related types in the same file
Use descriptive names for types and interfaces
Export types as named exports
Define interfaces for all complex objects
Use type guards for runtime type checking
Create generic types for common patterns
Use utility types for transformations
Extend base interfaces for specific use cases
Add JSDoc comments for complex types
Document type parameters and constraints
Provide examples for complex types
Avoid circular type dependencies
Use conditional types sparingly
Use consistent naming conventionsFiles:
types/index.d.tstypes/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Keep shared TypeScript type definitions under types/
Files:
types/index.d.ts**/*.d.ts
📄 CodeRabbit inference engine (.cursor/rules/zero-locker-rules.mdc)
Types definition files must be named in kebab-case.d.ts (e.g., dashboard.d.ts)
Files:
types/index.d.ts{.env,.env.*}
📄 CodeRabbit inference engine (.cursor/rules/prisma-folder.mdc)
Configure DATABASE_URL for each environment in .env files (development/production)
Files:
.env.exampleprisma/schema/!(schema).prisma
📄 CodeRabbit inference engine (.cursor/rules/prisma-folder.mdc)
Organize each entity/model into its own .prisma file under prisma/schema (multi-file schema)
Files:
prisma/schema/auth.prismaprisma/schema/!(schema|enums).prisma
📄 CodeRabbit inference engine (.cursor/rules/prisma-folder.mdc)
prisma/schema/!(schema|enums).prisma: Follow base model conventions: id as String @id @default(cuid()), createdAt @default(now()), updatedAt @updatedat, status enum default ACTIVE, userId + User relation, and snake_case @@Map
Add indexes for frequently queried fields and foreign keys; use composite unique indexes when needed
Define relationships with proper @relation fields, foreign key references, and cascade deletes where appropriate
Choose appropriate data types, model nullability correctly, and prefer enums for status fieldsFiles:
prisma/schema/auth.prismaprisma/schema/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Maintain a multi-file Prisma schema under prisma/schema/
Files:
prisma/schema/auth.prismaprisma/schema/migrations/20251018210700_added_roadmap_subscription/migration.sqlapp/**/layout.tsx
📄 CodeRabbit inference engine (.cursor/rules/app-folder.mdc)
Layout files must be named layout.tsx and define shared UI/metadata for their segment
Files:
app/(marketing)/layout.tsxapp/**/[(]*[)]/layout.tsx
📄 CodeRabbit inference engine (.cursor/rules/app-folder.mdc)
Each route group can define its own layout.tsx for group-specific UI
Files:
app/(marketing)/layout.tsxcomponents/**/*-form.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
components/**/*-form.tsx: Form components should follow the standard props interface (data?, form: UseFormReturn, onSubmit?, onCancel?)
Use React Hook Form with Zod validation for forms; keep schemas in /schemasFiles:
components/app/marketing-waitlist-form.tsxmiddleware/index.ts
📄 CodeRabbit inference engine (.cursor/rules/middleware-folder.mdc)
middleware/index.ts: Protect /dashboard routes in Next.js middleware by checking the better-auth.session_token cookie and redirect unauthenticated users to /login
Redirect authenticated users away from auth routes (/login, /register) to /dashboard in Next.js middleware
Add CORS headers for /api/orpc routes in Next.js middleware (Allow-Origin: , Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS; Headers: Content-Type, Authorization)
Export Next.js middleware config matcher for "/dashboard/:path" and "/api/orpc/:path*"Files:
middleware/index.tsmiddleware/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Implement authentication and permission access control middleware under middleware/
Files:
middleware/index.tsmiddleware/rate-limit.tsorpc/hooks/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/orpc-folder.mdc)
orpc/hooks/**/*.ts: Create React Query hooks that call orpc client methods with stable queryKey factories
Use enabled flags for queries that depend on optional parameters (e.g., only fetch detail when id is truthy)
On successful mutations, invalidate relevant list queries and update detail cache via setQueryData when possible
Centralize query keys with a factory (e.g., entityKeys) to ensure consistent cache keys across hooksUse TanStack Query for server state in hooks
Files:
orpc/hooks/use-users.tsorpc/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-overview.mdc)
Ensure consistent error handling and responses within oRPC routes and client code
orpc/**/*.ts: Use oRPC standard error codes (ORPCError) with meaningful, contextual messages
Handle specific error types (e.g., Prisma codes), log errors, and map to appropriate ORPC error codes
Use FORBIDDEN for permission errors; include feature and action in the message and upgrade info when applicableAlways encrypt passwords before storage in server-side handlers/services
Files:
orpc/hooks/use-users.tsorpc/types.tsorpc/context.tsorpc/routers/user.tsorpc/hooks/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Place React Query hooks for API calls under orpc/hooks/
Files:
orpc/hooks/use-users.ts**/use-*.ts
📄 CodeRabbit inference engine (.cursor/rules/zero-locker-rules.mdc)
Hooks files must be named use-kebab-case.ts (e.g., use-copy-to-clipboard.ts)
Files:
orpc/hooks/use-users.tsorpc/hooks/use-*.ts
📄 CodeRabbit inference engine (.cursor/rules/zero-locker-rules.mdc)
Place oRPC-related hooks under orpc/hooks with use-*.ts naming
Files:
orpc/hooks/use-users.tsorpc/types.ts
📄 CodeRabbit inference engine (.cursor/rules/orpc-folder.mdc)
Define and export ORPCContext, AuthenticatedContext, and PublicContext to type middleware and handlers
Files:
orpc/types.tsorpc/context.ts
📄 CodeRabbit inference engine (.cursor/rules/orpc-folder.mdc)
Implement createContext to read the current session/user and return nulls on failure; log failures without throwing
Files:
orpc/context.tsorpc/routers/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/orpc-folder.mdc)
orpc/routers/**/*.ts: In oRPC routers, build procedures using os.$context(), then compose authProcedure and permissionProcedure via .use(...) middleware chaining
Define routes with .input(zodSchema).output(zodSchema).handler(async (...) => { ... }) to enforce schema-validated inputs/outputs
For list endpoints, implement pagination (page, limit), deterministic orderBy, and compute total/hasMore
Apply authMiddleware to protected routes and requirePermission(feature, action) where feature-gated access is needed
Use ORPCError with standard codes (e.g., UNAUTHORIZED, FORBIDDEN, NOT_FOUND, BAD_REQUEST) for predictable error handling
Map known ORM errors (e.g., Prisma P2002) to meaningful ORPCError codes like CONFLICT and fall back to INTERNAL_SERVER_ERROR
Always validate inputs with Zod schemas and define explicit output schemas; handle validation errors gracefully
Never expose sensitive fields in responses; use client-safe entity transformations and selective include projections
Group related procedures in a single router file and use consistent naming for routes and files
Use include helpers (e.g., EntityQuery.getClientSafeInclude()) to fetch only client-safe relations
Use server-side caching or efficient queries where appropriate; prefer pagination over large result sets
Return deterministic ordering for list endpoints (e.g., orderBy createdAt desc)
Provide meaningful error messages when throwing BAD_REQUEST/CONFLICT while avoiding sensitive details
orpc/routers/**/*.ts: Group routes by feature (e.g., credentialRouter, cardRouter) in dedicated router files
Use descriptive, action-indicative procedure names within routers
Use action-based names: create, getById, list, update, delete
Keep procedure names consistent across all routers
Use camelCase for procedure names
Use Zod schemas for all procedure inputs via .input(schema)
List procedures must return metadata (total, hasMore, page, limit) alongside data
Apply authentication middleware to all protecte...Files:
orpc/routers/user.tsorpc/routers/{credential,card,secret,user}.ts
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Define oRPC routers in orpc/routers/ with files credential.ts, card.ts, secret.ts, and user.ts
Files:
orpc/routers/user.tsorpc/routers/**/!(*index).ts
📄 CodeRabbit inference engine (.cursor/rules/orpc-patterns.mdc)
Export routers individually from their own files
Files:
orpc/routers/user.tsorpc/routers/*.ts
📄 CodeRabbit inference engine (.cursor/rules/zero-locker-rules.mdc)
Define route handlers in orpc/routers with one file per domain (e.g., credential.ts, card.ts)
Files:
orpc/routers/user.ts🧠 Learnings (12)
📚 Learning: 2025-10-02T22:19:00.164Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/orpc-patterns.mdc:0-0 Timestamp: 2025-10-02T22:19:00.164Z Learning: Applies to orpc/**/*.ts : Use oRPC standard error codes (ORPCError) with meaningful, contextual messagesApplied to files:
lib/utils/index.ts📚 Learning: 2025-10-02T21:03:44.148Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/project-overview.mdc:0-0 Timestamp: 2025-10-02T21:03:44.148Z Learning: Applies to orpc/**/*.ts : Ensure consistent error handling and responses within oRPC routes and client codeApplied to files:
lib/utils/index.ts📚 Learning: 2025-10-02T22:19:00.164Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/orpc-patterns.mdc:0-0 Timestamp: 2025-10-02T22:19:00.164Z Learning: Applies to orpc/**/*.ts : Handle specific error types (e.g., Prisma codes), log errors, and map to appropriate ORPC error codesApplied to files:
lib/utils/index.ts📚 Learning: 2025-10-02T21:01:44.094Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/orpc-folder.mdc:0-0 Timestamp: 2025-10-02T21:01:44.094Z Learning: Applies to orpc/routers/**/*.ts : Use ORPCError with standard codes (e.g., UNAUTHORIZED, FORBIDDEN, NOT_FOUND, BAD_REQUEST) for predictable error handlingApplied to files:
lib/utils/index.ts📚 Learning: 2025-10-02T22:21:11.735Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/zero-locker-rules.mdc:0-0 Timestamp: 2025-10-02T22:21:11.735Z Learning: Applies to orpc/routers/**/*.ts : Use standard error codes (oRPCError), provide meaningful messages, and log errors in API handlersApplied to files:
lib/utils/index.ts📚 Learning: 2025-10-02T20:56:15.975Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/components-folder.mdc:0-0 Timestamp: 2025-10-02T20:56:15.975Z Learning: Applies to components/app/marketing-*.tsx : Marketing components in components/app must be prefixed with marketing-Applied to files:
components/app/marketing-roadmap-item.tsxapp/(marketing)/page.tsxapp/(marketing)/layout.tsx📚 Learning: 2025-10-02T20:55:02.956Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/app-folder.mdc:0-0 Timestamp: 2025-10-02T20:55:02.956Z Learning: Applies to app/**/{page,layout}.tsx : Export a Metadata object as `export const metadata` in pages and layoutsApplied to files:
app/(marketing)/roadmap/page.tsx📚 Learning: 2025-10-02T20:55:02.956Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/app-folder.mdc:0-0 Timestamp: 2025-10-02T20:55:02.956Z Learning: Use required environment variables: NEXT_PUBLIC_APP_URL, BETTER_AUTH_SECRET, DATABASE_URL, NODE_ENVApplied to files:
env.ts.env.example📚 Learning: 2025-10-02T22:21:11.735Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/zero-locker-rules.mdc:0-0 Timestamp: 2025-10-02T22:21:11.735Z Learning: Applies to components/app/marketing-*.tsx : Place marketing components under components/app/ with filenames starting marketing-*.tsxApplied to files:
app/(marketing)/page.tsxapp/(marketing)/layout.tsx📚 Learning: 2025-10-02T21:01:02.677Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/middleware-folder.mdc:0-0 Timestamp: 2025-10-02T21:01:02.677Z Learning: Applies to middleware/auth.ts : Provide a publicMiddleware that passes through without authentication requirementsApplied to files:
middleware/index.ts📚 Learning: 2025-10-02T21:01:44.094Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/orpc-folder.mdc:0-0 Timestamp: 2025-10-02T21:01:44.094Z Learning: Applies to orpc/types.ts : Define and export ORPCContext, AuthenticatedContext, and PublicContext to type middleware and handlersApplied to files:
orpc/types.tsmiddleware/rate-limit.tsorpc/context.ts📚 Learning: 2025-10-02T21:01:44.094Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/orpc-folder.mdc:0-0 Timestamp: 2025-10-02T21:01:44.094Z Learning: Applies to orpc/context.ts : Implement createContext to read the current session/user and return nulls on failure; log failures without throwingApplied to files:
orpc/context.ts🧬 Code graph analysis (20)
lib/email/send-waitlist-email.ts (3)
lib/email/resend-client.ts (1)
resendClient(5-5)env.ts (1)
env(4-27)components/app/email-waitlist.tsx (1)
EmailWaitlist(25-90)components/app/marketing-header-desktop.tsx (2)
components/shared/icons.tsx (1)
Icons(139-262)config/site.ts (1)
siteConfig(3-41)app/(marketing)/roadmap/page.tsx (4)
config/site.ts (1)
siteConfig(3-41)types/index.d.ts (1)
RoadmapItem(61-65)components/app/marketing-roadmap-list.tsx (1)
MarketingRoadmapList(15-69)components/app/marketing-roadmap-subscription.tsx (1)
MarketingRoadmapSubscription(29-146)components/app/email-waitlist.tsx (2)
config/site.ts (1)
siteConfig(3-41)components/shared/email-footer.tsx (1)
EmailFooter(14-42)components/app/marketing-roadmap-subscription.tsx (3)
orpc/hooks/use-users.ts (1)
useSubscribeToRoadmap(94-101)lib/utils/index.ts (1)
handleORPCError(199-234)components/shared/icons.tsx (1)
Icons(139-262)lib/email/send-roadmap-subscription-email.ts (3)
lib/email/resend-client.ts (1)
resendClient(5-5)env.ts (1)
env(4-27)components/app/email-roadmap-subscription.tsx (1)
EmailRoadmapSubscription(24-106)components/shared/email-footer.tsx (1)
config/site.ts (1)
siteConfig(3-41)components/app/marketing-roadmap-list.tsx (3)
types/index.d.ts (1)
RoadmapItem(61-65)components/app/marketing-roadmap-item.tsx (1)
MarketingRoadmapItem(7-44)components/shared/icons.tsx (1)
Icons(139-262)schemas/user/roadmap.ts (1)
schemas/user/roadmap-subscription.ts (4)
subscribeToRoadmapInputSchema(4-6)SubscribeToRoadmapInput(8-10)subscribeToRoadmapOutputSchema(13-16)SubscribeToRoadmapOutput(18-20)lib/email/resend-client.ts (1)
env.ts (1)
env(4-27)app/(marketing)/page.tsx (4)
components/app/marketing-hero.tsx (1)
MarketingHero(29-98)components/app/marketing-stats.tsx (1)
MarketingStats(7-34)components/app/marketing-features.tsx (1)
MarketingFeatures(9-105)components/app/marketing-how-it-works.tsx (1)
MarketingHowItWorks(1-57)app/(marketing)/rate-limit-test/page.tsx (2)
orpc/hooks/use-users.ts (4)
useUserCount(58-64)useWaitlistCount(48-55)useEncryptedDataCount(67-73)useJoinWaitlist(28-45)lib/utils/index.ts (1)
handleORPCError(199-234)app/(marketing)/layout.tsx (3)
components/ui/animated-grid-pattern.tsx (1)
AnimatedGridPattern(27-157)components/app/marketing-header-desktop.tsx (1)
MarketingHeaderDesktop(12-42)components/app/marketing-footer.tsx (1)
MarketingFooter(5-21)components/app/marketing-waitlist-form.tsx (1)
lib/utils/index.ts (1)
handleORPCError(199-234)orpc/hooks/use-users.ts (1)
orpc/client/utils.ts (1)
orpc(5-7)orpc/types.ts (1)
types/dashboard.d.ts (1)
User(1-9)middleware/rate-limit.ts (2)
lib/utils/rate-limit.ts (3)
RateLimitConfig(14-29)checkRateLimit(159-224)RATE_LIMIT_PRESETS(258-279)orpc/types.ts (1)
PublicContext(35-35)components/app/email-roadmap-subscription.tsx (2)
config/site.ts (1)
siteConfig(3-41)components/shared/email-footer.tsx (1)
EmailFooter(14-42)orpc/context.ts (1)
orpc/types.ts (1)
ORPCContext(14-28)orpc/routers/user.ts (6)
orpc/types.ts (1)
ORPCContext(14-28)middleware/rate-limit.ts (3)
lenientRateLimit(94-99)strictRateLimit(72-77)moderateRateLimit(83-88)middleware/auth.ts (1)
authMiddleware(15-33)lib/email/send-waitlist-email.ts (1)
sendWaitlistEmail(12-34)schemas/user/roadmap.ts (3)
subscribeToRoadmapInputSchema(4-6)subscribeToRoadmapOutputSchema(13-16)SubscribeToRoadmapOutput(18-20)lib/email/send-roadmap-subscription-email.ts (1)
sendRoadmapSubscriptionEmail(11-32)🪛 dotenv-linter (3.3.0)
.env.example
[warning] 14-14: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 15-15: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 15-15: [UnorderedKey] The LOGO_DEV_TOKEN key should go before the NEXT_PUBLIC_LOGO_DEV_TOKEN key
(UnorderedKey)
[warning] 18-18: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 19-19: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
[warning] 19-19: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 19-19: [UnorderedKey] The MARKETING_SUBSCRIPTION_EMAIL key should go before the RESEND_API_KEY key
(UnorderedKey)
prisma/schema/migrations/20251018210700_added_roadmap_subscription/migration.sql
Show resolved
Hide resolved
| // Input schema for subscribing to roadmap updates | ||
| export const subscribeToRoadmapInputSchema = z.object({ | ||
| email: z.string().min(1, "Email is required").email("Invalid email address"), | ||
| }) | ||
|
|
||
| export type SubscribeToRoadmapInput = z.infer< | ||
| typeof subscribeToRoadmapInputSchema | ||
| > | ||
|
|
||
| // Output schema for roadmap subscription | ||
| export const subscribeToRoadmapOutputSchema = z.object({ | ||
| success: z.boolean(), | ||
| error: z.string().optional(), | ||
| }) | ||
|
|
||
| export type SubscribeToRoadmapOutput = z.infer< | ||
| typeof subscribeToRoadmapOutputSchema | ||
| > |
There was a problem hiding this comment.
🧩 Analysis chain
Possible duplicate schemas with schemas/user/roadmap-subscription.ts — consolidate.
This file appears to duplicate subscribe input/output already defined in schemas/user/roadmap-subscription.ts. Keep a single source of truth (prefer roadmap-subscription.ts), and re-export if you need a different path.
🏁 Script executed:
#!/bin/bash
# List all definitions/usages to confirm duplication
rg -nP 'subscribeToRoadmap(Input|Output)Schema|SubscribeToRoadmap(Input|Output)' -C2Length of output: 3268
Consolidate duplicate schema definitions — remove roadmap.ts or migrate import to roadmap-subscription.ts.
The duplication is confirmed. Both schemas/user/roadmap.ts and schemas/user/roadmap-subscription.ts define identical subscribeToRoadmapInputSchema, subscribeToRoadmapOutputSchema, and their associated types. The codebase currently imports from roadmap.ts (referenced in orpc/routers/user.ts:9-13).
To maintain a single source of truth, either:
- Remove the duplicate definitions from
roadmap.tsand updateorpc/routers/user.tsto import fromroadmap-subscription.ts, or - Remove
roadmap-subscription.tsentirely ifroadmap.tsis the preferred location.
🤖 Prompt for AI Agents
In schemas/user/roadmap.ts around lines 3-20, duplicate schema/type definitions
exist that are already defined in schemas/user/roadmap-subscription.ts; remove
the duplicate definitions from roadmap.ts and update any files that currently
import from roadmap.ts (notably orpc/routers/user.ts lines 9-13) to import the
schemas and types from schemas/user/roadmap-subscription.ts instead, ensuring
named exports match and running a quick search/replace across the repo to update
all imports and then run type checks/tests to confirm no broken imports remain.
There was a problem hiding this comment.
Please checkout this Remove rate-limit-test related
There was a problem hiding this comment.
Please checkout this Remove rate-limit-test related
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (6)
orpc/routers/user.ts (4)
107-157: Replace console.error with structured logging in error handlers.Lines 108, 131, 142, and 152 use
console.errorfor error logging, violating coding guidelines. Error objects may contain sensitive data. Implement structured logging with appropriate redaction.As per coding guidelines
337-340: Replace console.error with structured logging.Line 338 uses
console.error, violating coding guidelines. Implement structured logging for error tracking.As per coding guidelines
2-6: Remove unused moderateRateLimit import.
moderateRateLimitis imported on line 4 but never used in any exported procedure. Remove the unused import to avoid dead code.As per coding guidelines
88-97: Replace console.error with structured logging.Line 95 uses
console.error, which violates coding guidelines for TypeScript files. Error objects may contain PII (email addresses). Implement structured logging with PII redaction.As per coding guidelines
lib/email/send-waitlist-email.ts (1)
25-28: Replace console.error with structured logging.Lines 26 and 32 use
console.error, which violates coding guidelines for TypeScript files. Additionally, error objects may contain PII (email addresses). Implement structured logging (e.g., pino) and ensure sensitive fields are redacted.As per coding guidelines
lib/email/send-roadmap-subscription-email.ts (1)
23-26: Replace console.error with structured logging.Lines 24 and 30 use
console.error, violating coding guidelines for TypeScript files. Error objects may expose PII (email addresses). Implement structured logging with PII redaction.As per coding guidelines
🧹 Nitpick comments (3)
entities/utils/tag/entity.ts (2)
1-2: Use type‑only imports for types.Both imports appear to be types; mark them with
import typeto reduce runtime overhead and align with our TS import rules. As per coding guidelines.-import { TagEntitySimpleDbData } from "@/entities/utils/tag/query" -import { TagSimpleRo } from "@/schemas/utils/tag" +import type { TagEntitySimpleDbData } from "@/entities/utils/tag/query" +import type { TagSimpleRo } from "@/schemas/utils/tag"
5-15: Confirm Simple RO shape and handle potential nullables.
- Do we intentionally exclude timestamps (createdAt/updatedAt) from Simple RO for tags, or should they be included for consistency across entities? Please confirm the project convention.
- If any of
color,userId, orcontainerIdcan be nullable in DB, defensively handle them (optional chaining / defaults) to keep RO stable. Based on learnings.lib/email/send-roadmap-subscription-email.ts (1)
12-32: Consider extracting common email sending logic.This function shares nearly identical structure with
sendWaitlistEmailinlib/email/send-waitlist-email.ts(try/catch pattern, error checking, logging, rethrow). Consider creating a shared helper function that accepts the email parameters to reduce duplication and improve maintainability.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
components/app/marketing-roadmap-item.tsx(1 hunks)components/app/marketing-roadmap-list.tsx(1 hunks)config/converter.tsx(2 hunks)entities/card/card/entity.ts(1 hunks)entities/credential/credential-history/entity.ts(1 hunks)entities/credential/credential-key-value/entity.ts(1 hunks)entities/credential/credential-metadata/entity.ts(1 hunks)entities/credential/credential/entity.ts(1 hunks)entities/encryption/entity.ts(1 hunks)entities/secrets/secret/entity.ts(1 hunks)entities/utils/container/entity.ts(1 hunks)entities/utils/tag/entity.ts(1 hunks)lib/auth/verify.ts(1 hunks)lib/email/send-roadmap-subscription-email.ts(1 hunks)lib/email/send-waitlist-email.ts(1 hunks)lib/utils/index.ts(1 hunks)lib/utils/rate-limit.ts(1 hunks)middleware/rate-limit.ts(1 hunks)orpc/context.ts(1 hunks)orpc/routers/user.ts(5 hunks)
✅ Files skipped from review due to trivial changes (2)
- entities/card/card/entity.ts
- entities/credential/credential-history/entity.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- orpc/context.ts
- middleware/rate-limit.ts
- lib/utils/index.ts
🧰 Additional context used
📓 Path-based instructions (28)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{js,jsx,ts,tsx}: Don't use consecutive spaces in regular expression literals
Don't use the arguments object
Don't use the comma operator
Don't write functions that exceed a given Cognitive Complexity score
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Use for...of statements instead of Array.forEach
Don't create classes that only have static members
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules that don't change anything
Don't use unnecessary escape sequences in regular expression literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports, exports, and destructured assignments to the same name
Don't use unnecessary string or template literal concatenation
Don't use String.raw in template literals when there are no escape sequences
Don't use useless case statements in switch statements
Don't use ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use void operators
Use arrow functions instead of function expressions
Use Date.now() to get milliseconds since the Unix Epoch
Use .flatMap() instead of map().flat() when possible
Use literal property access instead of computed property access
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work
Use concise optional chaining instead of chained logical expressions
Use regular expression literals instead of the RegExp constructor when possible
Don't use number literal object member names that aren't base 10 or use underscore separators
Remove redundant terms from logical expressions
Use while loops instead of for loops when you don't need initializer and update expressions
Don't reassign const variables
Don't use constant expressions in conditions
Don't use Math.min and Math.max to clamp value...
Files:
lib/email/send-waitlist-email.tsentities/credential/credential-key-value/entity.tsentities/secrets/secret/entity.tsentities/credential/credential/entity.tsentities/utils/container/entity.tsconfig/converter.tsxcomponents/app/marketing-roadmap-list.tsxlib/email/send-roadmap-subscription-email.tsentities/encryption/entity.tsorpc/routers/user.tslib/utils/rate-limit.tsentities/credential/credential-metadata/entity.tslib/auth/verify.tscomponents/app/marketing-roadmap-item.tsxentities/utils/tag/entity.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't return a value from a function that has a 'void' return type
Don't use the TypeScript directive @ts-ignore
Make sure switch-case statements are exhaustive
Don't use TypeScript enums
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Use as const instead of literal types and type annotations
Use either T[] or Array consistently
Use consistent accessibility modifiers on class properties and methods
Put default function parameters and optional function parameters last
Initialize each enum member value explicitly
Use export type for types
Use import type for types
Make sure all enum members are literal values
Use function types instead of object types with call signatures
Don't use void type outside of generic or return types
Don't use TypeScript const enum
Don't declare empty interfaces
Don't let variables evolve into any type through reassignments
Don't use the any type
Don't misuse the non-null assertion operator (!) in TypeScript files
Don't use implicit any type on variable declarations
Don't merge interfaces and classes unsafely
Don't use overload signatures that aren't next to each other
Use the namespace keyword instead of the module keyword to declare TypeScript namespacesUse type-only imports when possible
**/*.{ts,tsx}: Use camelCase for function and method names
Use UPPER_SNAKE_CASE for constants
Use camelCase for variables
Use PascalCase for classes and types
Use absolute imports for top-level modules (e.g., @/components, @/lib, @/entities) instead of long relative paths
Follow import order: React imports, third-party, internal abso...
Files:
lib/email/send-waitlist-email.tsentities/credential/credential-key-value/entity.tsentities/secrets/secret/entity.tsentities/credential/credential/entity.tsentities/utils/container/entity.tsconfig/converter.tsxcomponents/app/marketing-roadmap-list.tsxlib/email/send-roadmap-subscription-email.tsentities/encryption/entity.tsorpc/routers/user.tslib/utils/rate-limit.tsentities/credential/credential-metadata/entity.tslib/auth/verify.tscomponents/app/marketing-roadmap-item.tsxentities/utils/tag/entity.ts
**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{js,ts}: Don't use __dirname and __filename in the global scope
Use with { type: "json" } for JSON module imports
Don't access namespace imports dynamically
Use node:assert/strict over node:assert
Use the node: protocol for Node.js builtin modules
Files:
lib/email/send-waitlist-email.tsentities/credential/credential-key-value/entity.tsentities/secrets/secret/entity.tsentities/credential/credential/entity.tsentities/utils/container/entity.tslib/email/send-roadmap-subscription-email.tsentities/encryption/entity.tsorpc/routers/user.tslib/utils/rate-limit.tsentities/credential/credential-metadata/entity.tslib/auth/verify.tsentities/utils/tag/entity.ts
entities/**/entity.ts
📄 CodeRabbit inference engine (.cursor/rules/entities-folder.mdc)
entities/**/entity.ts: Place all database-to-client transformation logic in entity.ts using an entity class with static methods (e.g., getSimpleRo, getRo, helper converters)
Simple Return Objects (RO) must exclude sensitive data and relations; full RO may include relations but must remain server-only
Implement status conversion helpers (e.g., convertStatusToClient or similar) to map database enums to client enums
Ensure consistent date handling in getSimpleRo (e.g., include createdAt, updatedAt, nullable lastViewed)
Map related entities via their corresponding Entity.getSimpleRo methods; avoid passing raw related entities through
Decrypt sensitive data only on the server and never expose raw encrypted payloads; on decryption failure, return safe defaults
Handle null/optional relations defensively (optional chaining, default empty arrays/values) when building ROs
Use Prisma GetPayload with ReturnType of include helpers to define types: EntitySimpleDbData, EntityIncludeDbData, EntityClientSafeDbData
Log decryption failures in a safe manner and return defaults without leaking sensitive dataImplement Entity Transformation Pattern: convert database entities to client-safe Return Objects (ROs) and exclude sensitive fields
Use client-safe entity transformations before returning data to the client
Files:
entities/credential/credential-key-value/entity.tsentities/secrets/secret/entity.tsentities/credential/credential/entity.tsentities/utils/container/entity.tsentities/encryption/entity.tsentities/credential/credential-metadata/entity.tsentities/utils/tag/entity.ts
entities/**/{entity,query,index}.ts
📄 CodeRabbit inference engine (.cursor/rules/project-overview.mdc)
Entity folders must contain files named entity.ts, query.ts, and an index.ts for barrel exports
Follow the Three-File Entity Pattern: each entity must have entity.ts (transforms), query.ts (DB helpers), and index.ts (barrel)
Files:
entities/credential/credential-key-value/entity.tsentities/secrets/secret/entity.tsentities/credential/credential/entity.tsentities/utils/container/entity.tsentities/encryption/entity.tsentities/credential/credential-metadata/entity.tsentities/utils/tag/entity.ts
entities/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Perform Prisma-to-RO transformations and sensitive data encryption/decryption in entities/
Files:
entities/credential/credential-key-value/entity.tsentities/secrets/secret/entity.tsentities/credential/credential/entity.tsentities/utils/container/entity.tsentities/encryption/entity.tsentities/credential/credential-metadata/entity.tsentities/utils/tag/entity.ts
entities/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/database-patterns.mdc)
entities/**/*.{ts,tsx}: Use appropriate include levels to avoid N+1 queries
Select only needed fields in queries for performance
Use client-safe includes for client-facing endpoints
Use offset-based pagination for small datasets
Include total count when needed with paginated results
Use a consistent pagination structure in API responses
Use transactions for multi-step operations that must succeed atomically
Handle rollback on errors within transactions
Keep transactions short to avoid deadlocks
Handle Prisma errors appropriately (e.g., P2002, P2003, P2025)
Provide meaningful error messages for failures
Log errors for debugging while returning safe errors to clients
Convert database entities to client-safe representations (RO)
Exclude sensitive/internal fields (e.g., encryption IDs/keys) from RO outputs
Handle null/optional values gracefully in transformations
Include related entities as needed in RO and transform nested entities consistently
Handle optional relationships safely in transformations
Decrypt sensitive data only on the server and never expose encryption keys
Handle decryption errors gracefully and fall back to safe defaults
Use database-level filtering and limits instead of application-level post-processing
Avoid SELECT * patterns on large tables; select only required fields
Files:
entities/credential/credential-key-value/entity.tsentities/secrets/secret/entity.tsentities/credential/credential/entity.tsentities/utils/container/entity.tsentities/encryption/entity.tsentities/credential/credential-metadata/entity.tsentities/utils/tag/entity.ts
entities/**/{entity.ts,query.ts,index.ts}
📄 CodeRabbit inference engine (.cursor/rules/zero-locker-rules.mdc)
entities/**/{entity.ts,query.ts,index.ts}: Entities use entity.ts for transformations, query.ts for DB helpers, and index.ts for barrel exports
Within each entity folder, keep transformation logic in entity.ts, queries in query.ts, and barrel exports in index.ts
Files:
entities/credential/credential-key-value/entity.tsentities/secrets/secret/entity.tsentities/credential/credential/entity.tsentities/utils/container/entity.tsentities/encryption/entity.tsentities/credential/credential-metadata/entity.tsentities/utils/tag/entity.ts
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{html,jsx,tsx}: Don't use the accessKey attribute on any HTML element
Don't set aria-hidden="true" on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like or
Only use the scope prop on elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assign tabIndex to non-interactive HTML elements
Don't use positive integers for the tabIndex property
Don't include "image", "picture", or "photo" in img alt prop
Don't use an explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
Assign tabIndex to non-interactive HTML elements with aria-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include a type attribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden)
Always include a title attribute for iframe elements
Include caption tracks for audio and video elements
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid ARIA state and property values
Use valid values for the autocomplete attribute on input elements
Use correct ISO language/country codes for the lang attribute
Don't use variables that haven't been declared in the document
Make sure void (self-closing) elements don't have children
Don't use event handlers on non-interactiv...Files:
config/converter.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsx**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{jsx,tsx}: Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress
Accompany onMouseOver/onMouseOut with onFocus/onBlur
Use semantic elements instead of role attributes in JSX
Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't assign to React component props
Don't use dangerous JSX props
Don't use both children and dangerouslySetInnerHTML props on the same element
Use <>...</> instead of ...
Don't add extra closing tags for components without children
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Watch out for possible "wrong" semicolons inside JSX elements
Don't put multiple components in one file; each file must have one componentFiles:
config/converter.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsx**/*.{html,jsx,tsx,css}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
Don't use TailwindCSS class names 'h-NUMBER w-NUMBER'; instead use 'size-NUMBR'
Files:
config/converter.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsxconfig/**/*
📄 CodeRabbit inference engine (.cursor/rules/config-folder.mdc)
Place application configuration files, constants, and utility configurations under the /config directory
Files:
config/converter.tsxconfig/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/config-folder.mdc)
config/**/*.{ts,tsx}: Export configurations as named exports
Use TypeScript for all configuration
Define interfaces for complex configurations
Use const assertions for immutable configurations
Use descriptive names for configuration objectsFiles:
config/converter.tsx**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/zero-locker-patterns.mdc)
**/*.tsx: Use Component Composition Pattern: build complex UI by composing smaller, reusable components (e.g., Form, FormField, FormItem)
Handle form errors with React Hook Form and Zod resolver for client-side validation
Avoid direct database access in React components; use hooks/services instead
**/*.tsx: Use PascalCase for React component names
Use functional components only, typed with explicit props interfaces
Optimize components using React.memo for expensive components, useMemo for heavy computations, and useCallback for stable handlers
**/*.tsx: Never use useState for form state; use React Hook Form (useForm) instead
Always use Zod schemas with zodResolver for form validation
Always use shadcn Form components: Form, FormField, FormItem, FormLabel, FormControl, FormMessage
Always use Icons.spinner with animate-spin for loading states
Never use text like "Deleting..." or "Loading..." for loading states
Use size-4 instead of h-4 w-4 for spinner sizing (Tailwind)
Disable buttons during loading: disabled={isLoading}
Show spinner + text inside buttons when loading
Use Button variant="destructive" for delete actions
Add disabled opacity class to buttons: className="disabled:opacity-50"
Avoid manual form validation; rely on Zod + resolver integrationFiles:
config/converter.tsxcomponents/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsxcomponents/app/**
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
Place application-specific React components under components/app
Files:
components/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsxcomponents/app/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
Use naming pattern {feature}-{purpose}-{type}.tsx for app components
Files:
components/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsxcomponents/app/marketing-*.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
Marketing components in components/app must be prefixed with marketing-
Place marketing components under components/app/ with filenames starting marketing-*.tsx
Files:
components/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsxcomponents/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
components/**/*.{ts,tsx}: Always define a TypeScript interface for component props
Follow established naming patterns consistently across files
Use strict, explicit TypeScript types for safetyFiles:
components/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsxcomponents/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
components/**/*.tsx: Add 'use client' directive for components that use client-side hooks/effects
Use cn() for conditional Tailwind classes
Use semantic Tailwind colors (e.g., text-foreground, bg-background) and consistent spacing (space-y-4, gap-4)
Use responsive Tailwind prefixes (md:, lg:, xl:) for adaptive layouts
Define component variants with cva and defaultVariants when variants are needed
Prefer useState for local state; use useReducer for complex state
Use TanStack Query hooks from /orpc/hooks for server state; prefer server-side data fetching when possible
Implement proper ARIA attributes and keyboard navigation for accessibility
Use React.memo for expensive components
Use useMemo and useCallback for expensive computations and stable callbacks
Lazy-load heavy components via dynamic import
Import only needed icons from lucide-react (no wildcard imports)
Wrap components that might fail in Error Boundaries
Provide loading and error states for async UI
Add JSDoc comments for complex components
components/**/*.tsx: Components must be named in kebab-case.tsx (e.g., dashboard-credential-form.tsx)
Use React Hook Form for form management in UI forms
Use React Hook Form error handling, display validation errors, and handle submission errors gracefully in formsFiles:
components/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsxcomponents/**/[a-z0-9-]*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-overview.mdc)
Name React components in kebab-case with .tsx under the components directory (e.g., dashboard-credential-form.tsx)
Files:
components/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsxcomponents/{app,layout,shared,ui}/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Organize React components under components/app, components/layout, components/shared, and components/ui
Files:
components/app/marketing-roadmap-list.tsxcomponents/app/marketing-roadmap-item.tsxorpc/routers/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/orpc-folder.mdc)
orpc/routers/**/*.ts: In oRPC routers, build procedures using os.$context(), then compose authProcedure and permissionProcedure via .use(...) middleware chaining
Define routes with .input(zodSchema).output(zodSchema).handler(async (...) => { ... }) to enforce schema-validated inputs/outputs
For list endpoints, implement pagination (page, limit), deterministic orderBy, and compute total/hasMore
Apply authMiddleware to protected routes and requirePermission(feature, action) where feature-gated access is needed
Use ORPCError with standard codes (e.g., UNAUTHORIZED, FORBIDDEN, NOT_FOUND, BAD_REQUEST) for predictable error handling
Map known ORM errors (e.g., Prisma P2002) to meaningful ORPCError codes like CONFLICT and fall back to INTERNAL_SERVER_ERROR
Always validate inputs with Zod schemas and define explicit output schemas; handle validation errors gracefully
Never expose sensitive fields in responses; use client-safe entity transformations and selective include projections
Group related procedures in a single router file and use consistent naming for routes and files
Use include helpers (e.g., EntityQuery.getClientSafeInclude()) to fetch only client-safe relations
Use server-side caching or efficient queries where appropriate; prefer pagination over large result sets
Return deterministic ordering for list endpoints (e.g., orderBy createdAt desc)
Provide meaningful error messages when throwing BAD_REQUEST/CONFLICT while avoiding sensitive details
orpc/routers/**/*.ts: Group routes by feature (e.g., credentialRouter, cardRouter) in dedicated router files
Use descriptive, action-indicative procedure names within routers
Use action-based names: create, getById, list, update, delete
Keep procedure names consistent across all routers
Use camelCase for procedure names
Use Zod schemas for all procedure inputs via .input(schema)
List procedures must return metadata (total, hasMore, page, limit) alongside data
Apply authentication middleware to all protecte...Files:
orpc/routers/user.tsorpc/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-overview.mdc)
Ensure consistent error handling and responses within oRPC routes and client code
orpc/**/*.ts: Use oRPC standard error codes (ORPCError) with meaningful, contextual messages
Handle specific error types (e.g., Prisma codes), log errors, and map to appropriate ORPC error codes
Use FORBIDDEN for permission errors; include feature and action in the message and upgrade info when applicableAlways encrypt passwords before storage in server-side handlers/services
Files:
orpc/routers/user.tsorpc/routers/{credential,card,secret,user}.ts
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Define oRPC routers in orpc/routers/ with files credential.ts, card.ts, secret.ts, and user.ts
Files:
orpc/routers/user.tsorpc/routers/**/!(*index).ts
📄 CodeRabbit inference engine (.cursor/rules/orpc-patterns.mdc)
Export routers individually from their own files
Files:
orpc/routers/user.tsorpc/routers/*.ts
📄 CodeRabbit inference engine (.cursor/rules/zero-locker-rules.mdc)
Define route handlers in orpc/routers with one file per domain (e.g., credential.ts, card.ts)
Files:
orpc/routers/user.tslib/utils/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Place general-purpose utilities under lib/utils/
Files:
lib/utils/rate-limit.tslib/auth/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Implement authentication utilities under lib/auth/
Files:
lib/auth/verify.ts🧠 Learnings (16)
📚 Learning: 2025-10-02T22:16:27.157Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/architecture.mdc:0-0 Timestamp: 2025-10-02T22:16:27.157Z Learning: Applies to entities/**/*.ts : Perform Prisma-to-RO transformations and sensitive data encryption/decryption in entities/Applied to files:
entities/secrets/secret/entity.tsentities/credential/credential/entity.tsentities/encryption/entity.ts📚 Learning: 2025-10-02T22:21:11.735Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/zero-locker-rules.mdc:0-0 Timestamp: 2025-10-02T22:21:11.735Z Learning: Applies to entities/**/{entity.ts,query.ts,index.ts} : Entities use entity.ts for transformations, query.ts for DB helpers, and index.ts for barrel exportsApplied to files:
entities/secrets/secret/entity.ts📚 Learning: 2025-10-02T20:57:51.336Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/entities-folder.mdc:0-0 Timestamp: 2025-10-02T20:57:51.336Z Learning: Applies to entities/**/index.ts : Barrel export from index.ts using export * from "./entity" and export * from "./query"Applied to files:
entities/secrets/secret/entity.ts📚 Learning: 2025-10-02T20:57:51.336Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/entities-folder.mdc:0-0 Timestamp: 2025-10-02T20:57:51.336Z Learning: Applies to entities/**/entity.ts : Place all database-to-client transformation logic in entity.ts using an entity class with static methods (e.g., getSimpleRo, getRo, helper converters)Applied to files:
entities/secrets/secret/entity.tsentities/utils/container/entity.tsentities/encryption/entity.tsentities/utils/tag/entity.ts📚 Learning: 2025-10-02T20:57:51.336Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/entities-folder.mdc:0-0 Timestamp: 2025-10-02T20:57:51.336Z Learning: Applies to entities/**/entity.ts : Simple Return Objects (RO) must exclude sensitive data and relations; full RO may include relations but must remain server-onlyApplied to files:
entities/secrets/secret/entity.ts📚 Learning: 2025-10-02T20:57:51.336Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/entities-folder.mdc:0-0 Timestamp: 2025-10-02T20:57:51.336Z Learning: Applies to entities/**/entity.ts : Map related entities via their corresponding Entity.getSimpleRo methods; avoid passing raw related entities throughApplied to files:
entities/secrets/secret/entity.tsentities/utils/tag/entity.ts📚 Learning: 2025-10-02T22:19:32.110Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/zero-locker-patterns.mdc:0-0 Timestamp: 2025-10-02T22:19:32.110Z Learning: Applies to entities/**/entity.ts : Implement Entity Transformation Pattern: convert database entities to client-safe Return Objects (ROs) and exclude sensitive fieldsApplied to files:
entities/secrets/secret/entity.ts📚 Learning: 2025-10-02T22:17:55.478Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/database-patterns.mdc:0-0 Timestamp: 2025-10-02T22:17:55.478Z Learning: Applies to entities/**/*.{ts,tsx} : Convert database entities to client-safe representations (RO)Applied to files:
entities/secrets/secret/entity.ts📚 Learning: 2025-10-02T20:57:51.336Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/entities-folder.mdc:0-0 Timestamp: 2025-10-02T20:57:51.336Z Learning: Applies to entities/**/entity.ts : Decrypt sensitive data only on the server and never expose raw encrypted payloads; on decryption failure, return safe defaultsApplied to files:
entities/credential/credential/entity.tsentities/encryption/entity.ts📚 Learning: 2025-10-02T20:57:51.336Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/entities-folder.mdc:0-0 Timestamp: 2025-10-02T20:57:51.336Z Learning: Applies to entities/**/entity.ts : Log decryption failures in a safe manner and return defaults without leaking sensitive dataApplied to files:
entities/credential/credential/entity.ts📚 Learning: 2025-10-02T22:17:55.478Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/database-patterns.mdc:0-0 Timestamp: 2025-10-02T22:17:55.478Z Learning: Applies to prisma/schema.prisma : Separate encryption storage (e.g., EncryptedData) from main entitiesApplied to files:
entities/encryption/entity.ts📚 Learning: 2025-10-02T22:17:55.478Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/database-patterns.mdc:0-0 Timestamp: 2025-10-02T22:17:55.478Z Learning: Applies to prisma/schema.prisma : Use a consistent encryption structure across entitiesApplied to files:
entities/encryption/entity.ts📚 Learning: 2025-10-02T21:03:01.338Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/prisma-folder.mdc:0-0 Timestamp: 2025-10-02T21:03:01.338Z Learning: Applies to prisma/schema/encryption.prisma : Model encrypted values using EncryptedData with fields (encryptedValue, iv, algorithm default AES_256_GCM, encryptionKey) and relations/indexes as shownApplied to files:
entities/encryption/entity.ts📚 Learning: 2025-10-02T20:57:51.336Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/entities-folder.mdc:0-0 Timestamp: 2025-10-02T20:57:51.336Z Learning: Applies to entities/**/entity.ts : Use Prisma GetPayload with ReturnType of include helpers to define types: EntitySimpleDbData, EntityIncludeDbData, EntityClientSafeDbDataApplied to files:
entities/encryption/entity.ts📚 Learning: 2025-10-02T20:56:15.975Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/components-folder.mdc:0-0 Timestamp: 2025-10-02T20:56:15.975Z Learning: Applies to components/**/*.tsx : Use cn() for conditional Tailwind classesApplied to files:
components/app/marketing-roadmap-item.tsx📚 Learning: 2025-10-02T21:00:01.595Z
Learnt from: CR PR: FindMalek/zero-locker#0 File: .cursor/rules/lib-folder.mdc:0-0 Timestamp: 2025-10-02T21:00:01.595Z Learning: Applies to lib/utils/cn.ts : Provide a cn utility in lib/utils/cn.ts that wraps clsx with tailwind-merge: export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) }Applied to files:
components/app/marketing-roadmap-item.tsx🧬 Code graph analysis (6)
lib/email/send-waitlist-email.ts (4)
lib/email/resend-client.ts (1)
resendClient(5-5)env.ts (1)
env(4-27)config/site.ts (1)
siteConfig(3-41)components/app/email-waitlist.tsx (1)
EmailWaitlist(25-90)config/converter.tsx (1)
types/index.d.ts (1)
RoadmapStatus(59-59)components/app/marketing-roadmap-list.tsx (3)
types/index.d.ts (1)
RoadmapItem(61-65)components/app/marketing-roadmap-item.tsx (1)
MarketingRoadmapItem(6-27)components/shared/icons.tsx (1)
Icons(139-262)lib/email/send-roadmap-subscription-email.ts (4)
lib/email/resend-client.ts (1)
resendClient(5-5)env.ts (1)
env(4-27)config/site.ts (1)
siteConfig(3-41)components/app/email-roadmap-subscription.tsx (1)
EmailRoadmapSubscription(24-106)orpc/routers/user.ts (6)
orpc/types.ts (1)
ORPCContext(14-28)middleware/rate-limit.ts (2)
lenientRateLimit(95-100)strictRateLimit(73-78)middleware/auth.ts (1)
authMiddleware(15-33)lib/email/send-waitlist-email.ts (1)
sendWaitlistEmail(13-35)schemas/user/roadmap.ts (3)
subscribeToRoadmapInputSchema(4-6)subscribeToRoadmapOutputSchema(13-16)SubscribeToRoadmapOutput(18-20)lib/email/send-roadmap-subscription-email.ts (1)
sendRoadmapSubscriptionEmail(12-33)components/app/marketing-roadmap-item.tsx (2)
types/index.d.ts (1)
RoadmapItem(61-65)config/converter.tsx (1)
roadmapStatusConfig(67-86)🔇 Additional comments (11)
entities/credential/credential-key-value/entity.ts (1)
1-4: LGTM: Import path refactored to absolute alias.The change from relative to absolute import path is correct and aligns with the project's module resolution pattern.
entities/credential/credential-metadata/entity.ts (1)
1-1: LGTM: Import path refactored to absolute alias.The change from relative to absolute import path is correct and consistent with the broader refactor.
entities/secrets/secret/entity.ts (1)
1-1: LGTM: Import path refactored to absolute alias.The change from relative to absolute import path is correct and maintains consistency across entity modules.
lib/auth/verify.ts (1)
5-5: LGTM: Import path refactored to absolute alias.The change from relative to absolute import path is correct and aligns with the project-wide import standardization.
entities/encryption/entity.ts (1)
1-1: LGTM: Import path refactored to absolute alias.The change from relative to absolute import path is correct and consistent with the encryption entity module pattern.
entities/credential/credential/entity.ts (1)
1-4: LGTM: Import path refactored to absolute alias.The change from relative to absolute import path is correct and aligns with the credential entity module pattern. All class methods and logic remain unchanged.
entities/utils/container/entity.ts (1)
1-1: LGTM! Import standardization improves maintainability.The change from a relative import to an absolute alias import follows the coding guidelines and aligns with the broader refactoring effort to standardize module resolution across the codebase.
entities/utils/tag/entity.ts (1)
1-1: No action required. The Three-File Entity Pattern is correctly implemented.The entity triad exists: query.ts, index.ts, and entity.ts are all present in entities/utils/tag/. The "@/*" alias is configured in tsconfig.json with proper Next.js path resolution. The code follows the established entity structure guidelines without issues.
Likely an incorrect or invalid review comment.
components/app/marketing-roadmap-item.tsx (1)
1-27: LGTM! Past review concern addressed.The component now correctly uses the
cn()utility for conditional className composition (lines 16, 21) instead of template literals. The implementation is clean, type-safe, and follows component guidelines.config/converter.tsx (1)
67-86: LGTM! Well-structured status configuration.The roadmap status configuration is properly typed, uses semantic Tailwind colors (including
text-muted-foreground), and provides clear labels for each status. The structure integrates cleanly with the component layer.lib/utils/rate-limit.ts (1)
1-322: LGTM! All past review concerns addressed.The implementation has successfully addressed all previous review feedback:
- Cross-runtime interval typing with
ReturnType<typeof setInterval>(line 90)- Event loop management with
unref()(line 112)- Precise expiry boundaries with
<= now(lines 119, 129, 230)- Exported public interfaces for
RateLimitStoreandRateLimitStats(lines 69-82)- Input validation for
maxRequestsandwindowSeconds(lines 194-205)The rate limiting utility is well-structured, type-safe, and production-ready.
| {items.map((item, index) => ( | ||
| <MarketingRoadmapItem | ||
| key={`${item.title}-${index}`} | ||
| title={item.title} | ||
| description={item.description} | ||
| status={item.status} | ||
| /> | ||
| ))} |
There was a problem hiding this comment.
Avoid using array index in key prop.
Line 43 uses array index in the key prop (key={${item.title}-${index}}). Per coding guidelines, avoid using array indices in keys as they can cause issues with React reconciliation if items are reordered or filtered. If a stable unique identifier is available on RoadmapItem, use that instead.
As per coding guidelines
🤖 Prompt for AI Agents
In components/app/marketing-roadmap-list.tsx around lines 41 to 48, the key prop
currently uses the array index (`${item.title}-${index}`) which can break React
reconciliation; replace the index-based key with a stable unique identifier from
the RoadmapItem (e.g., `item.id` or `item.slug`) — if the model lacks such a
field, add a stable unique ID to each item at the source (or compute a stable
slug from the title) and update the prop to use that unique value as the key.
| <div | ||
| className={`from-background via-background/50 pointer-events-none absolute bottom-0 left-0 right-0 h-32 rounded-b-lg bg-gradient-to-t to-transparent transition-opacity duration-500 ${ | ||
| showScrollIndicator ? "opacity-100" : "opacity-0" | ||
| }`} | ||
| /> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use cn() utility for conditional className composition.
Lines 53-55 compose className using template literals. Per coding guidelines and retrieved learnings, use the cn() utility for conditional Tailwind classes to ensure proper class merging and deduplication.
Apply this diff:
+import { cn } from "@/lib/utils"
+
interface MarketingRoadmapListProps {
items: RoadmapItem[]
} {/* Bottom blur gradient */}
<div
- className={`from-background via-background/50 pointer-events-none absolute bottom-0 left-0 right-0 h-32 rounded-b-lg bg-gradient-to-t to-transparent transition-opacity duration-500 ${
- showScrollIndicator ? "opacity-100" : "opacity-0"
- }`}
+ className={cn(
+ "from-background via-background/50 pointer-events-none absolute bottom-0 left-0 right-0 h-32 rounded-b-lg bg-gradient-to-t to-transparent transition-opacity duration-500",
+ showScrollIndicator ? "opacity-100" : "opacity-0"
+ )}
/>As per coding guidelines
Based on learnings
🤖 Prompt for AI Agents
In components/app/marketing-roadmap-list.tsx around lines 52 to 56, replace the
template-literal className with the cn() utility: pass the static Tailwind
classes as string arguments and the conditional opacity class as an object or
conditional argument (e.g., { "opacity-100": showScrollIndicator, "opacity-0":
!showScrollIndicator }) so classes are merged/deduped correctly; also ensure cn
is imported from your utilities at the top of the file if not already.
| <div | ||
| className={`pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-500 ${ | ||
| showScrollIndicator ? "opacity-100" : "opacity-0" | ||
| }`} | ||
| > | ||
| <Icons.down className="text-muted-foreground size-4" /> | ||
| </div> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use cn() utility for conditional className composition.
Lines 60-62 compose className using template literals. Use the cn() utility for conditional Tailwind classes.
Apply this diff:
{/* Bouncing scroll indicator */}
<div
- className={`pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-500 ${
- showScrollIndicator ? "opacity-100" : "opacity-0"
- }`}
+ className={cn(
+ "pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-500",
+ showScrollIndicator ? "opacity-100" : "opacity-0"
+ )}
>As per coding guidelines
Based on learnings
📝 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.
| <div | |
| className={`pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-500 ${ | |
| showScrollIndicator ? "opacity-100" : "opacity-0" | |
| }`} | |
| > | |
| <Icons.down className="text-muted-foreground size-4" /> | |
| </div> | |
| <div | |
| className={cn( | |
| "pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-500", | |
| showScrollIndicator ? "opacity-100" : "opacity-0" | |
| )} | |
| > | |
| <Icons.down className="text-muted-foreground size-4" /> | |
| </div> |
🤖 Prompt for AI Agents
components/app/marketing-roadmap-list.tsx lines 59-65: the className is built
via a template literal; replace it with the cn() utility so Tailwind conditional
classes are composed consistently. Import cn if it's not already imported, then
call cn("pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2
animate-bounce transition-opacity duration-500", showScrollIndicator ?
"opacity-100" : "opacity-0") to preserve all static classes and the conditional
opacity class.
| // Send subscription confirmation email | ||
| try { | ||
| await sendRoadmapSubscriptionEmail({ | ||
| to: input.email, | ||
| }) | ||
| } catch (emailError) { | ||
| console.error("Failed to send subscription email:", emailError) | ||
| // Don't fail the request if email fails | ||
| } | ||
|
|
||
| return { success: true } | ||
| } catch (error) { | ||
| // Re-throw ORPC errors to let ORPC handle them | ||
| if (error instanceof ORPCError) { | ||
| throw error | ||
| } | ||
|
|
||
| // Handle Prisma-specific errors | ||
| if (error instanceof Prisma.PrismaClientKnownRequestError) { | ||
| console.error("Database constraint error subscribing to roadmap:", { | ||
| code: error.code, | ||
| message: error.message, | ||
| meta: error.meta, | ||
| }) | ||
|
|
||
| // Handle unique constraint violations | ||
| if (error.code === "P2002") { | ||
| return { | ||
| success: false, | ||
| error: "Email is already subscribed", | ||
| } | ||
| } | ||
|
|
||
| // Handle other known Prisma errors | ||
| return { | ||
| success: false, | ||
| error: "Database constraint violation occurred", | ||
| } | ||
| } | ||
|
|
||
| // Handle Prisma client errors (connection issues, etc.) | ||
| if (error instanceof Prisma.PrismaClientUnknownRequestError) { | ||
| console.error("Unknown Prisma error subscribing to roadmap:", { | ||
| message: error.message, | ||
| }) | ||
| return { | ||
| success: false, | ||
| error: "Database connection issue occurred", | ||
| } | ||
| } | ||
|
|
||
| // Handle Prisma validation errors | ||
| if (error instanceof Prisma.PrismaClientValidationError) { | ||
| console.error("Prisma validation error subscribing to roadmap:", { | ||
| message: error.message, | ||
| }) | ||
| return { | ||
| success: false, | ||
| error: "Invalid data provided", | ||
| } | ||
| } | ||
|
|
||
| // Handle unexpected errors | ||
| console.error("Unexpected error subscribing to roadmap:", error) | ||
| return { | ||
| success: false, | ||
| error: "An unexpected error occurred. Please try again later.", | ||
| } | ||
| } | ||
| }) |
There was a problem hiding this comment.
Replace console.error with structured logging.
Lines 243, 256, 279, 290, and 300 use console.error for logging, violating coding guidelines for TypeScript files. Error contexts may expose PII (email addresses). Implement structured logging with PII redaction throughout this handler.
As per coding guidelines
Summary by CodeRabbit
New Features
Chores
Documentation