Skip to content

refactor: replace individual Async*MultiSelect components with consolidated Async*FilterSelect components and adjust query logic for filters#375

Merged
Producdevity merged 35 commits into
stagingfrom
perf-device-listing-queries
Jun 2, 2026
Merged

refactor: replace individual Async*MultiSelect components with consolidated Async*FilterSelect components and adjust query logic for filters#375
Producdevity merged 35 commits into
stagingfrom
perf-device-listing-queries

Conversation

@Producdevity

@Producdevity Producdevity commented May 28, 2026

Copy link
Copy Markdown
Owner

What changed

This PR reduces the amount of work the app does for report list filters and fixes a production submit path that could time out with the current one-connection Prisma pool.

Main changes:

  • Added Prisma indexes for the listing and PC report query patterns we use most often: status/date plus hardware, game, emulator, and author filters.
  • Added slim .options endpoints for Devices, SoCs, CPUs, and GPUs so filters/selectors do not need to fetch full admin list payloads with counts and relations.
  • Moved the listings and PC listings hardware filters onto async option loading, with selected chips preserved from selectedByIds.
  • Cleaned up the duplicate async multi-select files and kept the shared implementation under src/components/ui/form/async-multi-select.
  • Fixed the dropdown shell behavior for the async select and aligned the regular MultiSelect shell where it was causing layout overlap.
  • Invalidated the new option-query caches after hardware and brand admin CRUD, so long-lived filter data does not stay stale after edits.
  • Hardened public runtime env flags so browser behavior is driven by explicit NEXT_PUBLIC_* values instead of server-only env inference.
  • Forced analytics, Ko-fi, Sentry, Vercel Analytics, and the cookie banner off in Playwright web server runs.
  • Seeded missing custom field categories and representative custom field values for Eden, GameNative, and Azahar, and added more PC report seed data.
  • Fixed handheld report creation with the current Prisma pool setup by making the auto-approval trust check use the active transaction client instead of trying to acquire a second connection from a one-connection pool.

Why

The old filter paths were doing too much work for simple option feeds, and several of those results were being cached for a long time without matching invalidation. That made the app slower and made admin changes look stale.

The submit timeout fix is separate but important: production can hit timeout exceeded when trying to connect when a transaction holds the only pool connection and code inside that transaction tries to query through the root Prisma client. The create path now keeps that read on the transaction client.

Testing

  • pnpm types
  • pnpm lint - passes with the existing warning set
  • pnpm test - 1801 passed, 4 skipped
  • pnpm test:e2e:ci - completed with 274 passed, 2 skipped, 1 flaky retry in tests/error-handling.spec.ts that passed on retry
  • Local Prisma migration and seed run against the local database

Notes

The Prisma migration in this PR should be reviewed as a normal schema migration. I recreated it after cleaning up the local drift from the temporary platforms branch work.

Summary by CodeRabbit

  • New Features

    • Added configurable feature toggles for analytics, Ko-fi widget, and Sentry integration.
    • Introduced improved async filter components for devices and hardware selection with incremental loading and search.
    • Added human verification support for game creation and submissions.
    • PC listings now support custom fields and improved filtering.
  • Performance

    • Added database indexes for faster listing queries.
    • Improved dropdown positioning logic in filters.
  • Bug Fixes

    • Fixed environment-based feature gating for telemetry services.
    • Enhanced Turnstile hostname validation for human verification.

@vercel

vercel Bot commented May 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
emuready Ready Ready Preview, Comment Jun 2, 2026 11:11am

Request Review

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Unifies env flags and Sentry gating; adds async options endpoints and UI; updates repositories, pagination, and schemas; enhances human verification and spam detection; adds DB indexes and seeders; revises analytics, CORS, proxy, and tests/CI/tooling.

Changes

Env, Sentry, async options, repositories, HV, and data seeding

Layer / File(s) Summary
Env unification and Sentry gating
.env*.example, src/lib/env.ts, next.config.ts, sentry.*.config.ts, src/instrumentation-client.*, playwright.config.ts, src/lib/cors.*, src/app/api/proxy-image/route.ts
Adds APP_ENV and feature flags, gates Sentry via ENABLE_SENTRY across Next config/server/edge/client, updates analytics gating, CORS/proxy logic, and Playwright env.
Async options APIs and UI
src/schemas/*, src/server/api/routers/*, src/server/repositories/*, src/components/ui/form/async-multi-select/*, src/app/*/filters/*, src/app/*/*Page.tsx
Introduces Get*Options schemas, options endpoints for devices/cpus/gpus/socs, repository option queries with hasMore, AsyncMultiSelect refactor/tests, and pages/components switching to async options with caching.
Human verification and spam checks
src/features/human-verification/*, src/server/utils/spam-check*, src/server/utils/spamDetection*, src/app/games/new*/**, src/server/api/routers/games*
Expands Turnstile config/hostname validation, integrates HV into game creation flows, extends spam detection to games and rate limits, adjusts tests and error-cause helpers.
DB indexes and seed data
prisma/migrations/*, prisma/schema.prisma, prisma/seed*.ts, prisma/seeders/*
Adds listing/pc_listings composite indexes, seeds pcListings and custom field categories/values, adds options-related utilities and fixtures.
UI/Tooling refinements
src/components/ui/*, src/app/layout.tsx, src/components/footer/*, .github/workflows/*, .husky/*, eslint.config.mjs, tests/*
Refines MultiSelect/Autocomplete, removes beta badge, updates analytics render and third-party mocks, stabilizes E2E helpers, disables mutation retries, and updates Husky/ESLint.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant TRPC as TRPC options endpoint
  participant Repo
  participant DB
  Client->>TRPC: gpus.options({ search, limit, offset })
  TRPC->>Repo: options(filters)
  Repo->>DB: findMany(select: option, orderBy, skip/take)
  DB-->>Repo: results (+extra row)
  Repo-->>TRPC: { gpus, hasMore }
  TRPC-->>Client: { gpus, hasMore }
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Possibly related PRs

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf-device-listing-queries
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch perf-device-listing-queries

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (8)
src/app/admin/devices/components/DeviceModal.tsx (1)

23-24: 🏗️ Heavy lift

Replace the 1000-item preload with real async lookup.

Line 24 still preloads a large SoC list in the modal path, which can degrade open-time performance as data grows. Implement a search-driven async fetch instead of bulk loading and remove the TODO once done.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/admin/devices/components/DeviceModal.tsx` around lines 23 - 24, The
modal currently preloads 1000 SoC options via the call to
api.socs.options.useQuery({ limit: 1000 }) (socsQuery) which must be replaced
with a search-driven async fetch: remove the hardcoded limit call, add a
debounced search state in DeviceModal and call api.socs.options.useQuery or an
equivalent endpoint with the search term (e.g., { q: searchTerm, limit: 50 })
when the user types, wire those results into the select component (or use an
AsyncSelect pattern) to load options on demand, ensure loading/error states are
handled, and remove the TODO comment once complete.
sentry.edge.config.ts (1)

8-20: ⚡ Quick win

Trim explanatory inline comments in this block.

Keep only comments that capture non-obvious external constraints or business invariants.

Proposed cleanup
-// Only enable Sentry when explicitly configured for this deployment.
 if (process.env.NEXT_PUBLIC_ENABLE_SENTRY === 'true') {
   Sentry.init({
     dsn: 'https://85ca585e45005d8786e361c3456518bf@o74828.ingest.us.sentry.io/4509717207318529',

-    // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
     tracesSampleRate: 1,

-    // Enable logs to be sent to Sentry
     enableLogs: true,

-    // Setting this option to true will print useful information to the console while you're setting up Sentry.
     debug: false,
   })
 }
As per coding guidelines `**/*.{ts,tsx,js,jsx}`: Comments should be rare, factual, and explain only non-obvious external constraints or business invariants.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sentry.edge.config.ts` around lines 8 - 20, This block contains excessive
explanatory inline comments around the Sentry initialization; trim comments to
only those that convey non-obvious external constraints or business invariants.
In the if (process.env.NEXT_PUBLIC_ENABLE_SENTRY === 'true') { ... } /
Sentry.init({...}) section, remove or shorten comments for obvious configuration
keys (dsn, tracesSampleRate, enableLogs, debug) and keep a single factual
comment only if there is an external requirement (e.g., a deployment-level
mandate about NEXT_PUBLIC_ENABLE_SENTRY or mandatory DSN handling). Ensure
remaining comments are concise and factual.
src/app/pc-listings/components/filters/AsyncGpuFilterSelect.test.tsx (1)

