Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .env.docker.example
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ NEXT_PUBLIC_GITHUB_URL="https://github.com/Producdevity/EmuReady"
NEXT_PUBLIC_EMUREADY_LITE_GITHUB_URL="https://github.com/Producdevity/EmuReadyLite/releases"
NEXT_PUBLIC_APP_URL="https://dev.emuready.com"
NEXT_PUBLIC_ENABLE_SW=false
NEXT_PUBLIC_ENABLE_ASYNC_LISTINGS_FILTERS=false
NEXT_PUBLIC_ENABLE_V2_LISTINGS=false
NEXT_TELEMETRY_DISABLED=1
NEXT_IMAGE_UNOPTIMIZED=false

Expand Down
2 changes: 0 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ NEXT_PUBLIC_EMUREADY_LITE_GITHUB_URL="https://github.com/Producdevity/EmuReadyLi
NEXT_PUBLIC_APP_URL="http://localhost:3000" # Make sure to change this if you are using a tunnel
NEXT_PUBLIC_ENABLE_SW=false
NEXT_PUBLIC_ENABLE_PATREON_VERIFICATION=true
NEXT_PUBLIC_ENABLE_ASYNC_LISTINGS_FILTERS=false
NEXT_PUBLIC_ENABLE_V2_LISTINGS=false
NEXT_TELEMETRY_DISABLED=1
NEXT_IMAGE_UNOPTIMIZED=false

Expand Down
2 changes: 0 additions & 2 deletions .env.test.example
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ NEXT_PUBLIC_APP_URL="https://dev.emuready.com"
NEXT_PUBLIC_ENABLE_PATREON_VERIFICATION=true
NEXT_PUBLIC_ENABLE_SW=false
NEXT_PUBLIC_DISABLE_COOKIE_BANNER=true
NEXT_PUBLIC_ENABLE_ASYNC_LISTINGS_FILTERS=false
NEXT_PUBLIC_ENABLE_V2_LISTINGS=false
NEXT_TELEMETRY_DISABLED=1
NEXT_IMAGE_UNOPTIMIZED=true

Expand Down
52 changes: 48 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,53 @@ This file is the source of working guidance for AI coding agents in this reposit
- Feature folders should follow the project-structure guidance from Bulletproof React:
https://github.com/alan2207/bulletproof-react/blob/master/docs/project-structure.md
- Within `src/features/*`, prefer scoped subdirectories such as `components`, `hooks`, `utils`, `server`, and `shared` instead of flat feature folders.
- Routers in `src/server/api/routers/` are thin orchestration layers. They handle auth context, schema-validated input, repository/service calls, and response formatting.
- Feature-owned modules may colocate client, server, and shared code under
`src/features/<domain>/<module>/`.
- Use this feature module structure for new or actively-refactored domain code:
- `shared/` contains Zod schemas, types, constants, and pure formatting helpers usable by client and server.
- `server/` contains repositories, services, policies, mappers, and feature-owned tRPC routers.
- `client/` contains hooks, reusable components, and workflow views. Add client API wrappers only when they remove real duplication or encode a stable UI contract.
- `client/admin/` is allowed for admin-only workflows.
- Import direction matters more than folder names:
- `client/` may import from its feature `shared/` and app-wide client-safe utilities.
- `server/` may import from its feature `shared/`, server utilities, and repositories.
- `shared/` must not import from `client/`, `server/`, `src/app`, or server-only libraries.
- `src/app/**` routes/pages should compose feature modules; feature modules should not import from `src/app/**`.
- tRPC routers are transport adapters. Feature-specific routers should live in the feature `server/` folder when that feature owns the full use case; `src/server/api/root.ts` should only compose them.
- Legacy routers in `src/server/api/routers/` are thin orchestration layers. They handle auth context, schema-validated input, repository/service calls, and response formatting.
- Do not put raw Prisma queries or business logic in routers.
- Define Zod schemas in `src/schemas/*`; do not define inline schemas in router `.input(...)` calls.
- All database access belongs in repository classes under `src/server/repositories/` extending `BaseRepository`.
- Define Zod schemas in feature `shared/*.schemas.ts` for feature-owned code, or in `src/schemas/*` for legacy/shared code. Do not define inline schemas in router `.input(...)` calls.
- Feature-owned tRPC procedures should declare `.output(...)` with Zod schemas. Compatibility transports that must keep legacy shapes should still have explicit legacy output schemas instead of returning raw Prisma payloads by convention.
- All database access belongs in repository classes. Feature-owned repositories may live in feature `server/` folders; legacy/shared repositories may remain under `src/server/repositories/`.
- New or actively-refactored feature-owned Prisma repositories should extend `PrismaRepository` or `PrismaWriteRepository` from `src/server/persistence/prisma.repository.ts` for Prisma client ownership and shared write handling. Do not extend the legacy `BaseRepository` unless the inherited behavior is deliberately required and documented.
- Do not add generic CRUD methods to shared repository bases. Prisma already provides typed CRUD; feature repositories should expose domain/use-case persistence operations with named select contracts.
- For feature-owned Prisma repositories, prefer a `server/persistence/` subfolder for named `select` contracts, query builders, and Prisma error translation. Derive repository record types from Prisma `GetPayload` plus those named `select` contracts instead of hand-maintaining structural copies.
- Services should depend on the concrete feature repository by default. Do not add service-owned `Pick<Repository, ...>` contracts only for tests.
- Add repository interfaces only for real boundaries: multiple implementations, external provider adapters, lifecycle concerns that route composition cannot handle directly, or domain/application layers that intentionally must not depend on infrastructure.
- Repositories should use project error helpers and consistent database operation handling.
- Multi-step business logic, external API orchestration, and complex calculations belong in services under `src/server/services/`.
- Multi-step business logic, external API orchestration, and complex calculations belong in services under feature `server/` folders or legacy `src/server/services/`.
- Use policy functions for reusable authorization/business access rules that must be shared across transports. Routers may still use broad auth procedures, but services should enforce feature-level capabilities when the use case can be called from multiple transports.
- Use `AppError` and `ResourceError` helpers instead of raw `Error`, raw strings, or one-off `TRPCError` usage.
- Use specialized procedures such as `protectedProcedure`, `adminProcedure`, and `permissionProcedure(...)` instead of ad hoc permission checks.

