A modern web application for browsing and discovering 3D models, built with Next.js, TypeScript, and Drizzle ORM.
- Framework: Next.js 16.2.6 with App Router, Cache Components, and typed routes (
typedRoutes) - Language: TypeScript 6.0.3 with React 19.3 canary
- Styling: Panda CSS 1.11.1 (
@pandacss/dev,panda.config.ts); generatedstyled-system/frompanda codegen(gitignored; run viabun install/prepare); imports use the@styled-system/*path alias (tsconfig.json); global view transitions and@layerrules insrc/app/index.css; Biome CSS parser withtailwindDirectives(Tailwind v4 directive syntax) for layered CSS - Database: Neon (PostgreSQL) with Drizzle ORM 1.0.0-rc.2
- Authentication: Better Auth 1.6.11 with email/password and GitHub OAuth, cookie caching enabled, ElysiaJS API backend
- Search Params: nuqs 2.8.9 for type-safe URL state management; listing canonical URLs use
nuqs/serverloaders/serializers (features/pagination/listing-canonical.ts) for SEO metadata - Linting & Formatting: Biome 2.4.15 with Ultracite 7.7.0 presets (
ultracite/biome/core,react,next); React Doctor on PRs (.github/workflows/react-doctor.yml,react-doctor.config.json) - Type Checking: tsgo (TypeScript Native Preview)
- Package Manager: Bun
- Build Tool: Turbopack for dev and build; experimental view transitions, MCP server, and cached navigations (
next.config.ts); env types from Varlock (.env.schema,src/env.d.ts), not NexttypedEnv - Environment: Varlock 1.2.0 with
.env.schema,@varlock/nextjs-integrationplugin innext.config.ts, optional Bitwarden Secrets Manager via@varlock/bitwarden-plugin(seedocs/VARLOCK.md) - Validation: Varlock for environment; Valibot 1.4.0 for server action and form schemas
- Browse 3D Models: View a curated collection of 3D models across various categories
- Category Filtering: Filter models by category (3D Printer, Art, Education, Fashion, etc.)
- Responsive Design: Optimized for desktop, tablet, and mobile devices
- Smooth Page Transitions: View Transitions API with composable fade and slide animations for pagination
- Type-Safe Database: Full TypeScript support with Drizzle ORM
- Performance Optimized: Caching for frequently accessed data
- Modern Stack: Built with Next.js 16.2.6, TypeScript, and Panda CSS
- Feature-Based Architecture: Well-organized codebase with clear separation of concerns
Note: Like/dislike functionality with optimistic updates and real-time like count synchronization is fully implemented.
Static assets are served from public/ at the repository root (not under src/), including hero-image-square.png referenced by src/lib/hero-image.ts. Supplemental docs live in docs/ (for example AUTH_SETUP.md, VARLOCK.md, PSEUDO_CLASS_TRANSITIONS.md, PERFORMANCE_IMPROVEMENTS.md). Panda CSS writes generated files to styled-system/ at the repo root (panda.config.ts β outdir); that folder is gitignoredβrun bun install (or bunx panda codegen) so imports like @styled-system/css resolve. Root tooling includes react-doctor.config.json and .github/workflows/react-doctor.yml for PR diagnostics.
src/
βββ app/ # Next.js App Router
β βββ @navbar/ # Parallel route for navbar
β β βββ default.tsx
β β βββ error.tsx
β βββ @footer/ # Parallel route for footer
β β βββ default.tsx
β βββ 3d-models/ # 3D models routes
β β βββ @categories/ # Parallel route for categories nav
β β β βββ default.tsx
β β β βββ error.tsx # Error boundary for categories
β β βββ @results/ # Parallel route for search results
β β β βββ [...catchAll]/
β β β β βββ page.tsx
β β β βββ default.tsx
β β β βββ error.tsx # Error boundary for results with retry functionality
β β β βββ loading.tsx # Loading state for results
β β β βββ page.tsx
β β βββ [slug]/ # Individual model page
β β β βββ error.tsx # Error boundary for model detail page
β β β βββ not-found.tsx
β β β βββ page.tsx
β β βββ categories/ # Category-specific pages
β β β βββ [categoryName]/
β β β βββ error.tsx # Error boundary for category pages with retry functionality
β β β βββ loading.tsx # Loading state for category pages
β β β βββ not-found.tsx
β β β βββ page.tsx
β β βββ layout.tsx # Models layout
β β βββ page.tsx # Models landing page
β βββ about/ # About page
β β βββ page.tsx
β βββ (auth)/ # Authentication group route
β β βββ layout.tsx # Centered auth layout
β β βββ signin/
β β β βββ page.tsx
β β βββ signup/
β β βββ page.tsx
β βββ api/ # API routes
β β βββ [[...slugs]]/
β β βββ better-auth-openapi.ts # Better Auth OpenAPI spec for Elysia docs
β β βββ route.ts # ElysiaJS handler mounting Better Auth (`basePath` /api/auth)
β βββ index.css # Global @layer stack, view-transition animations
β βββ styles.ts # Shared Panda `css` / pattern exports for app shells
β βββ icon.png # App icon (metadata)
β βββ layout.tsx # Root layout
β βββ page.tsx # Home page
β βββ global-error.tsx # Root error boundary (App Router)
β βββ robots.ts # robots.txt Route Handler
β βββ sitemap.ts # Sitemap generation
βββ features/
β βββ auth/ # Authentication feature
β β βββ actions/ # Server actions
β β β βββ sign-in-action.ts
β β β βββ sign-out-action.ts
β β β βββ sign-up-action.ts
β β βββ components/ # Auth components
β β β βββ auth-buttons.tsx
β β β βββ auth-buttons-skeleton.tsx
β β β βββ auth-card.tsx
β β β βββ auth-footer-link.tsx
β β β βββ avatar.tsx # User avatar (GitHub image, fallback icon)
β β β βββ has-auth.tsx # Generic auth component with session provider
β β β βββ sign-in-button.tsx
β β βββ constants.ts # Auth validation constants
β β βββ queries/
β β β βββ get-user.ts
β β βββ types.ts # Auth type definitions
β βββ categories/ # Categories feature
β β βββ components/
β β β βββ categories-block-transition.tsx
β β β βββ categories-nav.tsx
β β βββ constants.ts # ALL_CATEGORIES, CATEGORY_LIST_ITEMS, not-found metadata
β β βββ types.ts
β β βββ queries/
β β βββ get-all-categories.ts
β β βββ get-all-category-slugs.ts
β β βββ get-category-by-slug.ts
β βββ models/ # Models feature
β β βββ actions/
β β β βββ likes.ts
β β βββ components/
β β β βββ heart-button/
β β β β βββ heart-button-client.tsx
β β β β βββ heart-button-count.tsx
β β β β βββ heart-button-server.tsx
β β β β βββ heart-button-skeleton.tsx
β β β β βββ heart-like-optimistic.ts
β β β β βββ likes-count-transition.tsx
β β β βββ model-card.tsx
β β β βββ model-card-skeleton.tsx
β β β βββ model-detail.tsx
β β β βββ models-grid.tsx
β β β βββ models-grid-skeleton.tsx
β β β βββ models-not-found.tsx
β β β βββ models-view.tsx
β β βββ constants.ts
β β βββ dal/
β β β βββ get-models.ts # `{ result, isAuthenticated }`; search + user, batched likes
β β β βββ search-models.ts # Unified listing/search (optional query + category)
β β βββ queries/
β β β βββ get-all-model-slugs.ts
β β β βββ get-model-by-slug.ts
β β β βββ get-model-with-like-status.ts
β β β βββ get-models-count.ts
β β β βββ get-models-list.ts
β β βββ types.ts
β βββ pagination/
β βββ components/
β β βββ pagination-button.tsx
β β βββ pagination-offset-transition.tsx
β β βββ pagination-skeleton.tsx
β β βββ pagination.tsx
β βββ dal/
β β βββ paginate-items.ts
β βββ utils/
β β βββ to-paginated-result.ts
β βββ listing-canonical.ts
β βββ pagination-search-params.ts
β βββ constants.ts
β βββ types.ts
βββ constants.ts # Shared constants (EMPTY_LIST_LENGTH)
βββ components/ # Shared/generic components
β βββ form/
β β βββ field-errors.tsx
β β βββ form-error.tsx
β β βββ input.tsx
β β βββ label.tsx
β β βββ reset-button.tsx
β β βββ submit-button.tsx
β βββ button.tsx
β βββ generic-component.tsx
β βββ nav-link.tsx
β βββ not-found/
β β βββ unsuccessful-state-list-item.tsx
β β βββ unsuccessful-state.tsx
β βββ pill.tsx
β βββ scroll-progress.tsx
β βββ skeleton.tsx # Shared loading skeleton primitive
β βββ search-input/
β β βββ search-input.tsx
β β βββ search-input-transition.tsx
β β βββ search-input-skeleton.tsx
β βββ suspend.tsx
β βββ top-link.tsx
βββ db/
β βββ schema/
β β βββ auth.ts
β β βββ likes.ts
β β βββ models.ts
β β βββ relations.ts
β β βββ index.ts
β βββ seed-data/
β β βββ categories.ts
β β βββ models.ts
β βββ seed.ts
β βββ drop-tables.ts
β βββ index.ts
βββ lib/
β βββ api.ts
β βββ auth.ts
β βββ auth-client.ts
β βββ date.ts
β βββ hero-image.ts
βββ types/
β βββ index.ts
βββ utils/
β βββ cache-invalidation.ts
β βββ sanitise-name.ts
β βββ to-action-state.ts
β βββ try-catch.ts
βββ global.d.ts
βββ proxy.ts
The project follows a feature-based architecture where related functionality is co-located:
features/models/: All model-related components, actions, queries, and DALfeatures/categories/: All category-related components and data queriesfeatures/pagination/: Pagination utilities, types, and components shared across featuresfeatures/auth/: Authentication actions, components, queries, and typescomponents/: Shared components used across features (including navigation)
_prefix: Private folders that are not part of Next.js routingfeatures/: Feature-based modules with their own components and queriescomponents/: Shared/generic components used across featuresdb/seed-data/: Explicitly named seed data files
- NuqsAdapter: Scoped to
/3d-modelslayout only (not root layout) for reduced overhead on routes that don't use URL state management - Font Loading: Only required font weights are loaded (Albert Sans: 400,500,600,700; Montserrat Alternates: 400,600,700)
- Error Handling: Centralized
tryCatchutility for consistent error handling across database queries - Cache Components: Uses
"use cache","use cache: remote", and"use cache: private"directives for persistent caching; Reactcache()is used only for functions called multiple times in the same render pass (e.g.,getModelBySlugandgetCategoryBySlugcalled in bothgenerateMetadataand page components) - Type Safety:
Maybe<T>type helper used consistently across all query functions for nullable return types; centralized type definitions insrc/types/index.tsand feature-specifictypes.tsfiles for better organization and reusability - Query Builder: Migrated to Drizzle ORM RQBv2 for simple relational queries (
db.query.tableName.findMany/findFirst) with object-basedwhereclauses; complex queries and mutations remain on SQL builder - Error Recovery: Error boundaries with
error.tsxfor failed queries (results, category pages, and model detail pages) with built-inreset()retry functionality and helpful error guidance - Database Query Separation: Database queries return raw
DatabaseQueryResult<T>; transformation toPaginatedResult<T>happens in higher-level functions usingtransformToPaginatedResultutility fromfeatures/pagination/utils/ - View Transitions: Composable CSS animations using base fade and slide keyframes with CSS variables for slide distance, enabling smooth directional page transitions (enter-left, exit-left, enter-right, exit-right) for pagination
- Bun (recommended) or a current Node.js LTS
- Neon database account (or any PostgreSQL database)
- Optional: Bitwarden Secrets Manager machine account token if you use
bitwarden()resolvers in.env.schema(seedocs/VARLOCK.md)
-
Clone the repository
git clone <repository-url> cd 3dmodels
-
Install dependencies
bun install
This runs the
preparelifecycle script (panda codegento generatestyled-system/, plus Husky). If codegen ever needs a manual rerun:bunx panda codegen. -
Environment Setup Configuration is defined in
.env.schema(Varlock). Copy it to.envand fill in values, or use literal strings in place ofbitwarden("β¦")UUIDs for local development. Typical variables:# Bootstrap (Bitwarden resolvers in .env.schema) BITWARDEN_ACCESS_TOKEN="your-machine-account-token" NEXT_PUBLIC_SITE_URL="http://localhost:3000" BETTER_AUTH_SECRET="your-secret-key-here-change-this-in-production" GITHUB_CLIENT_ID="your-github-oauth-client-id" GITHUB_CLIENT_SECRET="your-github-oauth-client-secret" DATABASE_URL="your-neon-database-connection-string"
Run
bun run env:typegenafter changing.env.schemato refreshsrc/env.d.ts. Typed access usesimport { ENV } from "varlock/env". Seedocs/VARLOCK.mdanddocs/AUTH_SETUP.mdfor Bitwarden, Bun, and Vercel notes. -
Database Setup Scripts use
varlock run --so Drizzle and seed commands receive resolved env (seepackage.json):bun run db:push bun run db:seed
Alternatively, migrations (SQL and
meta/snapshots are written tosrc/db/migrations/when you run generate; clones may usedb:pushonly until migrations exist):bun run db:generate bun run db:migrate bun run db:seed
For one-off Drizzle CLI use without the
db:*scripts, use the same pattern aspackage.json(for examplevarlock run -- bun x drizzle-kit push). -
Start the development server
bun run dev
Open http://localhost:3000 to view the application.
id: Primary key (auto-increment)displayName: Human-readable category nameslug: URL-friendly identifier (unique)
slug: Primary key (text, auto-generated from name)name: Model name (unique)description: Model descriptionlikes: Number of likes (counter)image: Image URLcategorySlug: Foreign key to categories.sluguserId: Foreign key to user.id (cascade delete)dateAdded: Timestamp when model was added
id: Primary key (auto-increment)userId: Foreign key to users.id (cascade delete)modelSlug: Foreign key to models.slug (cascade delete)createdAt: Timestamp when like was created- Unique constraint on
(userId, modelSlug)pair
user: User accounts with email/password and OAuth supportaccount: OAuth provider accounts (GitHub)session: User sessions with cookie cachingverification: Email verification tokens
bun run db:generateβ Generate migrations (varlock run -- bun x drizzle-kit generate)bun run db:migrateβ Run migrations (varlock run -- bun x drizzle-kit migrate)bun run db:pushβ Push schema (varlock run -- bun x drizzle-kit push --force)bun run db:studioβ Drizzle Studio (varlock run -- bun x drizzle-kit studio)bun run db:seedβ Seed database (requires existing users for seeded models)bun run db:dropβ Drop all tables (development reset)
The application uses Drizzle ORM 1.0.0-rc.2 with defineRelations for type-safe relations:
- Relations defined using the v1/rc syntax with
r.one()andr.many()helpers - Relation names avoid conflicts with column names (e.g.,
modelLikesinstead oflikesto avoid conflict withmodels.likescolumn) - All relations exported from
schema/relations.tsand included in the database schema
The application uses Drizzle ORM's Relational Query Builder v2 (RQBv2) for type-safe relational queries:
- Read queries: All read queries use RQBv2 syntax (
db.query.tableName.findMany(),db.query.tableName.findFirst()) with object-basedwhereclauses, including complex conditions withOR: [],AND: [],NOT: {}, and column filters like{ column: { eq: value, ilike: pattern } }for better type safety and developer experience - Count queries: Count queries use
db.$count()(RQBv2), with where conditions passed using SQL builder syntax (and(),or(),ilike(), etc.) since$countaccepts SQL builder conditions - Mutations: Insert, update, and delete operations use the SQL builder syntax (mutations not yet available in RQBv2)
- Hybrid approach: The codebase uses a hybrid strategy - RQBv2 object syntax for all read queries (including complex conditions with
AND/ORarrays), SQL builder for count where conditions and mutations - Query organization: Model queries are split into focused functions (
get-models-list.tsfor listing with RQBv2,get-models-count.tsfor counting with SQL builder) and composed in higher-level DAL functions (get-models.ts,search-models.ts). Both helpers support optionalsearchPatternandcategoryparameters for flexible querying - Note: Better Auth's
drizzleAdaptercurrently has compatibility issues with RQBv2, showing errors about unknown relational filter fields (e.g., "decoder"). Experimental joins have been disabled for Drizzle v1 compatibility. Both email/password and GitHub OAuth authentication are fully functional. The application will continue using RQBv2 for queries as Better Auth is expected to update their adapter soon. Better Auth is mounted on ElysiaJS at/api/[[...slugs]]/route.tswithbasePath/api/auth; OpenAPI documentation includes auth routes viabetter-auth-openapi.ts.
The application uses Next.js Cache Components for optimal performance:
- Static content is pre-rendered at build time
- Dynamic content (like authentication state) is rendered at request time
- Server components use
connection()to opt into dynamic rendering when needed - Cache invalidation handled by
cacheTagutilities - Error handling with
error.tsxerror boundaries for failed queries (categories, results, and category pages with built-inreset()retry functionality) - Loading states with
loading.tsxfor results and category pages
The application uses Next.js Cache Components with granular cache tags for efficient invalidation:
- Models: Cached with
models,model-{slug}, andmodels-category-{slug}tags - Categories: Cached at component level with
categoriestag andcacheLife("max")for pre-rendered HTML output - Cache Life: Hours profile for most queries (5 min stale, 1 hour revalidate, 1 day expire), max for static categories (component-level caching)
- Query Functions: Unified
getModels()function usessearchModels()which handles search (with optional query), category filtering, and listing. The function uses helper functionsgetModelsListandgetModelsCountwhich support optional search and category parameters - Like Status:
getHasLikedStatususes"use cache: private"for user-specific like status (cached on device) - Model Lists:
get-models.tsaddshasLikedper model after a single batched like query for the page - Invalidation: Centralized utilities in
utils/cache-invalidation.tswith on-demand invalidation viainvalidateModel() - Optimistic Updates: Heart button uses
useOptimisticfor immediate UI feedback with server state synchronization via form actions
- Tokens & utilities: Panda CSS semantic tokens and preset utilities (
panda.config.ts,@pandacss/preset-panda, typography preset); orange accent and shared patterns (e.g.,navLink) live in config - Typography: Albert Sans + Montserrat Alternates via
next/fontin root layout; heading font applied in PandaglobalCss - Layout & spacing: Panda
css()/ layout patterns (e.g.,gridfor model grids insrc/app/styles.ts) - Responsive: Mobile-first breakpoints via Panda conditions and component styles
features/models/components/model-card- Individual model display cardfeatures/models/components/model-card-skeleton- Loading skeleton for model cardsfeatures/models/components/model-detail- Detailed model view pagefeatures/models/components/models-grid- Grid layout for model cardsfeatures/models/components/models-not-found- Cached component for displaying no search results with helpful suggestionsfeatures/models/components/models-view- Shared server shell:Suspense+ async inner that awaitsgetModels; pagination usesPaginationOffsetTransitionfor directional View Transitionsfeatures/pagination/components/pagination- Reusable pagination with nuqs integration and View Transition supportfeatures/pagination/components/pagination-button- Page/limit control button used by paginationfeatures/models/components/heart-button/heart-button-client- Client component with form action, unified optimistic like/count state, View Transition types for count changesfeatures/models/components/heart-button/likes-count-transition- Wraps like count withViewTransitionupdate names for increase/decreasefeatures/models/components/heart-button/heart-button-server- Server component for detail pages (resolves like status server-side)features/models/components/heart-button/heart-button-skeleton- Loading skeleton for heart buttoncomponents/search-input/search-input- Model search with nuqs URL state;search-input-transitionfor view transitionsfeatures/categories/components/categories-nav- Category filtering sidebar (server component)features/categories/components/categories-block-transition- View transition wrapper for category listing blocksapp/3d-models/@categories/error.tsx- Error boundary for categories with built-in retry functionalityapp/3d-models/@results/error.tsx- Error boundary for search results with retry and error guidanceapp/3d-models/@results/loading.tsx- Loading state for search resultsapp/3d-models/categories/[categoryName]/error.tsx- Error boundary for category pages with retry and error guidanceapp/3d-models/categories/[categoryName]/loading.tsx- Loading state for category pagesapp/3d-models/[slug]/error.tsx- Error boundary for model detail pages with retry and error guidance
app/@navbar/default- Navbar parallel route with auth integrationapp/@navbar/error.tsx- Error boundary for navbar with retry functionalityapp/@footer/default- Footer parallel route with copyrightcomponents/nav-link-NavLink(link with active state) andNavLinkListItem(li+NavLink); matching (includesorendsWith), border position (bottomorleft) (client component)components/top-link- Top-of-page control used in layoutsfeatures/auth/components/auth-buttons- Authentication buttons with user avatar (GitHub image priority, icon fallback)features/auth/components/auth-buttons-skeleton- Navbar auth slot loading statefeatures/auth/components/auth-card- Card shell for sign-in/sign-up pagesfeatures/auth/components/auth-footer-link- Footer link between auth screensfeatures/auth/components/avatar- Avatar image with fallback
components/button- Shared button styled with Panda variantscomponents/form/input- Text input with consistent field stylingcomponents/form/label- Accessible labels for form fieldscomponents/form/submit-button- Submit control wired for pending statecomponents/form/reset-button- Reset control for formscomponents/form/field-errors- Field-level error display component with ViewTransition supportcomponents/form/form-error- Form-level error display component with ViewTransition supportcomponents/not-found/unsuccessful-state- Unified component for not-found and error states with conditional styling based onisErrorpropcomponents/not-found/unsuccessful-state-list-item- List item component for unsuccessful state suggestionscomponents/pill- Small label componentcomponents/scroll-progress- Top-of-page reading progress indicator (client)components/skeleton- Shared skeleton primitive for loading placeholderscomponents/suspend- Suspense helper componentcomponents/generic-component- Generic wrapper for collections
lib/auth- Better Auth configuration with email/password and GitHub OAuthlib/auth-client- Better Auth client instance for client-side usagefeatures/auth/actions- Sign-in, sign-up, and sign-out server actions with Valibot validationfeatures/auth/components/has-auth- Generic auth component with session provider and Suspense wrapperfeatures/auth/constants- Validation constants (password length, email length, name length limits)features/auth/queries/get-user- User query with cache directives (returns user from session)features/auth/components/sign-in-button- GitHub OAuth sign-in buttonutils/to-action-state- Action state utilities for consistent server action responsescomponents/form/field-errors- Reusable field error component used in auth formscomponents/form/form-error- Reusable form-level error component used in auth forms
- Biome / Ultracite: Linting and formatting (see
.cursor/rules/ultracite.mdc) - React Doctor: React/Next.js diagnostics on pull requests; run locally with
bun run react-doctor - tsgo: TypeScript type checking
- TypeScript: Static type checking
prepare(automatic onbun install) β Pandastyled-system/codegen and Husky setupbun run dev- Start development server (Turbopack)bun run dev:inspect- Start development server with Node.js inspectorbun run next:upgrade- Upgrade Next.js to latest versionbun run next:analyze- Analyze Next.js bundle (experimental-analyze)bun run build- Build for production (Turbopack)bun run build:debug- Build with debug prerender informationbun run start- Start production serverbun run test- Run tests (Bun test runner)bun run test:watch- Run tests in watch modebun run test:unit- Run unit testsbun run test:components- Run component testsbun run test:integration- Run integration testsbunfig.tomlβ test preload (tests/setup/test-preload.ts) registers Happy DOM globals and stubsserver-onlyfor component testsbun run test:e2e- Run Playwright E2E testsbun run e2e:open- Open Playwright UIbun run e2e:codegen- Playwright codegen (localhost:3000)bun run type- Run tsgo type checkingbun run typegen- Generate Next.js routes and run tsgo (noEmit)bun run env:typegen- Regeneratesrc/env.d.tsfrom.env.schema(Varlock)bun run db:generate- Generate Drizzle migrationsbun run db:migrate- Run Drizzle migrationsbun run db:push- Push schema directly to databasebun run db:studio- Open Drizzle Studiobun run db:seed- Seed database with initial databun run db:drop- Drop all tables (development reset)bun run fix- Fix linting issues with Ultracite/Biomebun run check- Check linting rules with Ultracite/Biomebun run doctor- Run Ultracite doctor diagnosticsbun run ultracite:upgrade- Upgrade Ultracite configurationbun run react-doctor- Run React Doctor locally (react-doctor.config.json)
The project follows a consistent coding style with:
- ES modules (import/export syntax)
- TypeScript for type safety
- Panda CSS for styling (
cssrecipes, semantic tokens) - Feature-based organization
- Component-specific type definitions
- Proper error handling and logging
- Connect your repository to Vercel
- Set environment variables in Vercel dashboard
- Deploy automatically on push to main branch
- If you use Bun on Vercel, set
bunVersion: "1.x"andbuildCommand: "bun --bun run next build"invercel.json. Ensure Bun runtime is >= 1.3.7 for Cache Components.
Mirror .env.schema: NEXT_PUBLIC_SITE_URL, BETTER_AUTH_SECRET, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, DATABASE_URL, and BITWARDEN_ACCESS_TOKEN when using bitwarden() resolvers. Varlock validates at runtime; types live in src/env.d.ts. See docs/VARLOCK.md for Vercel and Bitwarden.
- Update
src/db/seed-data/models.tswith new model data (note:userIdandlikesare omitted from seed data) - Run
bun run db:seedto update the database (requires existing users in the database)
- Update
src/db/seed-data/categories.tswith new category data - Run
bun run db:seedto update the database
- Use centralized cache invalidation utilities in
utils/cache-invalidation.ts - Functions:
invalidateAllModels(),invalidateModel(slug),invalidateCategory(slug) - Cache tags provide granular control over what gets invalidated
- Automatic cache invalidation on data mutations (e.g.,
toggleLikeinvalidates model cache) - Session cache uses
"use cache: private"directive withcacheTag("session")for responsive auth state - Like status uses
getHasLikedStatuswith"use cache: private"for user-specific cache
- Fork the repository
- Create a feature branch
- Make your changes following the feature-based architecture
- Run tests and linting
- Submit a pull request
This project is licensed under the MIT License.
For support and questions:
- Check the documentation
- Review existing issues
- Create a new issue with detailed information