You've already forked domainstack.io
mirror of
https://github.com/jakejarvis/domainstack.io.git
synced 2025-12-02 19:33:48 -05:00
9.0 KiB
9.0 KiB
Repository Guidelines
Project Structure & Module Organization
app/Next.js App Router. Default to server components; keepapp/page.tsxandapp/api/*thin and delegate toserver/orlib/.components/reusable UI primitives (kebab-case files, PascalCase exports).hooks/shared stateful helpers (camelCase named exports).lib/domain utilities and shared modules; import via@/...aliases.lib/constants/modular constants organized by domain (app, decay, domain-validation, external-apis, headers, ttl).lib/inngest/Inngest client and functions for event-driven background section revalidation.lib/db/Drizzle ORM schema, migrations, and repository layer for Postgres persistence.lib/db/repos/repository layer for each table (domains, certificates, dns, favicons, headers, hosting, providers, registrations, screenshots, seo).lib/logger/unified structured logging system with OpenTelemetry integration, correlation IDs, and PII-safe field filtering.server/backend integrations and tRPC routers; isolate DNS, RDAP/WHOIS, TLS, and header probing services.server/routers/tRPC router definitions (_app.tsand domain-specific routers).server/services/service layer for domain data fetching (DNS, certificates, headers, hosting, registration, SEO, screenshot, favicon, etc.).public/static assets; Tailwind v4 tokens live inapp/globals.css. Updateinstrumentation-client.tswhen adding analytics.trpc/tRPC client setup, query client, and error handling.
Build, Test, and Development Commands
pnpm dev— start all local services (Postgres, Inngest, etc.) and Next.js dev server at http://localhost:3000 usingconcurrently.pnpm build— compile production bundle.pnpm start— serve compiled output for smoke tests.pnpm lint— run Biome lint + type-aware checks (--writeto fix).pnpm format— apply Biome formatting.pnpm typecheck— runtsc --noEmitfor stricter diagnostics.pnpm test— run Vitest in watch mode.pnpm test:run— run Vitest once.pnpm test:ui— open Vitest UI.pnpm test:coverage— run tests with coverage report.pnpm db:generate— generate Drizzle migrations from schema.pnpm db:push— push the current schema to the database.pnpm db:migrate— apply migrations to the database.pnpm db:studio— open Drizzle Studio.pnpm db:seed— run seed script (scripts/db/seed.ts).- Requires Node.js >= 22 (see
package.jsonengines).
Coding Style & Naming Conventions
- TypeScript only,
strictenabled; prefer small, pure modules (≈≤300 LOC). - 2-space indentation. Files/folders: kebab-case; exports: PascalCase; helpers: camelCase named exports.
- Client components must begin with
"use client". Consolidate imports via@/.... Keep page roots lean. - Constants: Organize by domain in
lib/constants/submodules; re-export vialib/constants/index.ts. - Use
drizzle-zodfor DB boundary validation:- Read schemas:
lib/db/zod.ts*Select(strictDatetypes) - Write schemas:
lib/db/zod.ts*Insert/*Update(dates coerced) - Reuse domain Zod types for JSON columns (SEO, registration) to avoid drift
- Reference: drizzle-zod docs drizzle-zod
- Read schemas:
Testing Guidelines
- Use Vitest with React Testing Library; config in
vitest.config.ts. - Uses
threadspool for compatibility with sandboxed environments (e.g., Cursor agent commands). - Global setup in
vitest.setup.ts:- Mocks analytics clients/servers (
@/lib/analytics/serverand@/lib/analytics/client). - Mocks logger clients/servers (
@/lib/logger/serverand@/lib/logger/client). - Mocks
server-onlymodule.
- Mocks analytics clients/servers (
- Database in tests: Drizzle client is not globally mocked. Replace
@/lib/db/clientwith a PGlite-backed instance when needed (@/lib/db/pglite). - UI tests:
- Do not add direct tests for
components/ui/*(shadcn). - Mock Radix primitives (Accordion, Tooltip) when testing domain sections.
- Mock tRPC/React Query for components like
FaviconandScreenshot.
- Do not add direct tests for
- Server tests:
- Prefer
vi.hoistedfor ESM module mocks (e.g.,node:tls). - Screenshot service (
server/services/screenshot.ts) uses hoisted mocks forpuppeteer/puppeteer-coreand@sparticuz/chromium. - Vercel Blob storage: mock
@vercel/blob(putanddelfunctions). SetBLOB_READ_WRITE_TOKENviavi.stubEnvin suites that touch uploads/deletes. - Repository tests (
lib/db/repos/*.test.ts): Use PGlite for isolated in-memory database testing.
- Prefer
- Browser APIs: Mock
URL.createObjectURL/revokeObjectURLwithvi.fn()in tests that need them. - Commands:
pnpm test,pnpm test:run,pnpm test:ui,pnpm test:coverage.
Commit & Pull Request Guidelines
- Commits: single-focus, imperative, sentence case (e.g., "Add RDAP caching layer").
- PRs: describe user impact, link issues, flag breaking changes/deps, and attach screenshots or terminal logs when relevant.
- Call out skipped checks and confirm
.env.localrequirements for reviewers.
Security & Configuration Tips
- Keep secrets in
.env.local. See.env.examplefor required variables. - Vercel Edge Config provides dynamic, low-latency configuration without redeployment:
domain_suggestions(array): Homepage domain suggestions; fails gracefully to empty array
- Vercel Blob backs favicon/screenshot storage with automatic public URLs; metadata cached in Postgres.
- Screenshots (Puppeteer): prefer
puppeteer-core+@sparticuz/chromiumon Vercel. - Persist domain data in Postgres via Drizzle with per-table TTL columns (
expiresAt). - All caching uses Next.js Data Cache (
fetchwithnext: { revalidate }) or Postgres. - Database connections: Use Vercel's Postgres connection pooling (
@vercel/postgres) for optimal performance. - Background revalidation: Event-driven via Inngest functions in
lib/inngest/functions/with built-in concurrency control. - Use Next.js 16
after()for fire-and-forget background operations (analytics, domain access tracking) with graceful degradation. - Review
trpc/init.tswhen extending procedures to ensure auth/context remain intact.
Analytics & Observability
- Uses PostHog for analytics and error tracking with reverse proxy via
/_proxy/ingest/*. - PostHog sourcemap uploads configured in
next.config.tswith@posthog/nextjs-config. - OpenTelemetry integration via
@vercel/otelininstrumentation.tsfor distributed tracing. - Client-side analytics captured via
posthog-jsand initialized ininstrumentation-client.ts. - Server-side analytics captured via
posthog-nodeinlib/analytics/server.ts:- Uses
analytics.track()andanalytics.trackException()for unified tracking. - Leverages Next.js 16
after()for background event capture with graceful degradation. - Distinct ID sourced from PostHog cookie via
cache()-wrappedgetDistinctId()to comply with Next.js restrictions.
- Uses
- Analytics mocked in tests via
vitest.setup.ts.
Structured Logging
- Unified logging system in
lib/logger/with server (lib/logger/server.ts) and client (lib/logger/client.ts) implementations. - Server-side logging:
- Import singleton:
import { logger } from "@/lib/logger/server" - Or create service logger:
const logger = createLogger({ source: "dns" }) - Automatic OpenTelemetry trace/span ID injection from
@vercel/otel - Correlation ID tracking via AsyncLocalStorage for request tracing
- Critical errors automatically tracked in PostHog via
after() - Log levels:
trace,debug,info,warn,error,fatal
- Import singleton:
- Client-side logging:
- Import singleton:
import { logger } from "@/lib/logger/client" - Or use hook:
const logger = useLogger({ component: "MyComponent" }) - Errors automatically tracked in PostHog
- Console output only in development (info/debug) and always for errors
- Correlation IDs propagated from server via header/cookie/localStorage
- Import singleton:
- Log format: Structured JSON with consistent fields (level, message, timestamp, context, correlationId, traceId, spanId, environment).
- Usage examples:
// Server (service layer) import { createLogger } from "@/lib/logger/server"; const logger = createLogger({ source: "dns" }); logger.debug("start example.com", { domain: "example.com" }); logger.info("ok example.com", { domain: "example.com", count: 5 }); logger.error("failed to resolve", error, { domain: "example.com" }); // Client (components) import { useLogger } from "@/hooks/use-logger"; const logger = useLogger({ component: "DomainSearch" }); logger.info("search initiated", { domain: query }); logger.error("search failed", error, { domain: query }); - Correlation IDs: Generated server-side, propagated to client via
x-correlation-idheader, stored in cookie/localStorage. Enables request tracing across services. - Integration with tRPC: Middleware in
trpc/init.tsautomatically logs all procedures with correlation IDs and OpenTelemetry context. - Testing: Logger mocked in
vitest.setup.ts. Usevi.mocked(logger.info)to assert log calls in tests.