## API Compatibility And Legacy Contracts

- Before introducing any `legacy` select, repository method, route, endpoint,
schema, type, mapper, compatibility branch, or response shape, audit known
consumers first. For mobile/public API work, this includes
`/Volumes/T9/Coding/personal/2026/Emulation/EmuReadyApp` when it is available,
or a temporary clone of `Producdevity/EmuReadyApp` on the `master` branch when
the local app checkout is unavailable or not production-synced.
- Record which fields consumers actually read. Do not preserve fields only
because they existed in an old Prisma payload, old inferred type, or old API
response.
- Decide compatibility case by case: remove unused old fields, migrate the
consumer, keep one standardized endpoint with a small low-cost superset, or
keep a separate legacy contract only when the audited consumer behavior truly
requires it.
- Legacy contracts must have an owner, a reason, and an expected removal path.
Remove legacy code as soon as audited consumers do not need it.

## Database And Prisma

- Treat database changes as high risk.
Expand All @@ -60,6 +98,12 @@ This file is the source of working guidance for AI coding agents in this reposit
- Do not use casts to hide type problems. Fix the underlying type issue.
- Handle null and undefined explicitly.
- Use generated Prisma types where appropriate.
- Prefer deriving types from existing contracts instead of hand-maintaining
structural copies. Use Prisma `GetPayload`, Zod `z.input`/`z.output`, tRPC
`RouterInput`/`RouterOutput`, `ReturnType`, and `typeof` on const contracts
before adding a new interface or structural type alias. Add new manual
interfaces/types only for genuinely new UI/application state or external
boundaries that cannot be inferred, and keep them narrow and local.
- Do not add unused functions, exports, or speculative helpers.
- Remove dead code when refactoring.
- Do not remove or rewrite existing TODO comments unless the user explicitly
Expand Down
25 changes: 25 additions & 0 deletions config/image-hosts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const GAME_IMAGE_PROVIDER_HOST_PATTERNS = [
'media.rawg.io',
'cdn.thegamesdb.net',
'images.igdb.com',
'assets.nintendo.com',
'shared.akamai.steamstatic.com',
'cdn1.epicgames.com',
'cdn2.unrealengine.com',
'images.gog-statics.com',
] as const

export const NEXT_IMAGE_REMOTE_HOST_PATTERNS = [
'placehold.co',
'*.clerk.com',
'*.clerk.accounts.dev',
'storage.ko-fi.com',
'ko-fi.com',
...GAME_IMAGE_PROVIDER_HOST_PATTERNS,
] as const

export const NEXT_IMAGE_REMOTE_PATTERNS = NEXT_IMAGE_REMOTE_HOST_PATTERNS.map((hostname) => ({
protocol: 'https' as const,
hostname,
pathname: '/**',
}))
Loading
Loading