68-84: ⚡ Quick win

Reduce non-mock inline string literals in this test.

Move assertion/label strings to fixture-derived constants so literals are limited to mocking data.

Proposed refactor
+const FILTER_LABEL = 'GPUs'
+const SELECTED_GPU = { id: 'gpu-selected', modelName: 'Radeon RX 7800 XT', brandName: 'AMD' }
+const OPTION_GPU = { id: 'gpu-1', modelName: 'RTX 4070', brandName: 'NVIDIA' }

 it('maps GPU option and selected labels', () => {
-  render(<AsyncGpuFilterSelect label="GPUs" value={['gpu-selected']} onChange={vi.fn()} />)
+  render(<AsyncGpuFilterSelect label={FILTER_LABEL} value={[SELECTED_GPU.id]} onChange={vi.fn()} />)

-  expect(screen.getByText('AMD Radeon RX 7800 XT')).toBeInTheDocument()
-  fireEvent.click(screen.getByRole('button', { name: 'GPUs multi-select' }))
-  expect(screen.getByText('NVIDIA RTX 4070')).toBeInTheDocument()
+  expect(
+    screen.getByText(`${SELECTED_GPU.brandName} ${SELECTED_GPU.modelName}`),
+  ).toBeInTheDocument()
+  fireEvent.click(screen.getByRole('button', { name: `${FILTER_LABEL} multi-select` }))
+  expect(screen.getByText(`${OPTION_GPU.brandName} ${OPTION_GPU.modelName}`)).toBeInTheDocument()
 })
As per coding guidelines `**/*.{test,spec}.{ts,tsx}`: Tests may use string literals only when mocking requires it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/pc-listings/components/filters/AsyncGpuFilterSelect.test.tsx` around
lines 68 - 84, The test in AsyncGpuFilterSelect uses inline string literals for
labels and selected values; replace them with constants sourced from your test
fixtures/mock data instead of hard-coded strings. Update the test to import the
fixture constants (e.g., GPU_LABEL, GPU_OPTION_LABEL_NVIDIA,
GPU_OPTION_LABEL_AMD, GPU_SELECTED_ID) from the module used by setupApiMocks or
the API mock file, then use those constants in render(<AsyncGpuFilterSelect
label={GPU_LABEL} value={[GPU_SELECTED_ID]} .../>), in the screen.getByText
assertions, and in fireEvent.click(screen.getByRole('button', { name:
`${GPU_LABEL} multi-select` })); ensure setupApiMocks still provides the mock
data keys that the constants reference.
src/app/pc-listings/components/filters/AsyncCpuFilterSelect.test.tsx (1)

80-85: ⚡ Quick win

Reduce non-mock string literals in the test body.

Lines 80-85 use hard-coded UI strings in test title/props/assertions. Please switch to shared constants/test fixtures so literals remain only where mocking requires them.

As per coding guidelines "Tests may use string literals only when mocking requires it".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/pc-listings/components/filters/AsyncCpuFilterSelect.test.tsx` around
lines 80 - 85, The test uses hard-coded UI strings; replace those literals with
shared test constants/fixtures: import canonical labels and option text
constants (e.g., CPU_LABEL, CPU_MULTI_SELECT_NAME, CPU_OPTION_INTEL,
CPU_OPTION_AMD or equivalent) and use them in the it() title, the render label
prop for AsyncCpuFilterSelect, and the assertions that call screen.getByText /
getByRole; update references to AsyncCpuFilterSelect, the test case name,
render(...) and the two expect(...) calls to use those imported constants so
strings only remain in mocks/fixtures.
src/components/popups/StopKillingGamesPopup.tsx (1)

12-12: ⚡ Quick win

Remove or replace the non-actionable TODO comment.

Line 12 adds a vague TODO that does not document a concrete invariant/constraint. Please remove it or replace it with a specific tracked action.

