|
5 | 5 | - `components/` reusable UI primitives (kebab-case files, PascalCase exports). |
6 | 6 | - `hooks/` shared stateful helpers (camelCase named exports). |
7 | 7 | - `lib/` domain utilities and caching (`lib/cache`); import via `@/...` aliases. |
| 8 | +- `lib/inngest/` Inngest client and functions for background jobs and scheduled section revalidation. |
8 | 9 | - `server/` backend integrations and tRPC routers; isolate DNS, RDAP/WHOIS, TLS, and header probing services. |
9 | | -- `server/db/` Drizzle ORM schema, migrations, and repository layer for Postgres persistence. |
10 | | -- `server/inngest/` Inngest functions for background jobs and scheduled section revalidation. |
| 10 | +- `server/routers/` tRPC router definitions (`_app.ts` and domain-specific routers). |
| 11 | +- `server/services/` service layer for domain data fetching (DNS, certificates, headers, hosting, registration, SEO, screenshot, favicon, etc.). |
| 12 | +- `lib/db/` Drizzle ORM schema, migrations, and repository layer for Postgres persistence. |
11 | 13 | - `public/` static assets; Tailwind v4 tokens live in `app/globals.css`. Update `instrumentation-client.ts` when adding analytics. |
| 14 | +- `trpc/` tRPC client setup, query client, and error handling. |
12 | 15 |
|
13 | 16 | ## Build, Test, and Development Commands |
14 | 17 | - `pnpm dev` — start dev server at http://localhost:3000. |
15 | 18 | - `pnpm build` — compile production bundle. |
16 | 19 | - `pnpm start` — serve compiled output for smoke tests. |
| 20 | +- `pnpm docker:up` — start all Dockerized local services (Postgres, Redis, SRH, Inngest) and wait until ready. |
| 21 | +- `pnpm docker:down` — stop all Dockerized local services. |
17 | 22 | - `pnpm lint` — run Biome lint + type-aware checks (`--write` to fix). |
18 | 23 | - `pnpm format` — apply Biome formatting. |
19 | 24 | - `pnpm typecheck` — run `tsc --noEmit` for stricter diagnostics. |
20 | | -- `pnpm db:generate` — generate Drizzle migrations. |
| 25 | +- `pnpm test` — run Vitest in watch mode. |
| 26 | +- `pnpm test:run` — run Vitest once. |
| 27 | +- `pnpm test:ui` — open Vitest UI. |
| 28 | +- `pnpm test:coverage` — run tests with coverage report. |
| 29 | +- `pnpm db:generate` — generate Drizzle migrations from schema. |
21 | 30 | - `pnpm db:push` — push the current schema to the database. |
22 | 31 | - `pnpm db:migrate` — apply migrations to the database. |
23 | 32 | - `pnpm db:studio` — open Drizzle Studio. |
24 | | -- `pnpm db:seed:providers` — seed known provider rules. |
| 33 | +- `pnpm db:seed` — run seed script (scripts/db/seed.ts). |
25 | 34 | - Requires Node.js >= 22 (see `package.json` engines). |
26 | 35 |
|
27 | 36 | ## Coding Style & Naming Conventions |
|
37 | 46 | ## Testing Guidelines |
38 | 47 | - Use **Vitest** with React Testing Library; config in `vitest.config.ts`. |
39 | 48 | - Global setup in `vitest.setup.ts`: |
40 | | - - Mocks analytics clients/servers and `server-only`. |
| 49 | + - Mocks analytics clients/servers (`@/lib/analytics/server` and `@/lib/analytics/client`). |
| 50 | + - Mocks `server-only` module. |
41 | 51 | - `unstable_cache` mocked as a no-op; caching behavior is not under test. |
42 | | -- Database in tests: Drizzle client is not globally mocked. Replace `@/server/db/client` with a PGlite-backed instance when needed. |
| 52 | +- Database in tests: Drizzle client is not globally mocked. Replace `@/server/db/client` with a PGlite-backed instance when needed (`@/lib/db/pglite`). |
43 | 53 | - Redis in tests: do NOT use globals. Mock per-suite with the in-memory adapter: |
44 | 54 | - In `beforeAll`: `const { makeInMemoryRedis } = await import("@/lib/redis-mock"); const impl = makeInMemoryRedis(); vi.doMock("@/lib/redis", () => impl);` |
45 | 55 | - In `beforeEach`/`afterEach`: `const { resetInMemoryRedis } = await import("@/lib/redis-mock"); resetInMemoryRedis();` |
46 | 56 | - Seed/assert via the mocked client: `const { redis } = await import("@/lib/redis"); await redis.set(key, value);` |
47 | 57 | - UI tests: |
48 | 58 | - Do not add direct tests for `components/ui/*` (shadcn). |
49 | 59 | - Mock Radix primitives (Accordion, Tooltip) when testing domain sections. |
50 | | - - Mock TRPC/React Query for components like `Favicon` and `Screenshot`. |
| 60 | + - Mock tRPC/React Query for components like `Favicon` and `Screenshot`. |
51 | 61 | - Server tests: |
52 | 62 | - Prefer `vi.hoisted` for ESM module mocks (e.g., `node:tls`). |
53 | 63 | - Use unique cache keys/domains; call `resetInMemoryRedis()` in `afterEach`. |
54 | 64 | - Screenshot service (`server/services/screenshot.ts`) uses hoisted mocks for `puppeteer`/`puppeteer-core` and `@sparticuz/chromium`. |
55 | 65 | - Vercel Blob storage: mock `@vercel/blob` (`put` and `del` functions). Set `BLOB_READ_WRITE_TOKEN` via `vi.stubEnv` in suites that touch uploads/deletes. |
56 | 66 | - Browser APIs: Mock `URL.createObjectURL`/`revokeObjectURL` with `vi.fn()` in tests that need them. |
57 | | -- Commands: `pnpm test`, `pnpm test:run`, `pnpm test:coverage`. |
| 67 | +- Commands: `pnpm test`, `pnpm test:run`, `pnpm test:ui`, `pnpm test:coverage`. |
58 | 68 |
|
59 | 69 | ## Commit & Pull Request Guidelines |
60 | 70 | - Commits: single-focus, imperative, sentence case (e.g., "Add RDAP caching layer"). |
|
66 | 76 | - Vercel Blob backs favicon/screenshot storage with automatic public URLs. |
67 | 77 | - Screenshots (Puppeteer): prefer `puppeteer-core` + `@sparticuz/chromium` on Vercel. |
68 | 78 | - Persist domain data in Postgres via Drizzle; use Redis for short-lived caching/locks. Apply retry backoff to respect provider limits. |
69 | | -- Background revalidation runs via Inngest functions (scheduled and event-driven). |
70 | | -- Review `server/trpc.ts` when extending procedures to ensure auth/context remain intact. |
| 79 | +- Background revalidation runs via Inngest functions (scheduled and event-driven) in `lib/inngest/functions/`. |
| 80 | +- Cron jobs trigger Inngest events via `app/api/cron/` endpoints secured with `CRON_SECRET`. |
| 81 | +- Review `trpc/init.ts` when extending procedures to ensure auth/context remain intact. |
71 | 82 |
|
72 | | -## Feature Flags |
73 | | -- Uses **Vercel Flags SDK** with **Statsig** adapter via Edge Config for server-evaluated feature flags. |
74 | | -- Flag definitions: `flags.ts` at project root exports flag declarations. |
75 | | -- Flags API endpoint: `app/.well-known/vercel/flags/route.ts` for Vercel Toolbar integration. |
76 | | -- Server-side evaluation: Use flags in server components or API routes via `await flagName()`. |
77 | | -- Required env: `FLAGS_SECRET` (for encryption/overrides), `EDGE_CONFIG` (Vercel Edge Config URL). |
78 | | -- Optional env: `STATSIG_CONSOLE_API_KEY` and `STATSIG_PROJECT_ID` for Flags Explorer metadata in Vercel Toolbar. |
79 | | -- Statsig config structure: Dynamic configs return JSON objects (e.g., `{ domains: [...] }`); use adapter's mapping function to extract values. |
80 | | -- Defensive validation: Always validate flag return values with `Array.isArray()` or similar checks before use to handle null/undefined/invalid types gracefully. |
| 83 | +## Analytics & Observability |
| 84 | +- Uses **PostHog** for analytics and error tracking with reverse proxy via `/_proxy/ingest/*`. |
| 85 | +- PostHog sourcemap uploads configured in `next.config.ts` with `@posthog/nextjs-config`. |
| 86 | +- OpenTelemetry integration via `@vercel/otel` in `instrumentation.ts`. |
| 87 | +- Client-side analytics captured via `posthog-js` and initialized in `instrumentation-client.ts`. |
| 88 | +- Server-side analytics captured via `posthog-node` in `lib/analytics/server.ts`. |
| 89 | +- Analytics mocked in tests via `vitest.setup.ts`. |
0 commit comments