As per coding guidelines "Comments should be rare, factual, and explain only non-obvious external constraints or business invariants".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/popups/StopKillingGamesPopup.tsx` at line 12, Remove the vague
TODO on line 12 in the StopKillingGamesPopup component: either delete the
non-actionable comment or replace it with a specific, factual note (e.g., a
one-line explanation of the invariant or a reference to a tracking ticket/PR) so
the comment documents a concrete constraint or next step; update the
StopKillingGamesPopup.tsx file's comment near the component declaration
accordingly.
src/server/api/routers/socs.ts (1)

22-22: ⚡ Quick win

Remove the TODO note or convert it into a tracked issue.

Inline TODOs here add noise without documenting a business invariant or external constraint.

As per coding guidelines, “Comments should be rare, factual, and explain only non-obvious external constraints or business invariants.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/api/routers/socs.ts` at line 22, Remove the inline comment "//
TODO: use paginate helpers" from src/server/api/routers/socs.ts; either delete
the TODO entirely or replace it with a short factual comment linking to a
created issue/PR (e.g., "See ISSUE-123 for paginate helper work") so the intent
is tracked externally rather than left as inline noise. Locate the exact TODO
string in the file and apply the change, ensuring no other logic is altered.
src/lib/cors.test.ts (1)

5-123: ⚡ Quick win

Replace repeated raw literals with named test constants.

This test now relies on many direct string literals outside mocking paths; move env keys/URLs/app-env values into constants to align with test conventions.

As per coding guidelines, “Tests may use string literals only when mocking requires it.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/cors.test.ts` around lines 5 - 123, The tests use many repeated
string literals; define named test constants (e.g., ALLOWED_ORIGIN_EMUREADY =
'https://emuready.com', LOCALHOST_URL = 'http://localhost:3000', CI_ENV =
'true', PROD_ENV = 'production', TEST_APP_ENV = 'test') at the top of the file
and replace raw usages in calls to loadCors(), expectations for
getOriginFromUrl, getAllowedOrigins, and isAllowedRequestOrigin (and any
references to allowedOrigins or source strings) with those constants so the env
keys/URLs/app-env values are centralized and follow the test convention.
src/components/ui/form/async-multi-select/AsyncMultiSelect.test.tsx (1)

41-353: 🏗️ Heavy lift

Extract repeated UI query strings into named constants/fixtures.

This suite hardcodes many query strings (labels/placeholders/button names). Centralizing them in shared constants keeps tests resilient and aligns with repository test rules.

As per coding guidelines, "**/*.{test,spec}.{ts,tsx}: Tests may use string literals only when mocking requires it".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/form/async-multi-select/AsyncMultiSelect.test.tsx` around
lines 41 - 353, The tests hardcode many UI query strings (e.g., 'Devices
multi-select', 'Search devices...', 'Clear search', 'Retroid Pocket 5', 'AYN
Odin 2', 'Steam Deck OLED', etc.) which should be centralized; update the
AsyncMultiSelect.test.tsx to import and use a shared test-strings fixture (e.g.,
names like SELECT_BUTTON_LABEL, SEARCH_PLACEHOLDER, CLEAR_SEARCH_LABEL,
OPTION_RETROID_POCKET, OPTION_AYN_ODIN, SELECTED_STEAM_DECK) instead of literal
strings, replace all occurrences in the suite (including usages in renderSelect
calls and screen.getByRole/getByText/getByPlaceholderText assertions) and
add/update the fixture file exporting these constants so tests remain resilient
and follow the test string guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@prisma/seeders/pcListingsSeeder.ts`:
- Around line 1-2: Reorder the imports so external package imports come before
project-alias imports to satisfy ESLint import/order: move the line importing
ApprovalStatus, PcOs, Role, and PrismaClient from '`@orm/client`' above the line
importing calculateWilsonScore from '`@/utils/wilson-score`'. Ensure the symbols
ApprovalStatus, PcOs, Role, and PrismaClient remain imported from '`@orm/client`'
and calculateWilsonScore stays from '`@/utils/wilson-score`'.

In `@src/app/listings/components/filters/AsyncSocFilterSelect.test.tsx`:
- Around line 79-83: The test for AsyncSocFilterSelect is asserting UI text with
inline string literals ("MediaTek Dimensity 1100", "Qualcomm Snapdragon 8 Gen
2"), which violates the rule against non-mocking literals; update the test to
assert against values derived from your mocked fixture data instead (e.g. import
and use the mock SoC names/IDs used by the AsyncSocFilterSelect mock provider or
fixture object) so that render(<AsyncSocFilterSelect ... />) and subsequent
expect(...) calls reference the fixture constants rather than hardcoded strings;
locate references in this file to AsyncSocFilterSelect, the render call, and the
two expect(...) assertions and replace the literals with the corresponding
mocked fixture variables.

In `@src/app/pc-listings/new/NewPcListingPage.tsx`:
- Line 35: The import ordering is incorrect: move the named import "PcOs" from
"`@orm`" so it appears in the correct group/order before the "next/link" import in
NewPcListingPage.tsx; update the import list so "`@orm`" imports (PcOs) are
grouped with other external/module imports in the proper order expected by the
lint rule (import/order) to satisfy CI.

In `@src/app/profile/components/PcPresetModal.tsx`:
- Around line 3-10: The import order is incorrect for linting: move the named
import PcOs from '`@orm`' so it appears before the React imports (useCallback,
useState, useEffect, FormEvent) and other third-party imports; update the
top-of-file import sequence that currently includes "import { PcOs } from
'`@orm`'" and "import { useCallback, useState, useEffect, type FormEvent } from
'react'" so that PcOs/@orm is imported earlier to satisfy the project's
import-order rule.

In `@src/lib/env.test.ts`:
- Around line 21-76: The tests use many inline env key/value strings and
assertions (e.g., calls to loadEnv with NODE_ENV, NEXT_PUBLIC_APP_ENV,
NEXT_PUBLIC_ENABLE_ANALYTICS, NEXT_PUBLIC_ENABLE_KOFI_WIDGET,
NEXT_PUBLIC_ENABLE_SENTRY and assertions against env.APP_ENV, env.ENABLE_*);
refactor by introducing shared test constants/fixtures (e.g., ENV_KEYS and
ENV_FIXTURES or TEST_ENV_VALUES) exported from a test helper module and replace
the inline literals in these cases with those constants when calling loadEnv and
when asserting env properties so all env keys/expected values are centrally
maintained and reused across tests.

In `@src/lib/trust/service.test.ts`:
- Line 41: Remove all "as never" casts in src/lib/trust/service.test.ts and
replace them by passing a properly typed mock Prisma context that matches the
service's dependency shape: update the prismaCtx and prismaWithTx variables to
the exact interface expected by canUserAutoApprove and TrustService (use the
existing MockPrismaCtx type or create a narrowed mock interface), then call
canUserAutoApprove('user-1', prismaCtx) and construct new
TrustService(prismaWithTx, ...) with those typed mocks (ensure the mock
implements required methods/properties used in the tests such as the Prisma
client and transaction helpers).

In `@src/server/repositories/devices.repository.ts`:
- Around line 1-9: ESLint import/order is failing because the aliased internal
imports (e.g., TIME_CONSTANTS from '`@/utils/time`' and Prisma/ApprovalStatus or
getTrendingDevices from '`@orm/client`' / '`@orm/sql`') appear after the external
date-fns import (startOfMonth, subDays); move the internal/alias imports
(TIME_CONSTANTS and the `@orm` imports like Prisma/ApprovalStatus and
getTrendingDevices) so they come before the date-fns imports to satisfy the
rule, keeping other imports (LRUCache, HOME_PAGE_LIMITS, ResourceError,
paginate/calculateOffset, BaseRepository) in their logical groups.

In `@tests/helpers/external-services.ts`:
- Around line 9-35: The Playwright routes use exact URL strings so requests with
query strings bypass mocks; update the page.route patterns (the calls to
page.route in this file) to match query-string variants by using glob patterns
or regex (e.g., append a wildcard like '*' to the existing path patterns or use
a regex that allows trailing query strings) for the three routes handling
'_vercel/speed-insights/script.js', 'floating-chat-wrapper.css', and
'overlay-widget.js' so requests like '?v=123' are intercepted reliably.

---

Nitpick comments:
In `@sentry.edge.config.ts`:
- Around line 8-20: This block contains excessive explanatory inline comments
around the Sentry initialization; trim comments to only those that convey
non-obvious external constraints or business invariants. In the if
(process.env.NEXT_PUBLIC_ENABLE_SENTRY === 'true') { ... } / Sentry.init({...})
section, remove or shorten comments for obvious configuration keys (dsn,
tracesSampleRate, enableLogs, debug) and keep a single factual comment only if
there is an external requirement (e.g., a deployment-level mandate about
NEXT_PUBLIC_ENABLE_SENTRY or mandatory DSN handling). Ensure remaining comments
are concise and factual.

In `@src/app/admin/devices/components/DeviceModal.tsx`:
- Around line 23-24: The modal currently preloads 1000 SoC options via the call
to api.socs.options.useQuery({ limit: 1000 }) (socsQuery) which must be replaced
with a search-driven async fetch: remove the hardcoded limit call, add a
debounced search state in DeviceModal and call api.socs.options.useQuery or an
equivalent endpoint with the search term (e.g., { q: searchTerm, limit: 50 })
when the user types, wire those results into the select component (or use an
AsyncSelect pattern) to load options on demand, ensure loading/error states are
handled, and remove the TODO comment once complete.

In `@src/app/pc-listings/components/filters/AsyncCpuFilterSelect.test.tsx`:
- Around line 80-85: The test uses hard-coded UI strings; replace those literals
with shared test constants/fixtures: import canonical labels and option text
constants (e.g., CPU_LABEL, CPU_MULTI_SELECT_NAME, CPU_OPTION_INTEL,
CPU_OPTION_AMD or equivalent) and use them in the it() title, the render label
prop for AsyncCpuFilterSelect, and the assertions that call screen.getByText /
getByRole; update references to AsyncCpuFilterSelect, the test case name,
render(...) and the two expect(...) calls to use those imported constants so
strings only remain in mocks/fixtures.

In `@src/app/pc-listings/components/filters/AsyncGpuFilterSelect.test.tsx`:
- Around line 68-84: The test in AsyncGpuFilterSelect uses inline string
literals for labels and selected values; replace them with constants sourced
from your test fixtures/mock data instead of hard-coded strings. Update the test
to import the fixture constants (e.g., GPU_LABEL, GPU_OPTION_LABEL_NVIDIA,
GPU_OPTION_LABEL_AMD, GPU_SELECTED_ID) from the module used by setupApiMocks or
the API mock file, then use those constants in render(<AsyncGpuFilterSelect
label={GPU_LABEL} value={[GPU_SELECTED_ID]} .../>), in the screen.getByText
assertions, and in fireEvent.click(screen.getByRole('button', { name:
`${GPU_LABEL} multi-select` })); ensure setupApiMocks still provides the mock
data keys that the constants reference.

In `@src/components/popups/StopKillingGamesPopup.tsx`:
- Line 12: Remove the vague TODO on line 12 in the StopKillingGamesPopup
component: either delete the non-actionable comment or replace it with a
specific, factual note (e.g., a one-line explanation of the invariant or a
reference to a tracking ticket/PR) so the comment documents a concrete
constraint or next step; update the StopKillingGamesPopup.tsx file's comment
near the component declaration accordingly.

In `@src/components/ui/form/async-multi-select/AsyncMultiSelect.test.tsx`:
- Around line 41-353: The tests hardcode many UI query strings (e.g., 'Devices
multi-select', 'Search devices...', 'Clear search', 'Retroid Pocket 5', 'AYN
Odin 2', 'Steam Deck OLED', etc.) which should be centralized; update the
AsyncMultiSelect.test.tsx to import and use a shared test-strings fixture (e.g.,
names like SELECT_BUTTON_LABEL, SEARCH_PLACEHOLDER, CLEAR_SEARCH_LABEL,
OPTION_RETROID_POCKET, OPTION_AYN_ODIN, SELECTED_STEAM_DECK) instead of literal
strings, replace all occurrences in the suite (including usages in renderSelect
calls and screen.getByRole/getByText/getByPlaceholderText assertions) and
add/update the fixture file exporting these constants so tests remain resilient
and follow the test string guideline.

In `@src/lib/cors.test.ts`:
- Around line 5-123: The tests use many repeated string literals; define named
test constants (e.g., ALLOWED_ORIGIN_EMUREADY = 'https://emuready.com',
LOCALHOST_URL = 'http://localhost:3000', CI_ENV = 'true', PROD_ENV =
'production', TEST_APP_ENV = 'test') at the top of the file and replace raw
usages in calls to loadCors(), expectations for getOriginFromUrl,
getAllowedOrigins, and isAllowedRequestOrigin (and any references to
allowedOrigins or source strings) with those constants so the env
keys/URLs/app-env values are centralized and follow the test convention.

In `@src/server/api/routers/socs.ts`:
- Line 22: Remove the inline comment "// TODO: use paginate helpers" from
src/server/api/routers/socs.ts; either delete the TODO entirely or replace it
with a short factual comment linking to a created issue/PR (e.g., "See ISSUE-123
for paginate helper work") so the intent is tracked externally rather than left
as inline noise. Locate the exact TODO string in the file and apply the change,
ensuring no other logic is altered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9a93ba80-d89a-4690-b6e1-ef398a089291

📥 Commits

Reviewing files that changed from the base of the PR and between 895422f and 6b8e661.

📒 Files selected for processing (98)
  • .env.docker.example
  • .env.example
  • .env.test.example
  • .github/workflows/e2e-tests.yml
  • next.config.ts
  • playwright.config.ts
  • prisma/migrations/20260526215009_add_listing_query_indexes/migration.sql
  • prisma/schema.prisma
  • prisma/seed.ts
  • prisma/seeders/azaharCustomFieldsSeeder.ts
  • prisma/seeders/customFieldCategoryUtils.ts
  • prisma/seeders/customFieldValueSeederUtils.ts
  • prisma/seeders/edenCustomFieldsSeeder.ts
  • prisma/seeders/gamenativeCustomFieldsSeeder.ts
  • prisma/seeders/listingsSeeder.ts
  • prisma/seeders/pcListingsSeeder.ts
  • sentry.edge.config.ts
  • sentry.server.config.ts
  • src/app/admin/brands/page.tsx
  • src/app/admin/cpus/page.tsx
  • src/app/admin/devices/components/DeviceModal.tsx
  • src/app/admin/devices/page.tsx
  • src/app/admin/gpus/page.tsx
  • src/app/admin/listings/[id]/edit/components/ListingEditForm.tsx
  • src/app/admin/socs/page.tsx
  • src/app/api/proxy-image/route.ts
  • src/app/globals.css
  • src/app/home/components/HomeTrendingDevices.tsx
  • src/app/layout.tsx
  • src/app/listings/ListingsPage.tsx
  • src/app/listings/components/ListingsFiltersContent.tsx
  • src/app/listings/components/ListingsFiltersSidebar.tsx
  • src/app/listings/components/filters/AsyncDeviceFilterSelect.test.tsx
  • src/app/listings/components/filters/AsyncDeviceFilterSelect.tsx
  • src/app/listings/components/filters/AsyncSocFilterSelect.test.tsx
  • src/app/listings/components/filters/AsyncSocFilterSelect.tsx
  • src/app/listings/new/NewListingPage.tsx
  • src/app/listings/shared/components/ActiveFiltersSummary.tsx
  • src/app/listings/shared/components/MobileFiltersFab.tsx
  • src/app/pc-listings/PcListingsPage.tsx
  • src/app/pc-listings/components/PcFiltersContent.tsx
  • src/app/pc-listings/components/filters/AsyncCpuFilterSelect.test.tsx
  • src/app/pc-listings/components/filters/AsyncCpuFilterSelect.tsx
  • src/app/pc-listings/components/filters/AsyncGpuFilterSelect.test.tsx
  • src/app/pc-listings/components/filters/AsyncGpuFilterSelect.tsx
  • src/app/pc-listings/new/NewPcListingPage.tsx
  • src/app/profile/components/DeviceSelector.tsx
  • src/app/profile/components/PcPresetModal.tsx
  • src/app/profile/components/SocSelector.tsx
  • src/app/v2/listings/V2ListingsPage.tsx
  • src/app/v2/listings/components/ListingFilters.tsx
  • src/components/footer/Footer.tsx
  • src/components/footer/components/FooterBetaBadge.tsx
  • src/components/popups/BetaWarningPopup.tsx
  • src/components/popups/StopKillingGamesPopup.tsx
  • src/components/ui/KofiWidget.tsx
  • src/components/ui/form/AsyncCpuMultiSelect.tsx
  • src/components/ui/form/AsyncDeviceMultiSelect.tsx
  • src/components/ui/form/AsyncGpuMultiSelect.tsx
  • src/components/ui/form/AsyncMultiSelect.tsx
  • src/components/ui/form/AsyncSocMultiSelect.tsx
  • src/components/ui/form/MultiSelect.test.tsx
  • src/components/ui/form/MultiSelect.tsx
  • src/components/ui/form/async-multi-select/AsyncMultiSelect.test.tsx
  • src/components/ui/form/async-multi-select/AsyncMultiSelect.tsx
  • src/components/ui/form/index.ts
  • src/instrumentation-client.test.ts
  • src/instrumentation-client.ts
  • src/lib/analytics/utils/sendAnalyticsEvent.test.ts
  • src/lib/analytics/utils/sendAnalyticsEvent.ts
  • src/lib/cors.test.ts
  • src/lib/cors.ts
  • src/lib/env.test.ts
  • src/lib/env.ts
  • src/lib/trust/service.test.ts
  • src/lib/trust/service.ts
  • src/proxy.ts
  • src/schemas/cpu.ts
  • src/schemas/device.ts
  • src/schemas/gpu.ts
  • src/schemas/soc.ts
  • src/sentry-config.test.ts
  • src/server/api/routers/cpus.ts
  • src/server/api/routers/devices.ts
  • src/server/api/routers/gpus.ts
  • src/server/api/routers/socs.ts
  • src/server/repositories/cpus.repository.ts
  • src/server/repositories/devices.repository.ts
  • src/server/repositories/gpus.repository.ts
  • src/server/repositories/listings.repository.ts
  • src/server/repositories/pc-listings.repository.ts
  • src/server/repositories/socs.repository.ts
  • src/server/utils/pagination.ts
  • tests/filtering.spec.ts
  • tests/helpers/cookie-consent.ts
  • tests/helpers/external-services.ts
  • tests/pages/ListingsPage.ts
  • tests/third-party-services.spec.ts
💤 Files with no reviewable changes (8)
  • src/components/ui/form/AsyncSocMultiSelect.tsx
  • src/components/ui/form/AsyncGpuMultiSelect.tsx
  • src/components/ui/form/AsyncMultiSelect.tsx
  • src/components/footer/components/FooterBetaBadge.tsx
  • src/components/ui/form/AsyncDeviceMultiSelect.tsx
  • src/components/ui/form/AsyncCpuMultiSelect.tsx
  • src/components/ui/form/index.ts
  • src/components/footer/Footer.tsx

Comment thread prisma/seeders/pcListingsSeeder.ts
Comment thread src/app/listings/components/filters/AsyncSocFilterSelect.test.tsx Outdated
Comment thread src/app/pc-listings/new/NewPcListingPage.tsx
Comment thread src/app/profile/components/PcPresetModal.tsx Outdated
Comment thread src/lib/env.test.ts
Comment thread src/lib/trust/service.test.ts Outdated
Comment thread src/server/repositories/devices.repository.ts
Comment thread tests/helpers/external-services.ts Outdated
@Producdevity

Copy link
Copy Markdown
Owner Author

@coderabbitai create individual GitHub issues for each TODO . When the TODO comments are related to each other group them together in 1 issue. do not make changes to this PR (do not remove the TODO comments)

This was referenced May 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/ui/form/Autocomplete.tsx`:
- Line 590: The input's aria-activedescendant must be computed using the same id
schema as the option elements (the safeId defined as
`option-${idx}-${String(itemValue).replace(/[^A-Za-z0-9_-]/g, '_')}`) so the
active descendant points to an existing element; update the logic that currently
builds `option-${value}` (used for aria-activedescendant in the Autocomplete
component/input) to produce the same sanitized id including the option index
(use the active option's idx and itemValue or derive the id from the same helper
that creates safeId) so keyboard focus and screen-reader announcements reference
the correct element.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f16f2685-4807-4d20-b3da-c4e1adfb0355

📥 Commits

Reviewing files that changed from the base of the PR and between 65e1a19 and 9f664cc.

📒 Files selected for processing (6)
  • src/components/ui/form/Autocomplete.tsx
  • src/features/human-verification/server/providers/turnstile.test.ts
  • src/features/human-verification/server/providers/turnstile.ts
  • src/lib/api.tsx
  • src/server/utils/spam-check.test.ts
  • src/server/utils/spam-check.ts
✅ Files skipped from review due to trivial changes (1)
  • src/lib/api.tsx

Comment thread src/components/ui/form/Autocomplete.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
docs/ERROR_HANDLING.md (2)

21-21: ⚡ Quick win

Consider consolidating the mirrored error formatter implementations.

The documentation notes that the error formatter is "mirrored in src/server/api/mobileContext.ts". Duplicate error formatting logic creates a maintenance burden and risks divergence between web and mobile error handling.

♻️ Suggested approach

Extract the error formatter into a shared utility function that both trpc.ts and mobileContext.ts can import, ensuring consistent error serialization across both contexts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ERROR_HANDLING.md` at line 21, The repo has duplicated error formatter
logic between src/server/api/trpc.ts and src/server/api/mobileContext.ts;
extract the common formatting into a shared utility (e.g., export a function
like formatAppError or getSerializableAppError wrapper) in a new module (or
existing src/lib/app-error-cause.ts), update trpc.ts and mobileContext.ts to
import and call that single function, and ensure the exported function returns
the same shape (data.appError) so both contexts remain behaviorally identical.

30-30: 💤 Low value

Clarify the consequence of violating the plain JSON constraint.

The constraint that cause fields must be plain JSON values is stated but doesn't explain what happens if a nested Error instance is passed. Adding the consequence (e.g., "will throw during serialization" or "will be omitted from the client payload") helps developers understand the severity.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ERROR_HANDLING.md` at line 30, The docs state that `cause` fields must
be plain JSON values but don’t explain the consequence; update the `cause`
documentation to explicitly say that passing a nested Error instance will break
serialization (e.g., cause JSON.stringify to fail or result in non-serializable
fields being stripped/omitted from the client payload) and add a short
remediation: convert Error instances to plain objects (message, name, stack)
before assigning to `cause`. Include the term `cause` and mention JSON
serialization/JSON.stringify so readers can locate the rule and the fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@docs/ERROR_HANDLING.md`:
- Line 21: The repo has duplicated error formatter logic between
src/server/api/trpc.ts and src/server/api/mobileContext.ts; extract the common
formatting into a shared utility (e.g., export a function like formatAppError or
getSerializableAppError wrapper) in a new module (or existing
src/lib/app-error-cause.ts), update trpc.ts and mobileContext.ts to import and
call that single function, and ensure the exported function returns the same
shape (data.appError) so both contexts remain behaviorally identical.
- Line 30: The docs state that `cause` fields must be plain JSON values but
don’t explain the consequence; update the `cause` documentation to explicitly
say that passing a nested Error instance will break serialization (e.g., cause
JSON.stringify to fail or result in non-serializable fields being
stripped/omitted from the client payload) and add a short remediation: convert
Error instances to plain objects (message, name, stack) before assigning to
`cause`. Include the term `cause` and mention JSON serialization/JSON.stringify
so readers can locate the rule and the fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6744feaa-f1e0-47e0-9929-7ed49fa3d343

📥 Commits

Reviewing files that changed from the base of the PR and between 8326dda and a3fa69c.

📒 Files selected for processing (1)
  • docs/ERROR_HANDLING.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/server/api/routers/games.test.ts (1)

79-98: ⚡ Quick win

Avoid as never in the caller helper.

This suppresses router-context drift in a brand-new test and can let broken mocks compile unnoticed. Please type the mock context to the caller contract instead of casting it away. As per coding guidelines **/*.{ts,tsx}: Do not use casts to hide type problems. Fix the underlying type issue.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/api/routers/games.test.ts` around lines 79 - 98, The helper
createCaller uses an unsafe cast ("prisma: prisma as never") which hides real
type mismatches; instead build and pass a properly typed context to
gamesRouter.createCaller by using the router's context parameter type (e.g. type
Ctx = Parameters<typeof gamesRouter.createCaller>[0]) and declare the context
object with session and prisma typed as Ctx['prisma'] (or import the actual
Context type used by the router) before calling
gamesRouter.createCaller(context); remove the as never cast and ensure
MockPrisma implements the expected prisma shape so TypeScript will validate the
mock.
src/features/human-verification/client/components/HumanVerificationProvider.test.tsx (1)

98-207: ⚡ Quick win

Add coverage for the new script-load failure branches.

These tests cover the ready/render paths well, but the provider now also rejects on VERIFICATION_SCRIPT_LOAD_TIMEOUT_MS and <Script onError>. A fake-timer timeout case plus a direct scriptProps[0].onError?.() case would protect the new user-visible failure modes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/features/human-verification/client/components/HumanVerificationProvider.test.tsx`
around lines 98 - 207, Add two tests to cover script-load failure branches: one
that uses fake timers to advance time past VERIFICATION_SCRIPT_LOAD_TIMEOUT_MS
and asserts the captured promise rejects with a script-load timeout error, and
another that directly invokes scriptProps[0].onError?.() after rendering with
window.turnstile unset and asserts the captured promise rejects with the script
load error; place these alongside the existing tests in
HumanVerificationProvider.test.tsx and reference
VERIFICATION_SCRIPT_LOAD_TIMEOUT_MS, scriptProps[0].onError, and the same
CaptureButton request-handling pattern to collect the promise for assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/human-verification/server/providers/turnstile.ts`:
- Around line 77-86: The current bypass of validateSuccessfulResponse when the
secret is in TURNSTILE_TEST_SECRET_KEYS must be limited to test environments:
update the logic around the parsed.data.success check so that the test-secret
shortcut is only used when running in a test environment (e.g., via NODE_ENV ===
'test' or an existing isTestEnvironment helper); if a test secret is encountered
outside a test environment, treat it as invalid (do not skip
validateSuccessfulResponse and return failure/error codes). Change the
conditional that references TURNSTILE_TEST_SECRET_KEYS to also check the
test-env flag (or invert to fail-closed when test key + not test env) and ensure
validateSuccessfulResponse is always invoked for non-test environments.

---

Nitpick comments:
In
`@src/features/human-verification/client/components/HumanVerificationProvider.test.tsx`:
- Around line 98-207: Add two tests to cover script-load failure branches: one
that uses fake timers to advance time past VERIFICATION_SCRIPT_LOAD_TIMEOUT_MS
and asserts the captured promise rejects with a script-load timeout error, and
another that directly invokes scriptProps[0].onError?.() after rendering with
window.turnstile unset and asserts the captured promise rejects with the script
load error; place these alongside the existing tests in
HumanVerificationProvider.test.tsx and reference
VERIFICATION_SCRIPT_LOAD_TIMEOUT_MS, scriptProps[0].onError, and the same
CaptureButton request-handling pattern to collect the promise for assertions.

In `@src/server/api/routers/games.test.ts`:
- Around line 79-98: The helper createCaller uses an unsafe cast ("prisma:
prisma as never") which hides real type mismatches; instead build and pass a
properly typed context to gamesRouter.createCaller by using the router's context
parameter type (e.g. type Ctx = Parameters<typeof gamesRouter.createCaller>[0])
and declare the context object with session and prisma typed as Ctx['prisma']
(or import the actual Context type used by the router) before calling
gamesRouter.createCaller(context); remove the as never cast and ensure
MockPrisma implements the expected prisma shape so TypeScript will validate the
mock.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 947f5127-c9f1-4e0e-ba90-7cb9d3811c25

📥 Commits

Reviewing files that changed from the base of the PR and between 8326dda and fb51cfa.

⛔ Files ignored due to path filters (1)
  • public/placeholder/game.svg is excluded by !**/*.svg
📒 Files selected for processing (24)
  • .env.docker.example
  • .env.example
  • .env.test.example
  • docs/ERROR_HANDLING.md
  • src/app/games/new/NewGamePage.tsx
  • src/app/games/new/search/GameSearchPage.tsx
  • src/app/games/new/search/v2/IGDBSearchPage.tsx
  • src/app/layout.tsx
  • src/components/banners/CommunitySupportBanner.tsx
  • src/features/human-verification/client/components/HumanVerificationProvider.test.tsx
  • src/features/human-verification/client/components/HumanVerificationProvider.tsx
  • src/features/human-verification/server/providers/turnstile.test.ts
  • src/features/human-verification/server/providers/turnstile.ts
  • src/schemas/game.ts
  • src/schemas/listing.test.ts
  • src/server/api/routers/games.test.ts
  • src/server/api/routers/games.ts
  • src/server/api/routers/listings/comments.test.ts
  • src/server/api/routers/listings/core.test.ts
  • src/server/api/routers/pcListings.test.ts
  • src/server/utils/spam-check.test.ts
  • src/server/utils/spam-check.ts
  • src/server/utils/spamDetection.test.ts
  • src/server/utils/spamDetection.ts
💤 Files with no reviewable changes (2)
  • src/components/banners/CommunitySupportBanner.tsx
  • src/app/layout.tsx
✅ Files skipped from review due to trivial changes (1)
  • docs/ERROR_HANDLING.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • .env.test.example
  • .env.example
  • .env.docker.example

Comment on lines +77 to 86
if (parsed.data.success && !TURNSTILE_TEST_SECRET_KEYS.has(secret)) {
const validationErrors = validateSuccessfulResponse({
action: parsed.data.action,
hostname: parsed.data.hostname,
})

if (validationErrors.length > 0) {
return { success: false, errorCodes: validationErrors }
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope the test-secret bypass to test environments only.

Skipping validateSuccessfulResponse() solely because the secret matches Cloudflare's documented test keys means a staging or production env that is accidentally configured with a test secret also skips the new action and hostname checks. Gate this bypass behind a test-only env, or fail closed when test keys appear outside tests.

Suggested hardening
-  if (parsed.data.success && !TURNSTILE_TEST_SECRET_KEYS.has(secret)) {
+  const isTestSecret = TURNSTILE_TEST_SECRET_KEYS.has(secret)
+  const allowTestSecretBypass =
+    isTestSecret &&
+    (process.env.NODE_ENV === 'test' || process.env.NEXT_PUBLIC_APP_ENV === 'test')
+
+  if (isTestSecret && !allowTestSecretBypass) {
+    return { success: false, errorCodes: ['invalid-test-secret'] }
+  }
+
+  if (parsed.data.success && !allowTestSecretBypass) {
     const validationErrors = validateSuccessfulResponse({
       action: parsed.data.action,
       hostname: parsed.data.hostname,
     })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (parsed.data.success && !TURNSTILE_TEST_SECRET_KEYS.has(secret)) {
const validationErrors = validateSuccessfulResponse({
action: parsed.data.action,
hostname: parsed.data.hostname,
})
if (validationErrors.length > 0) {
return { success: false, errorCodes: validationErrors }
}
}
const isTestSecret = TURNSTILE_TEST_SECRET_KEYS.has(secret)
const allowTestSecretBypass =
isTestSecret &&
(process.env.NODE_ENV === 'test' || process.env.NEXT_PUBLIC_APP_ENV === 'test')
if (isTestSecret && !allowTestSecretBypass) {
return { success: false, errorCodes: ['invalid-test-secret'] }
}
if (parsed.data.success && !allowTestSecretBypass) {
const validationErrors = validateSuccessfulResponse({
action: parsed.data.action,
hostname: parsed.data.hostname,
})
if (validationErrors.length > 0) {
return { success: false, errorCodes: validationErrors }
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/human-verification/server/providers/turnstile.ts` around lines
77 - 86, The current bypass of validateSuccessfulResponse when the secret is in
TURNSTILE_TEST_SECRET_KEYS must be limited to test environments: update the
logic around the parsed.data.success check so that the test-secret shortcut is
only used when running in a test environment (e.g., via NODE_ENV === 'test' or
an existing isTestEnvironment helper); if a test secret is encountered outside a
test environment, treat it as invalid (do not skip validateSuccessfulResponse
and return failure/error codes). Change the conditional that references
TURNSTILE_TEST_SECRET_KEYS to also check the test-env flag (or invert to
fail-closed when test key + not test env) and ensure validateSuccessfulResponse
is always invoked for non-test environments.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
.husky/pre-push (1)

1-4: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add error handling to fail fast on first check failure.

Without set -e or command chaining, all three commands will execute sequentially even if earlier ones fail. The hook's exit code will be determined only by the last command (pnpm test), allowing a push to proceed even if pnpm lint or pnpm types fail.

🔧 Proposed fix
+#!/bin/sh -e
+
 pnpm lint
 pnpm types
 pnpm test

Alternatively, chain the commands:

-pnpm lint
-pnpm types
-pnpm test
+pnpm lint && pnpm types && pnpm test
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.husky/pre-push around lines 1 - 4, The pre-push hook runs pnpm lint, pnpm
types, pnpm test sequentially but does not fail fast; update the hook to abort
on the first failing command by either adding a shell fail-fast setting (e.g.,
set -e) at the top of the script or by chaining the commands with && so that
pnpm lint && pnpm types && pnpm test will stop on first error; apply this change
in the .husky/pre-push script where the pnpm lint, pnpm types, pnpm test
commands are listed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.husky/pre-push:
- Around line 1-4: The pre-push hook runs pnpm lint, pnpm types, pnpm test
sequentially but does not fail fast; update the hook to abort on the first
failing command by either adding a shell fail-fast setting (e.g., set -e) at the
top of the script or by chaining the commands with && so that pnpm lint && pnpm
types && pnpm test will stop on first error; apply this change in the
.husky/pre-push script where the pnpm lint, pnpm types, pnpm test commands are
listed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b22dc3bb-2559-4be5-9546-4968b919430a

📥 Commits

Reviewing files that changed from the base of the PR and between fb51cfa and 75f19a6.

📒 Files selected for processing (2)
  • .husky/pre-commit
  • .husky/pre-push
✅ Files skipped from review due to trivial changes (1)
  • .husky/pre-commit

@Producdevity

Copy link
Copy Markdown
Owner Author

Caution

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

⚠️ Outside diff range comments (1)

.husky/pre-push (1)> 1-4: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add error handling to fail fast on first check failure.
Without set -e or command chaining, all three commands will execute sequentially even if earlier ones fail. The hook's exit code will be determined only by the last command (pnpm test), allowing a push to proceed even if pnpm lint or pnpm types fail.

🔧 Proposed fix

+#!/bin/sh -e
+
 pnpm lint
 pnpm types
 pnpm test

Alternatively, chain the commands:

-pnpm lint
-pnpm types
-pnpm test
+pnpm lint && pnpm types && pnpm test

🤖 Prompt for AI Agents

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.husky/pre-push around lines 1 - 4, The pre-push hook runs pnpm lint, pnpm
types, pnpm test sequentially but does not fail fast; update the hook to abort
on the first failing command by either adding a shell fail-fast setting (e.g.,
set -e) at the top of the script or by chaining the commands with && so that
pnpm lint && pnpm types && pnpm test will stop on first error; apply this change
in the .husky/pre-push script where the pnpm lint, pnpm types, pnpm test
commands are listed.

🤖 Prompt for all review comments with AI agents

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.husky/pre-push:
- Around line 1-4: The pre-push hook runs pnpm lint, pnpm types, pnpm test
sequentially but does not fail fast; update the hook to abort on the first
failing command by either adding a shell fail-fast setting (e.g., set -e) at the
top of the script or by chaining the commands with && so that pnpm lint && pnpm
types && pnpm test will stop on first error; apply this change in the
.husky/pre-push script where the pnpm lint, pnpm types, pnpm test commands are
listed.

ℹ️ Review info

no. Git does not run that file directly. Husky runs it through .husky/_/h

@Producdevity Producdevity merged commit bb1f3a9 into staging Jun 2, 2026
9 checks passed
@Producdevity Producdevity deleted the perf-device-listing-queries branch June 2, 2026 23:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant